1# Licensed under the Apache License, Version 2.0 (the "License"); you may 2# not use this file except in compliance with the License. You may obtain 3# a copy of the License at 4# 5# http://www.apache.org/licenses/LICENSE-2.0 6# 7# Unless required by applicable law or agreed to in writing, software 8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10# License for the specific language governing permissions and limitations 11# under the License. 12 13import types 14 15from keystoneauth1 import exceptions as ks_exc 16from keystoneauth1.identity import v2 as v2_auth 17from keystoneauth1.identity import v3 as v3_auth 18from keystoneauth1 import session as ks_session 19import mock 20import requests 21 22from ceilometerclient.apiclient import exceptions 23from ceilometerclient import client 24from ceilometerclient import exc 25from ceilometerclient.tests.unit import utils 26from ceilometerclient.v2 import client as v2client 27 28FAKE_ENV = { 29 'username': 'username', 30 'password': 'password', 31 'tenant_name': 'tenant_name', 32 'auth_url': 'http://no.where', 33 'auth_plugin': mock.Mock(), 34 'ceilometer_url': 'http://no.where', 35 'token': '1234', 36 'user_domain_name': 'default', 37 'project_domain_name': 'default', 38} 39 40 41class ClientTest(utils.BaseTestCase): 42 @staticmethod 43 def create_client(env, api_version=2, endpoint=None, exclude=[]): 44 env = dict((k, v) for k, v in env.items() 45 if k not in exclude) 46 with mock.patch( 47 'ceilometerclient.v2.client.Client._get_redirect_client', 48 return_value=None): 49 return client.get_client(api_version, **env) 50 51 def test_client_v2_with_session(self): 52 resp = mock.Mock(status_code=200, text=b'') 53 resp.json.return_value = [] 54 session = mock.Mock() 55 session.request.return_value = resp 56 c = client.get_client(2, session=session) 57 c.resources.list() 58 self.assertTrue(session.request.called) 59 self.assertTrue(resp.json.called) 60 61 def test_client_version(self): 62 c2 = self.create_client(env=FAKE_ENV, api_version=2) 63 self.assertIsInstance(c2, v2client.Client) 64 65 def test_client_auth_lambda(self): 66 env = FAKE_ENV.copy() 67 env['token'] = lambda: env['token'] 68 self.assertIsInstance(env['token'], 69 types.FunctionType) 70 c2 = self.create_client(env) 71 self.assertIsInstance(c2, v2client.Client) 72 73 def test_client_auth_non_lambda(self): 74 env = FAKE_ENV.copy() 75 env['token'] = "1234" 76 self.assertIsInstance(env['token'], str) 77 c2 = self.create_client(env) 78 self.assertIsInstance(c2, v2client.Client) 79 80 def test_client_without_auth_plugin(self): 81 env = FAKE_ENV.copy() 82 del env['auth_plugin'] 83 c = self.create_client(env, api_version=2, endpoint='fake_endpoint') 84 self.assertIsInstance(c.auth_plugin, client.AuthPlugin) 85 86 def test_client_without_auth_plugin_keystone_v3(self): 87 env = FAKE_ENV.copy() 88 del env['auth_plugin'] 89 expected = { 90 'username': 'username', 91 'endpoint': 'http://no.where', 92 'tenant_name': 'tenant_name', 93 'service_type': None, 94 'token': '1234', 95 'endpoint_type': None, 96 'region_name': None, 97 'auth_url': 'http://no.where', 98 'tenant_id': None, 99 'insecure': None, 100 'cacert': None, 101 'password': 'password', 102 'user_domain_name': 'default', 103 'user_domain_id': None, 104 'project_domain_name': 'default', 105 'project_domain_id': None, 106 } 107 with mock.patch('ceilometerclient.client.AuthPlugin') as auth_plugin: 108 self.create_client(env, api_version=2, endpoint='http://no.where') 109 self.assertEqual(mock.call(**expected), 110 auth_plugin.mock_calls[0]) 111 112 def test_v2_client_timeout_invalid_value(self): 113 env = FAKE_ENV.copy() 114 env['timeout'] = 'abc' 115 self.assertRaises(ValueError, self.create_client, env) 116 env['timeout'] = '1.5' 117 self.assertRaises(ValueError, self.create_client, env) 118 119 def _test_v2_client_timeout_integer(self, timeout, expected_value): 120 env = FAKE_ENV.copy() 121 env['timeout'] = timeout 122 expected = { 123 'auth_plugin': mock.ANY, 124 'timeout': expected_value, 125 'original_ip': None, 126 'http': None, 127 'region_name': None, 128 'verify': True, 129 'timings': None, 130 'keyring_saver': None, 131 'cert': None, 132 'endpoint_type': None, 133 'user_agent': None, 134 'debug': None, 135 } 136 cls = 'ceilometerclient.apiclient.client.HTTPClient' 137 with mock.patch(cls) as mocked: 138 self.create_client(env) 139 mocked.assert_called_with(**expected) 140 141 def test_v2_client_timeout_zero(self): 142 self._test_v2_client_timeout_integer(0, None) 143 144 def test_v2_client_timeout_valid_value(self): 145 self._test_v2_client_timeout_integer(30, 30) 146 147 @mock.patch.object(ks_session, 'Session') 148 def test_v2_client_timeout_keystone_session(self, mocked_session): 149 mocked_session.side_effect = RuntimeError('Stop!') 150 env = FAKE_ENV.copy() 151 env['timeout'] = 5 152 del env['auth_plugin'] 153 del env['token'] 154 client = self.create_client(env) 155 self.assertRaises(RuntimeError, client.alarms.list) 156 args, kwargs = mocked_session.call_args 157 self.assertEqual(5, kwargs['timeout']) 158 159 def test_v2_client_cacert_in_verify(self): 160 env = FAKE_ENV.copy() 161 env['cacert'] = '/path/to/cacert' 162 client = self.create_client(env) 163 self.assertEqual('/path/to/cacert', 164 client.http_client.http_client.verify) 165 166 def test_v2_client_certfile_and_keyfile(self): 167 env = FAKE_ENV.copy() 168 env['cert_file'] = '/path/to/cert' 169 env['key_file'] = '/path/to/keycert' 170 client = self.create_client(env) 171 self.assertEqual(('/path/to/cert', '/path/to/keycert'), 172 client.http_client.http_client.cert) 173 174 def test_v2_client_insecure(self): 175 env = FAKE_ENV.copy() 176 env.pop('auth_plugin') 177 env['os_insecure'] = 'True' 178 client = self.create_client(env) 179 self.assertIn('insecure', client.auth_plugin.opts) 180 self.assertEqual('True', client.auth_plugin.opts['insecure']) 181 182 183class ClientTest2(ClientTest): 184 @staticmethod 185 def create_client(env, api_version=2, endpoint=None, exclude=[]): 186 env = dict((k, v) for k, v in env.items() 187 if k not in exclude) 188 with mock.patch( 189 'ceilometerclient.v2.client.Client._get_redirect_client', 190 return_value=None): 191 return client.Client(api_version, endpoint, **env) 192 193 194class ClientTestWithAodh(ClientTest): 195 @staticmethod 196 def create_client(env, api_version=2, endpoint=None, exclude=[]): 197 env = dict((k, v) for k, v in env.items() 198 if k not in exclude) 199 with mock.patch('ceilometerclient.apiclient.client.' 200 'HTTPClient.client_request', 201 return_value=mock.MagicMock()): 202 return client.get_client(api_version, **env) 203 204 def test_client_without_auth_plugin(self): 205 env = FAKE_ENV.copy() 206 del env['auth_plugin'] 207 c = self.create_client(env, api_version=2, endpoint='fake_endpoint') 208 self.assertIsInstance(c.alarm_client.http_client.auth_plugin, 209 client.AuthPlugin) 210 211 def test_v2_client_insecure(self): 212 env = FAKE_ENV.copy() 213 env.pop('auth_plugin') 214 env['insecure'] = 'True' 215 client = self.create_client(env) 216 self.assertIn('insecure', 217 client.alarm_client.http_client.auth_plugin.opts) 218 self.assertEqual('True', (client.alarm_client.http_client. 219 auth_plugin.opts['insecure'])) 220 221 def test_ceilometerclient_available_without_aodh_services_running(self): 222 env = FAKE_ENV.copy() 223 env.pop('auth_plugin', None) 224 with mock.patch('ceilometerclient.apiclient.client.' 225 'HTTPClient.client_request') as mocked_request: 226 mocked_request.side_effect = requests.exceptions.ConnectionError 227 ceiloclient = client.get_client(2, **env) 228 self.assertIsInstance(ceiloclient, v2client.Client) 229 230 @mock.patch('ceilometerclient.client.SessionClient') 231 def test_http_client_with_session_and_aodh(self, mock_sc): 232 session = mock.Mock() 233 kwargs = {"session": session, 234 "service_type": "metering", 235 "user_agent": "python-ceilometerclient"} 236 expected = { 237 "auth": None, 238 "interface": 'publicURL', 239 "region_name": None, 240 "timings": None, 241 "session": session, 242 "service_type": "metering", 243 "user_agent": "python-ceilometerclient"} 244 kwargs['aodh_endpoint'] = 'http://aodh.where' 245 client._construct_http_client(**kwargs) 246 mock_sc.assert_called_with(**expected) 247 248 249class ClientAuthTest(utils.BaseTestCase): 250 251 @staticmethod 252 def create_client(env, api_version=2, endpoint=None, exclude=[]): 253 env = dict((k, v) for k, v in env.items() 254 if k not in exclude) 255 with mock.patch('ceilometerclient.apiclient.client.' 256 'HTTPClient.client_request', 257 return_value=mock.MagicMock()): 258 return client.get_client(api_version, **env) 259 260 @mock.patch('keystoneauth1.discover.Discover') 261 @mock.patch('keystoneauth1.session.Session') 262 def test_discover_auth_versions(self, session, discover_mock): 263 env = FAKE_ENV.copy() 264 env.pop('auth_plugin', None) 265 266 mock_session_instance = mock.MagicMock() 267 session.return_value = mock_session_instance 268 269 client = self.create_client(env) 270 client.auth_plugin.opts.pop('token', None) 271 client.auth_plugin._do_authenticate(mock.MagicMock()) 272 273 self.assertEqual([mock.call(url='http://no.where', 274 session=mock_session_instance)], 275 discover_mock.call_args_list) 276 self.assertIsInstance(mock_session_instance.auth, v3_auth.Password) 277 278 @mock.patch('keystoneauth1.discover.Discover') 279 @mock.patch('keystoneauth1.session.Session') 280 def test_discover_auth_versions_v2_only(self, session, discover): 281 env = FAKE_ENV.copy() 282 env.pop('auth_plugin', None) 283 env.pop('user_domain_name', None) 284 env.pop('user_domain_id', None) 285 env.pop('project_domain_name', None) 286 env.pop('project_domain_id', None) 287 288 session_instance_mock = mock.MagicMock() 289 session.return_value = session_instance_mock 290 291 discover_instance_mock = mock.MagicMock() 292 discover_instance_mock.url_for.side_effect = (lambda v: v 293 if v == '2.0' else None) 294 discover.return_value = discover_instance_mock 295 296 client = self.create_client(env) 297 client.auth_plugin.opts.pop('token', None) 298 client.auth_plugin._do_authenticate(mock.MagicMock()) 299 self.assertEqual([mock.call(url='http://no.where', 300 session=session_instance_mock)], 301 discover.call_args_list) 302 303 self.assertIsInstance(session_instance_mock.auth, v2_auth.Password) 304 305 @mock.patch('keystoneauth1.discover.Discover') 306 @mock.patch('keystoneauth1.session.Session') 307 def test_discover_auth_versions_raise_discovery_failure(self, 308 session, 309 discover): 310 env = FAKE_ENV.copy() 311 env.pop('auth_plugin', None) 312 env.pop('token', None) 313 314 session_instance_mock = mock.MagicMock() 315 session.return_value = session_instance_mock 316 317 discover_instance_mock = mock.MagicMock() 318 discover_instance_mock.url_for.side_effect = (lambda v: v 319 if v == '2.0' else None) 320 discover.side_effect = ks_exc.DiscoveryFailure 321 client = self.create_client(env) 322 self.assertRaises(ks_exc.DiscoveryFailure, 323 client.auth_plugin._do_authenticate, 324 mock.Mock()) 325 discover.side_effect = mock.MagicMock() 326 client = self.create_client(env) 327 discover.side_effect = ks_exc.DiscoveryFailure 328 client.auth_plugin.opts.pop('token', None) 329 330 self.assertRaises(ks_exc.DiscoveryFailure, 331 client.auth_plugin._do_authenticate, 332 mock.Mock()) 333 self.assertEqual([mock.call(url='http://no.where', 334 session=session_instance_mock), 335 mock.call(url='http://no.where', 336 session=session_instance_mock)], 337 discover.call_args_list) 338 339 @mock.patch('ceilometerclient.client._get_keystone_session') 340 def test_get_endpoint(self, session): 341 env = FAKE_ENV.copy() 342 env.pop('auth_plugin', None) 343 env.pop('endpoint', None) 344 345 session_instance_mock = mock.MagicMock() 346 session.return_value = session_instance_mock 347 348 client = self.create_client(env) 349 client.auth_plugin.opts.pop('endpoint') 350 client.auth_plugin.opts.pop('token', None) 351 alarm_auth_plugin = client.alarm_client.http_client.auth_plugin 352 alarm_auth_plugin.opts.pop('endpoint') 353 alarm_auth_plugin.opts.pop('token', None) 354 355 self.assertNotEqual(client.auth_plugin, alarm_auth_plugin) 356 357 client.auth_plugin._do_authenticate(mock.MagicMock()) 358 alarm_auth_plugin._do_authenticate(mock.MagicMock()) 359 360 self.assertEqual([ 361 mock.call(interface='publicURL', region_name=None, 362 service_type='metering'), 363 mock.call(interface='publicURL', region_name=None, 364 service_type='alarming'), 365 ], session_instance_mock.get_endpoint.mock_calls) 366 367 def test_http_client_with_session(self): 368 session = mock.Mock() 369 session.request.return_value = mock.Mock(status_code=404, 370 text=b'') 371 env = {"session": session, 372 "service_type": "metering", 373 "user_agent": "python-ceilometerclient"} 374 c = client.SessionClient(**env) 375 self.assertRaises(exc.HTTPException, c.get, "/") 376 377 def test_get_aodh_endpoint_without_auth_url(self): 378 env = FAKE_ENV.copy() 379 env.pop('auth_plugin', None) 380 env.pop('endpoint', None) 381 env.pop('auth_url', None) 382 client = self.create_client(env, endpoint='fake_endpoint') 383 self.assertEqual(client.alarm_client.http_client.auth_plugin.opts, 384 client.auth_plugin.opts) 385 386 @mock.patch('ceilometerclient.client._get_keystone_session') 387 def test_get_different_endpoint_type(self, session): 388 env = FAKE_ENV.copy() 389 env.pop('auth_plugin', None) 390 env.pop('endpoint', None) 391 env['endpoint_type'] = 'internal' 392 393 session_instance_mock = mock.MagicMock() 394 session.return_value = session_instance_mock 395 396 client = self.create_client(env) 397 client.auth_plugin.opts.pop('endpoint') 398 client.auth_plugin.opts.pop('token', None) 399 alarm_auth_plugin = client.alarm_client.http_client.auth_plugin 400 alarm_auth_plugin.opts.pop('endpoint') 401 alarm_auth_plugin.opts.pop('token', None) 402 403 self.assertNotEqual(client.auth_plugin, alarm_auth_plugin) 404 405 client.auth_plugin._do_authenticate(mock.MagicMock()) 406 alarm_auth_plugin._do_authenticate(mock.MagicMock()) 407 408 self.assertEqual([ 409 mock.call(interface='internal', region_name=None, 410 service_type='metering'), 411 mock.call(interface='internal', region_name=None, 412 service_type='alarming'), 413 ], session_instance_mock.get_endpoint.mock_calls) 414 415 @mock.patch('ceilometerclient.client._get_keystone_session') 416 def test_get_sufficient_options_missing(self, session): 417 env = FAKE_ENV.copy() 418 env.pop('auth_plugin', None) 419 env.pop('password', None) 420 env.pop('endpoint', None) 421 env.pop('auth_token', None) 422 env.pop('tenant_name', None) 423 env.pop('username', None) 424 425 session_instance_mock = mock.MagicMock() 426 session.return_value = session_instance_mock 427 client = self.create_client(env) 428 client.auth_plugin.opts.pop('token', None) 429 self.assertRaises(exceptions.AuthPluginOptionsMissing, 430 client.auth_plugin.sufficient_options) 431