1# Copyright (c) 2016 VMware, Inc. 2# All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); you may 5# not use this file except in compliance with the License. You may obtain 6# a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13# License for the specific language governing permissions and limitations 14# under the License. 15 16import json 17from unittest import mock 18 19import ddt 20 21from osprofiler.drivers import loginsight 22from osprofiler import exc 23from osprofiler.tests import test 24 25 26@ddt.ddt 27class LogInsightDriverTestCase(test.TestCase): 28 29 BASE_ID = "8d28af1e-acc0-498c-9890-6908e33eff5f" 30 31 def setUp(self): 32 super(LogInsightDriverTestCase, self).setUp() 33 self._client = mock.Mock(spec=loginsight.LogInsightClient) 34 self._project = "cinder" 35 self._service = "osapi_volume" 36 self._host = "ubuntu" 37 with mock.patch.object(loginsight, "LogInsightClient", 38 return_value=self._client): 39 self._driver = loginsight.LogInsightDriver( 40 "loginsight://username:password@host", 41 project=self._project, 42 service=self._service, 43 host=self._host) 44 45 @mock.patch.object(loginsight, "LogInsightClient") 46 def test_init(self, client_class): 47 client = mock.Mock() 48 client_class.return_value = client 49 50 loginsight.LogInsightDriver("loginsight://username:password@host") 51 client_class.assert_called_once_with("host", "username", "password") 52 client.login.assert_called_once_with() 53 54 @ddt.data("loginsight://username@host", 55 "loginsight://username:p@ssword@host", 56 "loginsight://us:rname:password@host") 57 def test_init_with_invalid_connection_string(self, conn_str): 58 self.assertRaises(ValueError, loginsight.LogInsightDriver, conn_str) 59 60 @mock.patch.object(loginsight, "LogInsightClient") 61 def test_init_with_special_chars_in_conn_str(self, client_class): 62 client = mock.Mock() 63 client_class.return_value = client 64 65 loginsight.LogInsightDriver("loginsight://username:p%40ssword@host") 66 client_class.assert_called_once_with("host", "username", "p@ssword") 67 client.login.assert_called_once_with() 68 69 def test_get_name(self): 70 self.assertEqual("loginsight", self._driver.get_name()) 71 72 def _create_trace(self, 73 name, 74 timestamp, 75 parent_id="8d28af1e-acc0-498c-9890-6908e33eff5f", 76 base_id=BASE_ID, 77 trace_id="e465db5c-9672-45a1-b90b-da918f30aef6"): 78 return {"parent_id": parent_id, 79 "name": name, 80 "base_id": base_id, 81 "trace_id": trace_id, 82 "timestamp": timestamp, 83 "info": {"host": self._host}} 84 85 def _create_start_trace(self): 86 return self._create_trace("wsgi-start", "2016-10-04t11:50:21.902303") 87 88 def _create_stop_trace(self): 89 return self._create_trace("wsgi-stop", "2016-10-04t11:50:30.123456") 90 91 @mock.patch("json.dumps") 92 def test_notify(self, dumps): 93 json_str = mock.sentinel.json_str 94 dumps.return_value = json_str 95 96 trace = self._create_stop_trace() 97 self._driver.notify(trace) 98 99 trace["project"] = self._project 100 trace["service"] = self._service 101 exp_event = {"text": "OSProfiler trace", 102 "fields": [{"name": "base_id", 103 "content": trace["base_id"]}, 104 {"name": "trace_id", 105 "content": trace["trace_id"]}, 106 {"name": "project", 107 "content": trace["project"]}, 108 {"name": "service", 109 "content": trace["service"]}, 110 {"name": "name", 111 "content": trace["name"]}, 112 {"name": "trace", 113 "content": json_str}] 114 } 115 self._client.send_event.assert_called_once_with(exp_event) 116 117 @mock.patch.object(loginsight.LogInsightDriver, "_append_results") 118 @mock.patch.object(loginsight.LogInsightDriver, "_parse_results") 119 def test_get_report(self, parse_results, append_results): 120 start_trace = self._create_start_trace() 121 start_trace["project"] = self._project 122 start_trace["service"] = self._service 123 124 stop_trace = self._create_stop_trace() 125 stop_trace["project"] = self._project 126 stop_trace["service"] = self._service 127 128 resp = {"events": [{"text": "OSProfiler trace", 129 "fields": [{"name": "trace", 130 "content": json.dumps(start_trace) 131 } 132 ] 133 }, 134 {"text": "OSProfiler trace", 135 "fields": [{"name": "trace", 136 "content": json.dumps(stop_trace) 137 } 138 ] 139 } 140 ] 141 } 142 self._client.query_events = mock.Mock(return_value=resp) 143 144 self._driver.get_report(self.BASE_ID) 145 self._client.query_events.assert_called_once_with({"base_id": 146 self.BASE_ID}) 147 append_results.assert_has_calls( 148 [mock.call(start_trace["trace_id"], start_trace["parent_id"], 149 start_trace["name"], start_trace["project"], 150 start_trace["service"], start_trace["info"]["host"], 151 start_trace["timestamp"], start_trace), 152 mock.call(stop_trace["trace_id"], stop_trace["parent_id"], 153 stop_trace["name"], stop_trace["project"], 154 stop_trace["service"], stop_trace["info"]["host"], 155 stop_trace["timestamp"], stop_trace) 156 ]) 157 parse_results.assert_called_once_with() 158 159 160class LogInsightClientTestCase(test.TestCase): 161 162 def setUp(self): 163 super(LogInsightClientTestCase, self).setUp() 164 self._host = "localhost" 165 self._username = "username" 166 self._password = "password" 167 self._client = loginsight.LogInsightClient( 168 self._host, self._username, self._password) 169 self._client._session_id = "4ff800d1-3175-4b49-9209-39714ea56416" 170 171 def test_check_response_login_timeout(self): 172 resp = mock.Mock(status_code=440) 173 self.assertRaises( 174 exc.LogInsightLoginTimeout, self._client._check_response, resp) 175 176 def test_check_response_api_error(self): 177 resp = mock.Mock(status_code=401, ok=False) 178 resp.text = json.dumps( 179 {"errorMessage": "Invalid username or password.", 180 "errorCode": "FIELD_ERROR"}) 181 e = self.assertRaises( 182 exc.LogInsightAPIError, self._client._check_response, resp) 183 self.assertEqual("Invalid username or password.", str(e)) 184 185 @mock.patch("requests.Request") 186 @mock.patch("json.dumps") 187 @mock.patch.object(loginsight.LogInsightClient, "_check_response") 188 def test_send_request(self, check_resp, json_dumps, request_class): 189 req = mock.Mock() 190 request_class.return_value = req 191 prep_req = mock.sentinel.prep_req 192 req.prepare = mock.Mock(return_value=prep_req) 193 194 data = mock.sentinel.data 195 json_dumps.return_value = data 196 197 self._client._session = mock.Mock() 198 resp = mock.Mock() 199 self._client._session.send = mock.Mock(return_value=resp) 200 resp_json = mock.sentinel.resp_json 201 resp.json = mock.Mock(return_value=resp_json) 202 203 header = {"X-LI-Session-Id": "foo"} 204 body = mock.sentinel.body 205 params = mock.sentinel.params 206 ret = self._client._send_request( 207 "get", "https", "api/v1/events", header, body, params) 208 209 self.assertEqual(resp_json, ret) 210 exp_headers = {"X-LI-Session-Id": "foo", 211 "content-type": "application/json"} 212 request_class.assert_called_once_with( 213 "get", "https://localhost:9543/api/v1/events", headers=exp_headers, 214 data=data, params=mock.sentinel.params) 215 self._client._session.send.assert_called_once_with(prep_req, 216 verify=False) 217 check_resp.assert_called_once_with(resp) 218 219 @mock.patch.object(loginsight.LogInsightClient, "_send_request") 220 def test_is_current_session_active_with_active_session(self, send_request): 221 self.assertTrue(self._client._is_current_session_active()) 222 exp_header = {"X-LI-Session-Id": self._client._session_id} 223 send_request.assert_called_once_with( 224 "get", "https", "api/v1/sessions/current", headers=exp_header) 225 226 @mock.patch.object(loginsight.LogInsightClient, "_send_request") 227 def test_is_current_session_active_with_expired_session(self, 228 send_request): 229 send_request.side_effect = exc.LogInsightLoginTimeout 230 231 self.assertFalse(self._client._is_current_session_active()) 232 send_request.assert_called_once_with( 233 "get", "https", "api/v1/sessions/current", 234 headers={"X-LI-Session-Id": self._client._session_id}) 235 236 @mock.patch.object(loginsight.LogInsightClient, 237 "_is_current_session_active", return_value=True) 238 @mock.patch.object(loginsight.LogInsightClient, "_send_request") 239 def test_login_with_current_session_active(self, send_request, 240 is_current_session_active): 241 self._client.login() 242 is_current_session_active.assert_called_once_with() 243 send_request.assert_not_called() 244 245 @mock.patch.object(loginsight.LogInsightClient, 246 "_is_current_session_active", return_value=False) 247 @mock.patch.object(loginsight.LogInsightClient, "_send_request") 248 def test_login(self, send_request, is_current_session_active): 249 new_session_id = "569a80aa-be5c-49e5-82c1-bb62392d2667" 250 resp = {"sessionId": new_session_id} 251 send_request.return_value = resp 252 253 self._client.login() 254 is_current_session_active.assert_called_once_with() 255 exp_body = {"username": self._username, "password": self._password} 256 send_request.assert_called_once_with( 257 "post", "https", "api/v1/sessions", body=exp_body) 258 self.assertEqual(new_session_id, self._client._session_id) 259 260 @mock.patch.object(loginsight.LogInsightClient, "_send_request") 261 def test_send_event(self, send_request): 262 event = mock.sentinel.event 263 self._client.send_event(event) 264 265 exp_body = {"events": [event]} 266 exp_path = ("api/v1/events/ingest/%s" % 267 self._client.LI_OSPROFILER_AGENT_ID) 268 send_request.assert_called_once_with( 269 "post", "http", exp_path, body=exp_body) 270 271 @mock.patch.object(loginsight.LogInsightClient, "_send_request") 272 def test_query_events(self, send_request): 273 resp = mock.sentinel.response 274 send_request.return_value = resp 275 276 self.assertEqual(resp, self._client.query_events({"foo": "bar"})) 277 exp_header = {"X-LI-Session-Id": self._client._session_id} 278 exp_params = {"limit": 20000, "timeout": self._client._query_timeout} 279 send_request.assert_called_once_with( 280 "get", "https", "api/v1/events/foo/CONTAINS+bar/timestamp/GT+0", 281 headers=exp_header, params=exp_params) 282 283 @mock.patch.object(loginsight.LogInsightClient, "_send_request") 284 @mock.patch.object(loginsight.LogInsightClient, "login") 285 def test_query_events_with_session_expiry(self, login, send_request): 286 resp = mock.sentinel.response 287 send_request.side_effect = [exc.LogInsightLoginTimeout, resp] 288 289 self.assertEqual(resp, self._client.query_events({"foo": "bar"})) 290 login.assert_called_once_with() 291 exp_header = {"X-LI-Session-Id": self._client._session_id} 292 exp_params = {"limit": 20000, "timeout": self._client._query_timeout} 293 exp_send_request_call = mock.call( 294 "get", "https", "api/v1/events/foo/CONTAINS+bar/timestamp/GT+0", 295 headers=exp_header, params=exp_params) 296 send_request.assert_has_calls([exp_send_request_call] * 2) 297