1# Licensed to the Apache Software Foundation (ASF) under one or more 2# contributor license agreements. See the NOTICE file distributed with 3# this work for additional information regarding copyright ownership. 4# The ASF licenses this file to You under the Apache License, Version 2.0 5# (the "License"); you may not use this file except in compliance with 6# the License. You may obtain 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, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16from __future__ import with_statement 17 18import os 19import sys 20import unittest 21import datetime 22import mock 23import pytest 24 25from libcloud.utils.iso8601 import UTC 26 27try: 28 import simplejson as json 29except ImportError: 30 import json 31 32from mock import Mock, patch 33import requests_mock 34 35from libcloud.utils.py3 import httplib 36from libcloud.utils.py3 import method_type 37from libcloud.utils.py3 import u 38 39from libcloud.common.base import LibcloudConnection 40from libcloud.common.exceptions import BaseHTTPError 41from libcloud.common.openstack_identity import OpenStackAuthenticationCache 42from libcloud.common.types import InvalidCredsError, MalformedResponseError, \ 43 LibcloudError 44from libcloud.compute.types import Provider, KeyPairDoesNotExistError, StorageVolumeState, \ 45 VolumeSnapshotState, NodeImageMemberState 46from libcloud.compute.providers import get_driver 47from libcloud.compute.drivers.openstack import ( 48 OpenStack_1_0_NodeDriver, 49 OpenStack_1_1_NodeDriver, OpenStackSecurityGroup, 50 OpenStackSecurityGroupRule, OpenStack_1_1_FloatingIpPool, 51 OpenStack_1_1_FloatingIpAddress, OpenStackKeyPair, 52 OpenStack_1_0_Connection, OpenStack_2_FloatingIpPool, 53 OpenStackNodeDriver, 54 OpenStack_2_NodeDriver, OpenStack_2_PortInterfaceState, OpenStackNetwork, 55 OpenStackException) 56from libcloud.compute.base import Node, NodeImage, NodeSize 57from libcloud.pricing import set_pricing, clear_pricing_data 58 59from libcloud.test import MockHttp, XML_HEADERS 60from libcloud.test.file_fixtures import ComputeFileFixtures, OpenStackFixtures 61from libcloud.test.compute import TestCaseMixin 62 63from libcloud.test.secrets import OPENSTACK_PARAMS 64 65BASE_DIR = os.path.abspath(os.path.split(__file__)[0]) 66 67 68def test_driver_instantiation_invalid_auth(): 69 with pytest.raises(LibcloudError): 70 d = OpenStackNodeDriver( 71 'user', 'correct_password', 72 ex_force_auth_version='5.0', 73 ex_force_auth_url='http://x.y.z.y:5000', 74 ex_tenant_name='admin') 75 d.list_nodes() 76 77 78class OpenStackAuthTests(unittest.TestCase): 79 def setUp(self): 80 OpenStack_1_0_NodeDriver.connectionCls = OpenStack_1_0_Connection 81 OpenStack_1_0_NodeDriver.connectionCls.conn_class = LibcloudConnection 82 83 def test_auth_host_passed(self): 84 forced_auth = 'http://x.y.z.y:5000' 85 d = OpenStack_1_0_NodeDriver( 86 'user', 'correct_password', 87 ex_force_auth_version='2.0_password', 88 ex_force_auth_url='http://x.y.z.y:5000', 89 ex_tenant_name='admin') 90 self.assertEqual(d._ex_force_auth_url, forced_auth) 91 92 with requests_mock.Mocker() as mock: 93 body2 = ComputeFileFixtures('openstack').load('_v2_0__auth.json') 94 95 mock.register_uri('POST', 'http://x.y.z.y:5000/v2.0/tokens', text=body2, 96 headers={'content-type': 'application/json; charset=UTF-8'}) 97 d.connection._populate_hosts_and_request_paths() 98 self.assertEqual(d.connection.host, 'test_endpoint.com') 99 100 101class OpenStack_1_0_Tests(TestCaseMixin, unittest.TestCase): 102 should_list_locations = False 103 should_list_volumes = False 104 105 driver_klass = OpenStack_1_0_NodeDriver 106 driver_args = OPENSTACK_PARAMS 107 driver_kwargs = {} 108 # driver_kwargs = {'ex_force_auth_version': '1.0'} 109 110 @classmethod 111 def create_driver(self): 112 if self is not OpenStack_1_0_FactoryMethodTests: 113 self.driver_type = self.driver_klass 114 return self.driver_type(*self.driver_args, **self.driver_kwargs) 115 116 def setUp(self): 117 # monkeypatch get_endpoint because the base openstack driver doesn't actually 118 # work with old devstack but this class/tests are still used by the rackspace 119 # driver 120 def get_endpoint(*args, **kwargs): 121 return "https://servers.api.rackspacecloud.com/v1.0/slug" 122 self.driver_klass.connectionCls.get_endpoint = get_endpoint 123 124 self.driver_klass.connectionCls.conn_class = OpenStackMockHttp 125 self.driver_klass.connectionCls.auth_url = "https://auth.api.example.com" 126 127 OpenStackMockHttp.type = None 128 129 self.driver = self.create_driver() 130 # normally authentication happens lazily, but we force it here 131 self.driver.connection._populate_hosts_and_request_paths() 132 clear_pricing_data() 133 134 @patch('libcloud.common.openstack.OpenStackServiceCatalog') 135 def test_populate_hosts_and_requests_path(self, _): 136 tomorrow = datetime.datetime.today() + datetime.timedelta(1) 137 cls = self.driver_klass.connectionCls 138 139 count = 5 140 141 # Test authentication and token re-use 142 con = cls('username', 'key') 143 osa = con.get_auth_class() 144 145 mocked_auth_method = Mock() 146 osa.authenticate = mocked_auth_method 147 148 # Valid token returned on first call, should be reused. 149 for i in range(0, count): 150 con._populate_hosts_and_request_paths() 151 152 if i == 0: 153 osa.auth_token = '1234' 154 osa.auth_token_expires = tomorrow 155 156 self.assertEqual(mocked_auth_method.call_count, 1) 157 158 osa.auth_token = None 159 osa.auth_token_expires = None 160 161 # ex_force_auth_token provided, authenticate should never be called 162 con = cls('username', 'key', ex_force_base_url='http://ponies', 163 ex_force_auth_token='1234') 164 osa = con.get_auth_class() 165 166 mocked_auth_method = Mock() 167 osa.authenticate = mocked_auth_method 168 169 for i in range(0, count): 170 con._populate_hosts_and_request_paths() 171 172 self.assertEqual(mocked_auth_method.call_count, 0) 173 174 def test_auth_token_is_set(self): 175 self.driver.connection._populate_hosts_and_request_paths() 176 self.assertEqual( 177 self.driver.connection.auth_token, "aaaaaaaaaaaa-bbb-cccccccccccccc") 178 179 def test_auth_token_expires_is_set(self): 180 self.driver.connection._populate_hosts_and_request_paths() 181 182 expires = self.driver.connection.auth_token_expires 183 self.assertEqual(expires.isoformat(), "2999-11-23T21:00:14-06:00") 184 185 def test_auth(self): 186 if self.driver.connection._auth_version == '2.0': 187 return 188 189 OpenStackMockHttp.type = 'UNAUTHORIZED' 190 try: 191 self.driver = self.create_driver() 192 self.driver.list_nodes() 193 except InvalidCredsError as e: 194 self.assertEqual(True, isinstance(e, InvalidCredsError)) 195 else: 196 self.fail('test should have thrown') 197 198 def test_auth_missing_key(self): 199 if self.driver.connection._auth_version == '2.0': 200 return 201 202 OpenStackMockHttp.type = 'UNAUTHORIZED_MISSING_KEY' 203 try: 204 self.driver = self.create_driver() 205 self.driver.list_nodes() 206 except MalformedResponseError as e: 207 self.assertEqual(True, isinstance(e, MalformedResponseError)) 208 else: 209 self.fail('test should have thrown') 210 211 def test_auth_server_error(self): 212 if self.driver.connection._auth_version == '2.0': 213 return 214 215 OpenStackMockHttp.type = 'INTERNAL_SERVER_ERROR' 216 try: 217 self.driver = self.create_driver() 218 self.driver.list_nodes() 219 except MalformedResponseError as e: 220 self.assertEqual(True, isinstance(e, MalformedResponseError)) 221 else: 222 self.fail('test should have thrown') 223 224 def test_ex_auth_cache_passed_to_identity_connection(self): 225 kwargs = self.driver_kwargs.copy() 226 kwargs['ex_auth_cache'] = OpenStackMockAuthCache() 227 driver = self.driver_type(*self.driver_args, **kwargs) 228 driver.list_nodes() 229 self.assertEqual(kwargs['ex_auth_cache'], 230 driver.connection.get_auth_class().auth_cache) 231 232 def test_unauthorized_clears_cached_auth_context(self): 233 auth_cache = OpenStackMockAuthCache() 234 self.assertEqual(len(auth_cache), 0) 235 236 kwargs = self.driver_kwargs.copy() 237 kwargs['ex_auth_cache'] = auth_cache 238 driver = self.driver_type(*self.driver_args, **kwargs) 239 driver.list_nodes() 240 241 # Token was cached 242 self.assertEqual(len(auth_cache), 1) 243 244 # Simulate token being revoked 245 self.driver_klass.connectionCls.conn_class.type = 'UNAUTHORIZED' 246 with pytest.raises(BaseHTTPError) as ex: 247 driver.list_nodes() 248 249 # Token was evicted 250 self.assertEqual(len(auth_cache), 0) 251 252 def test_error_parsing_when_body_is_missing_message(self): 253 OpenStackMockHttp.type = 'NO_MESSAGE_IN_ERROR_BODY' 254 try: 255 self.driver.list_images() 256 except Exception as e: 257 self.assertEqual(True, isinstance(e, Exception)) 258 else: 259 self.fail('test should have thrown') 260 261 def test_list_locations(self): 262 locations = self.driver.list_locations() 263 self.assertEqual(len(locations), 1) 264 265 def test_list_nodes(self): 266 OpenStackMockHttp.type = 'EMPTY' 267 ret = self.driver.list_nodes() 268 self.assertEqual(len(ret), 0) 269 OpenStackMockHttp.type = None 270 ret = self.driver.list_nodes() 271 self.assertEqual(len(ret), 1) 272 node = ret[0] 273 self.assertEqual('67.23.21.33', node.public_ips[0]) 274 self.assertTrue('10.176.168.218' in node.private_ips) 275 self.assertEqual(node.extra.get('flavorId'), '1') 276 self.assertEqual(node.extra.get('imageId'), '11') 277 self.assertEqual(type(node.extra.get('metadata')), type(dict())) 278 279 OpenStackMockHttp.type = 'METADATA' 280 ret = self.driver.list_nodes() 281 self.assertEqual(len(ret), 1) 282 node = ret[0] 283 self.assertEqual(type(node.extra.get('metadata')), type(dict())) 284 self.assertEqual(node.extra.get('metadata').get('somekey'), 285 'somevalue') 286 OpenStackMockHttp.type = None 287 288 def test_list_images(self): 289 ret = self.driver.list_images() 290 expected = {10: {'serverId': None, 291 'status': 'ACTIVE', 292 'created': '2009-07-20T09:14:37-05:00', 293 'updated': '2009-07-20T09:14:37-05:00', 294 'progress': None, 295 'minDisk': None, 296 'minRam': None}, 297 11: {'serverId': '91221', 298 'status': 'ACTIVE', 299 'created': '2009-11-29T20:22:09-06:00', 300 'updated': '2009-11-29T20:24:08-06:00', 301 'progress': '100', 302 'minDisk': '5', 303 'minRam': '256'}} 304 for ret_idx, extra in list(expected.items()): 305 for key, value in list(extra.items()): 306 self.assertEqual(ret[ret_idx].extra[key], value) 307 308 def test_create_node(self): 309 image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', 310 driver=self.driver) 311 size = NodeSize(1, '256 slice', None, None, None, None, 312 driver=self.driver) 313 node = self.driver.create_node(name='racktest', image=image, size=size) 314 self.assertEqual(node.name, 'racktest') 315 self.assertEqual(node.extra.get('password'), 'racktestvJq7d3') 316 317 def test_create_node_without_adminPass(self): 318 OpenStackMockHttp.type = 'NO_ADMIN_PASS' 319 image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', 320 driver=self.driver) 321 size = NodeSize(1, '256 slice', None, None, None, None, 322 driver=self.driver) 323 node = self.driver.create_node(name='racktest', image=image, size=size) 324 self.assertEqual(node.name, 'racktest') 325 self.assertIsNone(node.extra.get('password')) 326 327 def test_create_node_ex_shared_ip_group(self): 328 OpenStackMockHttp.type = 'EX_SHARED_IP_GROUP' 329 image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', 330 driver=self.driver) 331 size = NodeSize(1, '256 slice', None, None, None, None, 332 driver=self.driver) 333 node = self.driver.create_node(name='racktest', image=image, size=size, 334 ex_shared_ip_group_id='12345') 335 self.assertEqual(node.name, 'racktest') 336 self.assertEqual(node.extra.get('password'), 'racktestvJq7d3') 337 338 def test_create_node_with_metadata(self): 339 OpenStackMockHttp.type = 'METADATA' 340 image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', 341 driver=self.driver) 342 size = NodeSize(1, '256 slice', None, None, None, None, 343 driver=self.driver) 344 metadata = {'a': 'b', 'c': 'd'} 345 files = {'/file1': 'content1', '/file2': 'content2'} 346 node = self.driver.create_node(name='racktest', image=image, size=size, 347 ex_metadata=metadata, ex_files=files) 348 self.assertEqual(node.name, 'racktest') 349 self.assertEqual(node.extra.get('password'), 'racktestvJq7d3') 350 self.assertEqual(node.extra.get('metadata'), metadata) 351 352 def test_reboot_node(self): 353 node = Node(id=72258, name=None, state=None, public_ips=None, 354 private_ips=None, driver=self.driver) 355 ret = node.reboot() 356 self.assertTrue(ret is True) 357 358 def test_destroy_node(self): 359 node = Node(id=72258, name=None, state=None, public_ips=None, 360 private_ips=None, driver=self.driver) 361 ret = node.destroy() 362 self.assertTrue(ret is True) 363 364 def test_ex_limits(self): 365 limits = self.driver.ex_limits() 366 self.assertTrue("rate" in limits) 367 self.assertTrue("absolute" in limits) 368 369 def test_create_image(self): 370 node = Node(id=444222, name=None, state=None, public_ips=None, 371 private_ips=None, driver=self.driver) 372 image = self.driver.create_image(node, "imgtest") 373 self.assertEqual(image.name, "imgtest") 374 self.assertEqual(image.id, "12345") 375 376 def test_delete_image(self): 377 image = NodeImage(id=333111, name='Ubuntu 8.10 (intrepid)', 378 driver=self.driver) 379 ret = self.driver.delete_image(image) 380 self.assertTrue(ret) 381 382 def test_ex_list_ip_addresses(self): 383 ret = self.driver.ex_list_ip_addresses(node_id=72258) 384 self.assertEqual(2, len(ret.public_addresses)) 385 self.assertTrue('67.23.10.131' in ret.public_addresses) 386 self.assertTrue('67.23.10.132' in ret.public_addresses) 387 self.assertEqual(1, len(ret.private_addresses)) 388 self.assertTrue('10.176.42.16' in ret.private_addresses) 389 390 def test_ex_list_ip_groups(self): 391 ret = self.driver.ex_list_ip_groups() 392 self.assertEqual(2, len(ret)) 393 self.assertEqual('1234', ret[0].id) 394 self.assertEqual('Shared IP Group 1', ret[0].name) 395 self.assertEqual('5678', ret[1].id) 396 self.assertEqual('Shared IP Group 2', ret[1].name) 397 self.assertTrue(ret[0].servers is None) 398 399 def test_ex_list_ip_groups_detail(self): 400 ret = self.driver.ex_list_ip_groups(details=True) 401 402 self.assertEqual(2, len(ret)) 403 404 self.assertEqual('1234', ret[0].id) 405 self.assertEqual('Shared IP Group 1', ret[0].name) 406 self.assertEqual(2, len(ret[0].servers)) 407 self.assertEqual('422', ret[0].servers[0]) 408 self.assertEqual('3445', ret[0].servers[1]) 409 410 self.assertEqual('5678', ret[1].id) 411 self.assertEqual('Shared IP Group 2', ret[1].name) 412 self.assertEqual(3, len(ret[1].servers)) 413 self.assertEqual('23203', ret[1].servers[0]) 414 self.assertEqual('2456', ret[1].servers[1]) 415 self.assertEqual('9891', ret[1].servers[2]) 416 417 def test_ex_create_ip_group(self): 418 ret = self.driver.ex_create_ip_group('Shared IP Group 1', '5467') 419 self.assertEqual('1234', ret.id) 420 self.assertEqual('Shared IP Group 1', ret.name) 421 self.assertEqual(1, len(ret.servers)) 422 self.assertEqual('422', ret.servers[0]) 423 424 def test_ex_delete_ip_group(self): 425 ret = self.driver.ex_delete_ip_group('5467') 426 self.assertEqual(True, ret) 427 428 def test_ex_share_ip(self): 429 ret = self.driver.ex_share_ip('1234', '3445', '67.23.21.133') 430 self.assertEqual(True, ret) 431 432 def test_ex_unshare_ip(self): 433 ret = self.driver.ex_unshare_ip('3445', '67.23.21.133') 434 self.assertEqual(True, ret) 435 436 def test_ex_resize(self): 437 node = Node(id=444222, name=None, state=None, public_ips=None, 438 private_ips=None, driver=self.driver) 439 size = NodeSize(1, '256 slice', None, None, None, None, 440 driver=self.driver) 441 self.assertTrue(self.driver.ex_resize(node=node, size=size)) 442 443 def test_ex_confirm_resize(self): 444 node = Node(id=444222, name=None, state=None, public_ips=None, 445 private_ips=None, driver=self.driver) 446 self.assertTrue(self.driver.ex_confirm_resize(node=node)) 447 448 def test_ex_revert_resize(self): 449 node = Node(id=444222, name=None, state=None, public_ips=None, 450 private_ips=None, driver=self.driver) 451 self.assertTrue(self.driver.ex_revert_resize(node=node)) 452 453 def test_list_sizes(self): 454 sizes = self.driver.list_sizes() 455 self.assertEqual(len(sizes), 7, 'Wrong sizes count') 456 457 for size in sizes: 458 self.assertTrue(isinstance(size.price, float), 459 'Wrong size price type') 460 461 if self.driver.api_name == 'openstack': 462 self.assertEqual(size.price, 0, 463 'Size price should be zero by default') 464 465 def test_list_sizes_with_specified_pricing(self): 466 if self.driver.api_name != 'openstack': 467 return 468 469 pricing = dict((str(i), i) for i in range(1, 8)) 470 471 set_pricing(driver_type='compute', driver_name='openstack', 472 pricing=pricing) 473 474 sizes = self.driver.list_sizes() 475 self.assertEqual(len(sizes), 7, 'Wrong sizes count') 476 477 for size in sizes: 478 self.assertTrue(isinstance(size.price, float), 479 'Wrong size price type') 480 self.assertEqual(float(size.price), float(pricing[size.id])) 481 482 483class OpenStack_1_0_FactoryMethodTests(OpenStack_1_0_Tests): 484 should_list_locations = False 485 should_list_volumes = False 486 487 driver_klass = OpenStack_1_0_NodeDriver 488 driver_type = get_driver(Provider.OPENSTACK) 489 driver_args = OPENSTACK_PARAMS + ('1.0',) 490 491 def test_factory_method_invalid_version(self): 492 try: 493 self.driver_type(*(OPENSTACK_PARAMS + ('15.5',))) 494 except NotImplementedError: 495 pass 496 else: 497 self.fail('Exception was not thrown') 498 499 500class OpenStackMockHttp(MockHttp, unittest.TestCase): 501 fixtures = ComputeFileFixtures('openstack') 502 auth_fixtures = OpenStackFixtures() 503 json_content_headers = {'content-type': 'application/json; charset=UTF-8'} 504 505 # fake auth token response 506 def _v1_0(self, method, url, body, headers): 507 headers = { 508 'x-server-management-url': 'https://servers.api.rackspacecloud.com/v1.0/slug', 509 'x-auth-token': 'FE011C19-CF86-4F87-BE5D-9229145D7A06', 510 'x-cdn-management-url': 'https://cdn.clouddrive.com/v1/MossoCloudFS_FE011C19-CF86-4F87-BE5D-9229145D7A06', 511 'x-storage-token': 'FE011C19-CF86-4F87-BE5D-9229145D7A06', 512 'x-storage-url': 'https://storage4.clouddrive.com/v1/MossoCloudFS_FE011C19-CF86-4F87-BE5D-9229145D7A06'} 513 return (httplib.NO_CONTENT, "", headers, httplib.responses[httplib.NO_CONTENT]) 514 515 def _v1_0_UNAUTHORIZED(self, method, url, body, headers): 516 return (httplib.UNAUTHORIZED, "", {}, httplib.responses[httplib.UNAUTHORIZED]) 517 518 def _v1_0_INTERNAL_SERVER_ERROR(self, method, url, body, headers): 519 return (httplib.INTERNAL_SERVER_ERROR, "<h1>500: Internal Server Error</h1>", {}, 520 httplib.responses[httplib.INTERNAL_SERVER_ERROR]) 521 522 def _v1_0_slug_images_detail_NO_MESSAGE_IN_ERROR_BODY(self, method, url, body, headers): 523 body = self.fixtures.load('300_multiple_choices.json') 524 return (httplib.MULTIPLE_CHOICES, body, self.json_content_headers, httplib.responses[httplib.OK]) 525 526 def _v1_0_UNAUTHORIZED_MISSING_KEY(self, method, url, body, headers): 527 headers = { 528 'x-server-management-url': 'https://servers.api.rackspacecloud.com/v1.0/slug', 529 'x-auth-tokenx': 'FE011C19-CF86-4F87-BE5D-9229145D7A06', 530 'x-cdn-management-url': 'https://cdn.clouddrive.com/v1/MossoCloudFS_FE011C19-CF86-4F87-BE5D-9229145D7A06'} 531 return (httplib.NO_CONTENT, "", headers, httplib.responses[httplib.NO_CONTENT]) 532 533 def _v2_0_tokens(self, method, url, body, headers): 534 body = self.auth_fixtures.load('_v2_0__auth.json') 535 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 536 537 def _v1_0_slug_servers_detail_EMPTY(self, method, url, body, headers): 538 body = self.fixtures.load('v1_slug_servers_detail_empty.xml') 539 return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK]) 540 541 def _v1_0_slug_servers_detail(self, method, url, body, headers): 542 body = self.fixtures.load('v1_slug_servers_detail.xml') 543 return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK]) 544 545 def _v1_0_slug_servers_detail_METADATA(self, method, url, body, headers): 546 body = self.fixtures.load('v1_slug_servers_detail_metadata.xml') 547 return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK]) 548 549 def _v1_0_slug_servers_detail_UNAUTHORIZED(self, method, url, body, headers): 550 return (httplib.UNAUTHORIZED, "", {}, httplib.responses[httplib.UNAUTHORIZED]) 551 552 def _v1_0_slug_images_333111(self, method, url, body, headers): 553 if method != "DELETE": 554 raise NotImplementedError() 555 # this is currently used for deletion of an image 556 # as such it should not accept GET/POST 557 return(httplib.NO_CONTENT, "", {}, httplib.responses[httplib.NO_CONTENT]) 558 559 def _v1_0_slug_images(self, method, url, body, headers): 560 if method != "POST": 561 raise NotImplementedError() 562 # this is currently used for creation of new image with 563 # POST request, don't handle GET to avoid possible confusion 564 body = self.fixtures.load('v1_slug_images_post.xml') 565 return (httplib.ACCEPTED, body, XML_HEADERS, httplib.responses[httplib.ACCEPTED]) 566 567 def _v1_0_slug_images_detail(self, method, url, body, headers): 568 if method != "GET": 569 raise ValueError('Invalid method: %s' % (method)) 570 571 body = self.fixtures.load('v1_slug_images_detail.xml') 572 return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK]) 573 574 def _v1_0_slug_images_detail_invalid_next(self, method, url, body, headers): 575 if method != "GET": 576 raise ValueError('Invalid method: %s' % (method)) 577 578 body = self.fixtures.load('v1_slug_images_detail.xml') 579 return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK]) 580 581 def _v1_0_slug_servers(self, method, url, body, headers): 582 body = self.fixtures.load('v1_slug_servers.xml') 583 return (httplib.ACCEPTED, body, XML_HEADERS, httplib.responses[httplib.ACCEPTED]) 584 585 def _v1_0_slug_servers_NO_ADMIN_PASS(self, method, url, body, headers): 586 body = self.fixtures.load('v1_slug_servers_no_admin_pass.xml') 587 return (httplib.ACCEPTED, body, XML_HEADERS, httplib.responses[httplib.ACCEPTED]) 588 589 def _v1_0_slug_servers_EX_SHARED_IP_GROUP(self, method, url, body, headers): 590 # test_create_node_ex_shared_ip_group 591 # Verify that the body contains sharedIpGroupId XML element 592 body = u(body) 593 self.assertTrue(body.find('sharedIpGroupId="12345"') != -1) 594 body = self.fixtures.load('v1_slug_servers.xml') 595 return (httplib.ACCEPTED, body, XML_HEADERS, httplib.responses[httplib.ACCEPTED]) 596 597 def _v1_0_slug_servers_METADATA(self, method, url, body, headers): 598 body = self.fixtures.load('v1_slug_servers_metadata.xml') 599 return (httplib.ACCEPTED, body, XML_HEADERS, httplib.responses[httplib.ACCEPTED]) 600 601 def _v1_0_slug_servers_72258_action(self, method, url, body, headers): 602 if method != "POST" or body[:8] != "<reboot ": 603 raise NotImplementedError() 604 # only used by reboot() right now, but we will need to parse body 605 # someday !!!! 606 return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) 607 608 def _v1_0_slug_limits(self, method, url, body, headers): 609 body = self.fixtures.load('v1_slug_limits.xml') 610 return (httplib.ACCEPTED, body, XML_HEADERS, httplib.responses[httplib.ACCEPTED]) 611 612 def _v1_0_slug_servers_72258(self, method, url, body, headers): 613 if method != "DELETE": 614 raise NotImplementedError() 615 # only used by destroy node() 616 return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) 617 618 def _v1_0_slug_servers_72258_ips(self, method, url, body, headers): 619 body = self.fixtures.load('v1_slug_servers_ips.xml') 620 return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK]) 621 622 def _v1_0_slug_shared_ip_groups_5467(self, method, url, body, headers): 623 if method != 'DELETE': 624 raise NotImplementedError() 625 return (httplib.NO_CONTENT, "", {}, httplib.responses[httplib.NO_CONTENT]) 626 627 def _v1_0_slug_shared_ip_groups(self, method, url, body, headers): 628 629 fixture = 'v1_slug_shared_ip_group.xml' if method == 'POST' else 'v1_slug_shared_ip_groups.xml' 630 body = self.fixtures.load(fixture) 631 return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK]) 632 633 def _v1_0_slug_shared_ip_groups_detail(self, method, url, body, headers): 634 body = self.fixtures.load('v1_slug_shared_ip_groups_detail.xml') 635 return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK]) 636 637 def _v1_0_slug_servers_3445_ips_public_67_23_21_133(self, method, url, body, headers): 638 return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) 639 640 def _v1_0_slug_servers_444222_action(self, method, url, body, headers): 641 body = u(body) 642 if body.find('resize') != -1: 643 # test_ex_resize_server 644 if body.find('personality') != -1: 645 return httplib.BAD_REQUEST 646 else: 647 return (httplib.ACCEPTED, "", headers, httplib.responses[httplib.NO_CONTENT]) 648 elif body.find('confirmResize') != -1: 649 # test_ex_confirm_resize 650 return (httplib.NO_CONTENT, "", headers, httplib.responses[httplib.NO_CONTENT]) 651 elif body.find('revertResize') != -1: 652 # test_ex_revert_resize 653 return (httplib.NO_CONTENT, "", headers, httplib.responses[httplib.NO_CONTENT]) 654 655 def _v1_0_slug_flavors_detail(self, method, url, body, headers): 656 body = self.fixtures.load('v1_slug_flavors_detail.xml') 657 headers = { 658 'date': 'Tue, 14 Jun 2011 09:43:55 GMT', 'content-length': '529'} 659 headers.update(XML_HEADERS) 660 return (httplib.OK, body, headers, httplib.responses[httplib.OK]) 661 662 def _v1_1_auth(self, method, url, body, headers): 663 body = self.auth_fixtures.load('_v1_1__auth.json') 664 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 665 666 def _v1_1_auth_UNAUTHORIZED(self, method, url, body, headers): 667 body = self.auth_fixtures.load('_v1_1__auth_unauthorized.json') 668 return (httplib.UNAUTHORIZED, body, self.json_content_headers, httplib.responses[httplib.UNAUTHORIZED]) 669 670 def _v1_1_auth_UNAUTHORIZED_MISSING_KEY(self, method, url, body, headers): 671 body = self.auth_fixtures.load('_v1_1__auth_mssing_token.json') 672 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 673 674 def _v1_1_auth_INTERNAL_SERVER_ERROR(self, method, url, body, headers): 675 return (httplib.INTERNAL_SERVER_ERROR, "<h1>500: Internal Server Error</h1>", {'content-type': 'text/html'}, 676 httplib.responses[httplib.INTERNAL_SERVER_ERROR]) 677 678 679class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin): 680 should_list_locations = False 681 should_list_volumes = True 682 683 driver_klass = OpenStack_1_1_NodeDriver 684 driver_type = OpenStack_1_1_NodeDriver 685 driver_args = OPENSTACK_PARAMS 686 driver_kwargs = {'ex_force_auth_version': '2.0'} 687 688 @classmethod 689 def create_driver(self): 690 if self is not OpenStack_1_1_FactoryMethodTests: 691 self.driver_type = self.driver_klass 692 return self.driver_type(*self.driver_args, **self.driver_kwargs) 693 694 def setUp(self): 695 self.driver_klass.connectionCls.conn_class = OpenStack_2_0_MockHttp 696 self.driver_klass.connectionCls.auth_url = "https://auth.api.example.com" 697 698 OpenStackMockHttp.type = None 699 OpenStack_1_1_MockHttp.type = None 700 OpenStack_2_0_MockHttp.type = None 701 702 self.driver = self.create_driver() 703 704 # normally authentication happens lazily, but we force it here 705 self.driver.connection._populate_hosts_and_request_paths() 706 clear_pricing_data() 707 self.node = self.driver.list_nodes()[1] 708 709 def _force_reauthentication(self): 710 """ 711 Trash current auth token so driver will be forced to re-authenticate 712 on next request. 713 """ 714 self.driver.connection._ex_force_base_url = 'http://ex_force_base_url.com:666/forced_url' 715 self.driver.connection.auth_token = None 716 self.driver.connection.auth_token_expires = None 717 self.driver.connection._osa.auth_token = None 718 self.driver.connection._osa.auth_token_expires = None 719 720 def test_auth_token_is_set(self): 721 self._force_reauthentication() 722 self.driver.connection._populate_hosts_and_request_paths() 723 724 self.assertEqual( 725 self.driver.connection.auth_token, "aaaaaaaaaaaa-bbb-cccccccccccccc") 726 727 def test_auth_token_expires_is_set(self): 728 self._force_reauthentication() 729 self.driver.connection._populate_hosts_and_request_paths() 730 731 expires = self.driver.connection.auth_token_expires 732 self.assertEqual(expires.isoformat(), "2999-11-23T21:00:14-06:00") 733 734 def test_ex_force_base_url(self): 735 # change base url and trash the current auth token so we can 736 # re-authenticate 737 self.driver.connection._ex_force_base_url = 'http://ex_force_base_url.com:666/forced_url' 738 self.driver.connection.auth_token = None 739 self.driver.connection._populate_hosts_and_request_paths() 740 741 # assert that we use the base url and not the auth url 742 self.assertEqual(self.driver.connection.host, 'ex_force_base_url.com') 743 self.assertEqual(self.driver.connection.port, 666) 744 self.assertEqual(self.driver.connection.request_path, '/forced_url') 745 746 def test_get_endpoint_populates_host_port_and_request_path(self): 747 # simulate a subclass overriding this method 748 self.driver.connection.get_endpoint = lambda: 'http://endpoint_auth_url.com:1555/service_url' 749 self.driver.connection.auth_token = None 750 self.driver.connection._ex_force_base_url = None 751 self.driver.connection._populate_hosts_and_request_paths() 752 753 # assert that we use the result of get endpoint 754 self.assertEqual(self.driver.connection.host, 'endpoint_auth_url.com') 755 self.assertEqual(self.driver.connection.port, 1555) 756 self.assertEqual(self.driver.connection.request_path, '/service_url') 757 758 def test_set_auth_token_populates_host_port_and_request_path(self): 759 # change base url and trash the current auth token so we can 760 # re-authenticate 761 self.driver.connection._ex_force_base_url = 'http://some_other_ex_force_base_url.com:1222/some-service' 762 self.driver.connection.auth_token = "preset-auth-token" 763 self.driver.connection._populate_hosts_and_request_paths() 764 765 # assert that we use the base url and not the auth url 766 self.assertEqual( 767 self.driver.connection.host, 'some_other_ex_force_base_url.com') 768 self.assertEqual(self.driver.connection.port, 1222) 769 self.assertEqual(self.driver.connection.request_path, '/some-service') 770 771 def test_auth_token_without_base_url_raises_exception(self): 772 kwargs = { 773 'ex_force_auth_version': '2.0', 774 'ex_force_auth_token': 'preset-auth-token' 775 } 776 try: 777 self.driver_type(*self.driver_args, **kwargs) 778 self.fail('Expected failure setting auth token without base url') 779 except LibcloudError: 780 pass 781 else: 782 self.fail('Expected failure setting auth token without base url') 783 784 def test_ex_force_auth_token_passed_to_connection(self): 785 base_url = 'https://servers.api.rackspacecloud.com/v1.1/slug' 786 kwargs = { 787 'ex_force_auth_version': '2.0', 788 'ex_force_auth_token': 'preset-auth-token', 789 'ex_force_base_url': base_url 790 } 791 792 driver = self.driver_type(*self.driver_args, **kwargs) 793 driver.list_nodes() 794 795 self.assertEqual(kwargs['ex_force_auth_token'], 796 driver.connection.auth_token) 797 self.assertEqual('servers.api.rackspacecloud.com', 798 driver.connection.host) 799 self.assertEqual('/v1.1/slug', driver.connection.request_path) 800 self.assertEqual(443, driver.connection.port) 801 802 def test_ex_auth_cache_passed_to_identity_connection(self): 803 kwargs = self.driver_kwargs.copy() 804 kwargs['ex_auth_cache'] = OpenStackMockAuthCache() 805 driver = self.driver_type(*self.driver_args, **kwargs) 806 osa = driver.connection.get_auth_class() 807 driver.list_nodes() 808 self.assertEqual(kwargs['ex_auth_cache'], 809 driver.connection.get_auth_class().auth_cache) 810 811 def test_unauthorized_clears_cached_auth_context(self): 812 auth_cache = OpenStackMockAuthCache() 813 self.assertEqual(len(auth_cache), 0) 814 815 kwargs = self.driver_kwargs.copy() 816 kwargs['ex_auth_cache'] = auth_cache 817 driver = self.driver_type(*self.driver_args, **kwargs) 818 driver.list_nodes() 819 820 # Token was cached 821 self.assertEqual(len(auth_cache), 1) 822 823 # Simulate token being revoked 824 self.driver_klass.connectionCls.conn_class.type = 'UNAUTHORIZED' 825 with pytest.raises(BaseHTTPError) as ex: 826 driver.list_nodes() 827 828 # Token was evicted 829 self.assertEqual(len(auth_cache), 0) 830 831 def test_list_nodes(self): 832 nodes = self.driver.list_nodes() 833 self.assertEqual(len(nodes), 2) 834 node = nodes[0] 835 836 self.assertEqual('12065', node.id) 837 838 # test public IPv4 839 self.assertTrue('12.16.18.28' in node.public_ips) 840 self.assertTrue('50.57.94.35' in node.public_ips) 841 842 # fixed public ip 843 self.assertTrue('1.1.1.1' in node.public_ips) 844 845 # floating public ip 846 self.assertTrue('2.2.2.2' in node.public_ips) 847 848 # test public IPv6 849 self.assertTrue( 850 '2001:4801:7808:52:16:3eff:fe47:788a' in node.public_ips) 851 852 # test private IPv4 853 self.assertTrue('10.182.64.34' in node.private_ips) 854 855 # fixed private ip 856 self.assertTrue('10.3.3.3' in node.private_ips) 857 858 # floating private ip 859 self.assertTrue('192.168.3.3' in node.private_ips) 860 self.assertTrue('172.16.1.1' in node.private_ips) 861 862 # test private IPv6 863 self.assertTrue( 864 'fec0:4801:7808:52:16:3eff:fe60:187d' in node.private_ips) 865 866 # test creation date 867 self.assertEqual(node.created_at, datetime.datetime(2011, 10, 11, 0, 51, 39, tzinfo=UTC)) 868 869 self.assertEqual(node.extra.get('flavorId'), '2') 870 self.assertEqual(node.extra.get('imageId'), '7') 871 self.assertEqual(node.extra.get('metadata'), {}) 872 self.assertEqual(node.extra['updated'], '2011-10-11T00:50:04Z') 873 self.assertEqual(node.extra['created'], '2011-10-11T00:51:39Z') 874 self.assertEqual(node.extra.get('userId'), 'rs-reach') 875 self.assertEqual(node.extra.get('hostId'), '912566d83a13fbb357ea' 876 '3f13c629363d9f7e1ba3f' 877 '925b49f3d2ab725') 878 self.assertEqual(node.extra.get('disk_config'), 'AUTO') 879 self.assertEqual(node.extra.get('task_state'), 'spawning') 880 self.assertEqual(node.extra.get('vm_state'), 'active') 881 self.assertEqual(node.extra.get('power_state'), 1) 882 self.assertEqual(node.extra.get('progress'), 25) 883 self.assertEqual(node.extra.get('fault')['id'], 1234) 884 self.assertTrue(node.extra.get('service_name') is not None) 885 self.assertTrue(node.extra.get('uri') is not None) 886 887 def test_list_nodes_no_image_id_attribute(self): 888 # Regression test for LIBCLOD-455 889 self.driver_klass.connectionCls.conn_class.type = 'ERROR_STATE_NO_IMAGE_ID' 890 891 nodes = self.driver.list_nodes() 892 self.assertIsNone(nodes[0].extra['imageId']) 893 894 def test_list_volumes(self): 895 volumes = self.driver.list_volumes() 896 self.assertEqual(len(volumes), 2) 897 volume = volumes[0] 898 899 self.assertEqual('cd76a3a1-c4ce-40f6-9b9f-07a61508938d', volume.id) 900 self.assertEqual('test_volume_2', volume.name) 901 self.assertEqual(StorageVolumeState.AVAILABLE, volume.state) 902 self.assertEqual(2, volume.size) 903 self.assertEqual(volume.extra, { 904 'description': '', 905 'attachments': [{ 906 'id': 'cd76a3a1-c4ce-40f6-9b9f-07a61508938d', 907 "device": "/dev/vdb", 908 "serverId": "12065", 909 "volumeId": "cd76a3a1-c4ce-40f6-9b9f-07a61508938d", 910 }], 911 'snapshot_id': None, 912 'state': 'available', 913 'location': 'nova', 914 'volume_type': 'None', 915 'metadata': {}, 916 'created_at': '2013-06-24T11:20:13.000000', 917 }) 918 919 # also test that unknown state resolves to StorageVolumeState.UNKNOWN 920 volume = volumes[1] 921 self.assertEqual('cfcec3bc-b736-4db5-9535-4c24112691b5', volume.id) 922 self.assertEqual('test_volume', volume.name) 923 self.assertEqual(50, volume.size) 924 self.assertEqual(StorageVolumeState.UNKNOWN, volume.state) 925 self.assertEqual(volume.extra, { 926 'description': 'some description', 927 'attachments': [], 928 'snapshot_id': '01f48111-7866-4cd2-986a-e92683c4a363', 929 'state': 'some-unknown-state', 930 'location': 'nova', 931 'volume_type': 'None', 932 'metadata': {}, 933 'created_at': '2013-06-21T12:39:02.000000', 934 }) 935 936 def test_list_sizes(self): 937 sizes = self.driver.list_sizes() 938 self.assertEqual(len(sizes), 8, 'Wrong sizes count') 939 940 for size in sizes: 941 self.assertTrue(size.price is None or isinstance(size.price, float), 942 'Wrong size price type') 943 self.assertTrue(isinstance(size.ram, int)) 944 self.assertTrue(isinstance(size.vcpus, int)) 945 self.assertTrue(isinstance(size.disk, int)) 946 self.assertTrue(isinstance(size.swap, int)) 947 self.assertTrue(isinstance(size.ephemeral_disk, int) or 948 size.ephemeral_disk is None) 949 self.assertTrue(isinstance(size.extra, dict)) 950 if size.id == '1': 951 self.assertEqual(size.ephemeral_disk, 40) 952 self.assertEqual(size.extra, { 953 "policy_class": "standard_flavor", 954 "class": "standard1", 955 "disk_io_index": "2", 956 "number_of_data_disks": "0", 957 "disabled": False 958 }) 959 960 self.assertEqual(sizes[0].vcpus, 8) 961 962 def test_list_sizes_with_specified_pricing(self): 963 964 pricing = dict((str(i), i * 5.0) for i in range(1, 9)) 965 966 set_pricing(driver_type='compute', 967 driver_name=self.driver.api_name, pricing=pricing) 968 969 sizes = self.driver.list_sizes() 970 self.assertEqual(len(sizes), 8, 'Wrong sizes count') 971 972 for size in sizes: 973 self.assertTrue(isinstance(size.price, float), 974 'Wrong size price type') 975 976 self.assertEqual(size.price, pricing[size.id], 977 'Size price should match') 978 979 def test_list_images(self): 980 images = self.driver.list_images() 981 self.assertEqual(len(images), 13, 'Wrong images count') 982 983 image = images[0] 984 self.assertEqual(image.id, '13') 985 self.assertEqual(image.name, 'Windows 2008 SP2 x86 (B24)') 986 self.assertEqual(image.extra['updated'], '2011-08-06T18:14:02Z') 987 self.assertEqual(image.extra['created'], '2011-08-06T18:13:11Z') 988 self.assertEqual(image.extra['status'], 'ACTIVE') 989 self.assertEqual(image.extra['metadata']['os_type'], 'windows') 990 self.assertEqual( 991 image.extra['serverId'], '52415800-8b69-11e0-9b19-734f335aa7b3') 992 self.assertEqual(image.extra['minDisk'], 0) 993 self.assertEqual(image.extra['minRam'], 0) 994 995 def test_create_node(self): 996 image = NodeImage( 997 id=11, name='Ubuntu 8.10 (intrepid)', driver=self.driver) 998 size = NodeSize( 999 1, '256 slice', None, None, None, None, driver=self.driver) 1000 node = self.driver.create_node(name='racktest', image=image, size=size) 1001 self.assertEqual(node.id, '26f7fbee-8ce1-4c28-887a-bfe8e4bb10fe') 1002 self.assertEqual(node.name, 'racktest') 1003 self.assertEqual(node.extra['password'], 'racktestvJq7d3') 1004 self.assertEqual(node.extra['metadata']['My Server Name'], 'Apache1') 1005 1006 def test_create_node_with_ex_keyname_and_ex_userdata(self): 1007 image = NodeImage( 1008 id=11, name='Ubuntu 8.10 (intrepid)', driver=self.driver) 1009 size = NodeSize( 1010 1, '256 slice', None, None, None, None, driver=self.driver) 1011 node = self.driver.create_node(name='racktest', image=image, size=size, 1012 ex_keyname='devstack', 1013 ex_userdata='sample data') 1014 self.assertEqual(node.id, '26f7fbee-8ce1-4c28-887a-bfe8e4bb10fe') 1015 self.assertEqual(node.name, 'racktest') 1016 self.assertEqual(node.extra['password'], 'racktestvJq7d3') 1017 self.assertEqual(node.extra['metadata']['My Server Name'], 'Apache1') 1018 self.assertEqual(node.extra['key_name'], 'devstack') 1019 1020 def test_create_node_with_availability_zone(self): 1021 image = NodeImage( 1022 id=11, name='Ubuntu 8.10 (intrepid)', driver=self.driver) 1023 size = NodeSize( 1024 1, '256 slice', None, None, None, None, driver=self.driver) 1025 node = self.driver.create_node(name='racktest', image=image, size=size, 1026 ex_availability_zone='testaz') 1027 self.assertEqual(node.id, '26f7fbee-8ce1-4c28-887a-bfe8e4bb10fe') 1028 self.assertEqual(node.name, 'racktest') 1029 self.assertEqual(node.extra['password'], 'racktestvJq7d3') 1030 self.assertEqual(node.extra['metadata']['My Server Name'], 'Apache1') 1031 self.assertEqual(node.extra['availability_zone'], 'testaz') 1032 1033 def test_create_node_with_ex_disk_config(self): 1034 OpenStack_1_1_MockHttp.type = 'EX_DISK_CONFIG' 1035 image = NodeImage( 1036 id=11, name='Ubuntu 8.10 (intrepid)', driver=self.driver) 1037 size = NodeSize( 1038 1, '256 slice', None, None, None, None, driver=self.driver) 1039 node = self.driver.create_node(name='racktest', image=image, size=size, 1040 ex_disk_config='AUTO') 1041 self.assertEqual(node.id, '26f7fbee-8ce1-4c28-887a-bfe8e4bb10fe') 1042 self.assertEqual(node.name, 'racktest') 1043 self.assertEqual(node.extra['disk_config'], 'AUTO') 1044 1045 def test_create_node_with_ex_config_drive(self): 1046 OpenStack_1_1_MockHttp.type = 'EX_CONFIG_DRIVE' 1047 image = NodeImage( 1048 id=11, name='Ubuntu 8.10 (intrepid)', driver=self.driver) 1049 size = NodeSize( 1050 1, '256 slice', None, None, None, None, driver=self.driver) 1051 node = self.driver.create_node(name='racktest', image=image, size=size, 1052 ex_config_drive=True) 1053 self.assertEqual(node.id, '26f7fbee-8ce1-4c28-887a-bfe8e4bb10fe') 1054 self.assertEqual(node.name, 'racktest') 1055 self.assertTrue(node.extra['config_drive']) 1056 1057 def test_create_node_from_bootable_volume(self): 1058 size = NodeSize( 1059 1, '256 slice', None, None, None, None, driver=self.driver) 1060 1061 node = self.driver.create_node( 1062 name='racktest', size=size, 1063 ex_blockdevicemappings=[ 1064 { 1065 'boot_index': 0, 1066 'uuid': 'ee7ee330-b454-4414-8e9f-c70c558dd3af', 1067 'source_type': 'volume', 1068 'destination_type': 'volume', 1069 'delete_on_termination': False 1070 }]) 1071 1072 self.assertEqual(node.id, '26f7fbee-8ce1-4c28-887a-bfe8e4bb10fe') 1073 self.assertEqual(node.name, 'racktest') 1074 self.assertEqual(node.extra['password'], 'racktestvJq7d3') 1075 self.assertEqual(node.extra['metadata']['My Server Name'], 'Apache1') 1076 1077 def test_destroy_node(self): 1078 self.assertTrue(self.node.destroy()) 1079 1080 def test_reboot_node(self): 1081 self.assertTrue(self.node.reboot()) 1082 1083 def test_create_volume(self): 1084 volume = self.driver.create_volume(1, 'test') 1085 self.assertEqual(volume.name, 'test') 1086 self.assertEqual(volume.size, 1) 1087 1088 def test_create_volume_passes_location_to_request_only_if_not_none(self): 1089 with patch.object(self.driver.connection, 'request') as mock_request: 1090 self.driver.create_volume(1, 'test', location='mylocation') 1091 name, args, kwargs = mock_request.mock_calls[0] 1092 self.assertEqual(kwargs["data"]["volume"]["availability_zone"], "mylocation") 1093 1094 def test_create_volume_does_not_pass_location_to_request_if_none(self): 1095 with patch.object(self.driver.connection, 'request') as mock_request: 1096 self.driver.create_volume(1, 'test') 1097 name, args, kwargs = mock_request.mock_calls[0] 1098 self.assertFalse("availability_zone" in kwargs["data"]["volume"]) 1099 1100 def test_create_volume_passes_volume_type_to_request_only_if_not_none(self): 1101 with patch.object(self.driver.connection, 'request') as mock_request: 1102 self.driver.create_volume(1, 'test', ex_volume_type='myvolumetype') 1103 name, args, kwargs = mock_request.mock_calls[0] 1104 self.assertEqual(kwargs["data"]["volume"]["volume_type"], "myvolumetype") 1105 1106 def test_create_volume_does_not_pass_volume_type_to_request_if_none(self): 1107 with patch.object(self.driver.connection, 'request') as mock_request: 1108 self.driver.create_volume(1, 'test') 1109 name, args, kwargs = mock_request.mock_calls[0] 1110 self.assertFalse("volume_type" in kwargs["data"]["volume"]) 1111 1112 def test_destroy_volume(self): 1113 volume = self.driver.ex_get_volume( 1114 'cd76a3a1-c4ce-40f6-9b9f-07a61508938d') 1115 self.assertEqual(self.driver.destroy_volume(volume), True) 1116 1117 def test_attach_volume(self): 1118 node = self.driver.list_nodes()[0] 1119 volume = self.driver.ex_get_volume( 1120 'cd76a3a1-c4ce-40f6-9b9f-07a61508938d') 1121 self.assertEqual( 1122 self.driver.attach_volume(node, volume, '/dev/sdb'), True) 1123 1124 def test_attach_volume_device_auto(self): 1125 node = self.driver.list_nodes()[0] 1126 volume = self.driver.ex_get_volume( 1127 'cd76a3a1-c4ce-40f6-9b9f-07a61508938d') 1128 1129 OpenStack_2_0_MockHttp.type = 'DEVICE_AUTO' 1130 1131 self.assertEqual( 1132 self.driver.attach_volume(node, volume, 'auto'), True) 1133 1134 def test_detach_volume(self): 1135 node = self.driver.list_nodes()[0] 1136 volume = self.driver.ex_get_volume( 1137 'cd76a3a1-c4ce-40f6-9b9f-07a61508938d') 1138 self.assertEqual( 1139 self.driver.attach_volume(node, volume, '/dev/sdb'), True) 1140 self.assertEqual(self.driver.detach_volume(volume), True) 1141 1142 def test_ex_set_password(self): 1143 self.assertTrue(self.driver.ex_set_password(self.node, 'New1&53jPass')) 1144 1145 def test_ex_rebuild(self): 1146 image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', 1147 driver=self.driver) 1148 success = self.driver.ex_rebuild(self.node, image=image) 1149 self.assertTrue(success) 1150 1151 def test_ex_rebuild_with_ex_disk_config(self): 1152 image = NodeImage(id=58, name='Ubuntu 10.10 (intrepid)', 1153 driver=self.driver) 1154 node = Node(id=12066, name=None, state=None, public_ips=None, 1155 private_ips=None, driver=self.driver) 1156 success = self.driver.ex_rebuild(node, image=image, 1157 ex_disk_config='MANUAL') 1158 self.assertTrue(success) 1159 1160 def test_ex_rebuild_with_ex_config_drive(self): 1161 image = NodeImage(id=58, name='Ubuntu 10.10 (intrepid)', 1162 driver=self.driver) 1163 node = Node(id=12066, name=None, state=None, public_ips=None, 1164 private_ips=None, driver=self.driver) 1165 success = self.driver.ex_rebuild(node, image=image, 1166 ex_disk_config='MANUAL', 1167 ex_config_drive=True) 1168 self.assertTrue(success) 1169 1170 def test_ex_resize(self): 1171 size = NodeSize(1, '256 slice', None, None, None, None, 1172 driver=self.driver) 1173 try: 1174 self.driver.ex_resize(self.node, size) 1175 except Exception as e: 1176 self.fail('An error was raised: ' + repr(e)) 1177 1178 def test_ex_confirm_resize(self): 1179 try: 1180 self.driver.ex_confirm_resize(self.node) 1181 except Exception as e: 1182 self.fail('An error was raised: ' + repr(e)) 1183 1184 def test_ex_revert_resize(self): 1185 try: 1186 self.driver.ex_revert_resize(self.node) 1187 except Exception as e: 1188 self.fail('An error was raised: ' + repr(e)) 1189 1190 def test_create_image(self): 1191 image = self.driver.create_image(self.node, 'new_image') 1192 self.assertEqual(image.name, 'new_image') 1193 self.assertEqual(image.id, '4949f9ee-2421-4c81-8b49-13119446008b') 1194 1195 def test_ex_set_server_name(self): 1196 old_node = Node( 1197 id='12064', name=None, state=None, 1198 public_ips=None, private_ips=None, driver=self.driver, 1199 ) 1200 new_node = self.driver.ex_set_server_name(old_node, 'Bob') 1201 self.assertEqual('Bob', new_node.name) 1202 1203 def test_ex_set_metadata(self): 1204 old_node = Node( 1205 id='12063', name=None, state=None, 1206 public_ips=None, private_ips=None, driver=self.driver, 1207 ) 1208 metadata = {'Image Version': '2.1', 'Server Label': 'Web Head 1'} 1209 returned_metadata = self.driver.ex_set_metadata(old_node, metadata) 1210 self.assertEqual(metadata, returned_metadata) 1211 1212 def test_ex_get_metadata(self): 1213 node = Node( 1214 id='12063', name=None, state=None, 1215 public_ips=None, private_ips=None, driver=self.driver, 1216 ) 1217 1218 metadata = {'Image Version': '2.1', 'Server Label': 'Web Head 1'} 1219 returned_metadata = self.driver.ex_get_metadata(node) 1220 self.assertEqual(metadata, returned_metadata) 1221 1222 def test_ex_update_node(self): 1223 old_node = Node( 1224 id='12064', 1225 name=None, state=None, public_ips=None, private_ips=None, driver=self.driver, 1226 ) 1227 1228 new_node = self.driver.ex_update_node(old_node, name='Bob') 1229 1230 self.assertTrue(new_node) 1231 self.assertEqual('Bob', new_node.name) 1232 self.assertEqual('50.57.94.30', new_node.public_ips[0]) 1233 1234 def test_ex_get_node_details(self): 1235 node_id = '12064' 1236 node = self.driver.ex_get_node_details(node_id) 1237 self.assertEqual(node.id, '12064') 1238 self.assertEqual(node.name, 'lc-test') 1239 1240 def test_ex_get_node_details_returns_none_if_node_does_not_exist(self): 1241 node = self.driver.ex_get_node_details('does-not-exist') 1242 self.assertTrue(node is None) 1243 1244 def test_ex_get_size(self): 1245 size_id = '7' 1246 size = self.driver.ex_get_size(size_id) 1247 self.assertEqual(size.id, size_id) 1248 self.assertEqual(size.name, '15.5GB slice') 1249 1250 def test_ex_get_size_extra_specs(self): 1251 size_id = '7' 1252 extra_specs = self.driver.ex_get_size_extra_specs(size_id) 1253 self.assertEqual(extra_specs, {"hw:cpu_policy": "shared", 1254 "hw:numa_nodes": "1"}) 1255 1256 def test_get_image(self): 1257 image_id = '13' 1258 image = self.driver.get_image(image_id) 1259 self.assertEqual(image.id, image_id) 1260 self.assertEqual(image.name, 'Windows 2008 SP2 x86 (B24)') 1261 self.assertIsNone(image.extra['serverId']) 1262 self.assertEqual(image.extra['minDisk'], "5") 1263 self.assertEqual(image.extra['minRam'], "256") 1264 self.assertIsNone(image.extra['visibility']) 1265 1266 def test_delete_image(self): 1267 image = NodeImage( 1268 id='26365521-8c62-11f9-2c33-283d153ecc3a', name='My Backup', driver=self.driver) 1269 result = self.driver.delete_image(image) 1270 self.assertTrue(result) 1271 1272 def test_extract_image_id_from_url(self): 1273 url = 'http://127.0.0.1/v1.1/68/images/1d4a8ea9-aae7-4242-a42d-5ff4702f2f14' 1274 url_two = 'http://127.0.0.1/v1.1/68/images/13' 1275 image_id = self.driver._extract_image_id_from_url(url) 1276 image_id_two = self.driver._extract_image_id_from_url(url_two) 1277 self.assertEqual(image_id, '1d4a8ea9-aae7-4242-a42d-5ff4702f2f14') 1278 self.assertEqual(image_id_two, '13') 1279 1280 def test_ex_rescue_with_password(self): 1281 node = Node(id=12064, name=None, state=None, public_ips=None, 1282 private_ips=None, driver=self.driver) 1283 n = self.driver.ex_rescue(node, 'foo') 1284 self.assertEqual(n.extra['password'], 'foo') 1285 1286 def test_ex_rescue_no_password(self): 1287 node = Node(id=12064, name=None, state=None, public_ips=None, 1288 private_ips=None, driver=self.driver) 1289 n = self.driver.ex_rescue(node) 1290 self.assertEqual(n.extra['password'], 'foo') 1291 1292 def test_ex_unrescue(self): 1293 node = Node(id=12064, name=None, state=None, public_ips=None, 1294 private_ips=None, driver=self.driver) 1295 result = self.driver.ex_unrescue(node) 1296 self.assertTrue(result) 1297 1298 def test_ex_get_node_security_groups(self): 1299 node = Node(id='1c01300f-ef97-4937-8f03-ac676d6234be', name=None, 1300 state=None, public_ips=None, private_ips=None, driver=self.driver) 1301 security_groups = self.driver.ex_get_node_security_groups(node) 1302 self.assertEqual( 1303 len(security_groups), 2, 'Wrong security groups count') 1304 1305 security_group = security_groups[1] 1306 self.assertEqual(security_group.id, 4) 1307 self.assertEqual(security_group.tenant_id, '68') 1308 self.assertEqual(security_group.name, 'ftp') 1309 self.assertEqual( 1310 security_group.description, 'FTP Client-Server - Open 20-21 ports') 1311 self.assertEqual(security_group.rules[0].id, 1) 1312 self.assertEqual(security_group.rules[0].parent_group_id, 4) 1313 self.assertEqual(security_group.rules[0].ip_protocol, "tcp") 1314 self.assertEqual(security_group.rules[0].from_port, 20) 1315 self.assertEqual(security_group.rules[0].to_port, 21) 1316 self.assertEqual(security_group.rules[0].ip_range, '0.0.0.0/0') 1317 1318 def test_ex_list_security_groups(self): 1319 security_groups = self.driver.ex_list_security_groups() 1320 self.assertEqual( 1321 len(security_groups), 2, 'Wrong security groups count') 1322 1323 security_group = security_groups[1] 1324 self.assertEqual(security_group.id, 4) 1325 self.assertEqual(security_group.tenant_id, '68') 1326 self.assertEqual(security_group.name, 'ftp') 1327 self.assertEqual( 1328 security_group.description, 'FTP Client-Server - Open 20-21 ports') 1329 self.assertEqual(security_group.rules[0].id, 1) 1330 self.assertEqual(security_group.rules[0].parent_group_id, 4) 1331 self.assertEqual(security_group.rules[0].ip_protocol, "tcp") 1332 self.assertEqual(security_group.rules[0].from_port, 20) 1333 self.assertEqual(security_group.rules[0].to_port, 21) 1334 self.assertEqual(security_group.rules[0].ip_range, '0.0.0.0/0') 1335 1336 def test_ex_create_security_group(self): 1337 name = 'test' 1338 description = 'Test Security Group' 1339 security_group = self.driver.ex_create_security_group( 1340 name, description) 1341 1342 self.assertEqual(security_group.id, 6) 1343 self.assertEqual(security_group.tenant_id, '68') 1344 self.assertEqual(security_group.name, name) 1345 self.assertEqual(security_group.description, description) 1346 self.assertEqual(len(security_group.rules), 0) 1347 1348 def test_ex_delete_security_group(self): 1349 security_group = OpenStackSecurityGroup( 1350 id=6, tenant_id=None, name=None, description=None, driver=self.driver) 1351 result = self.driver.ex_delete_security_group(security_group) 1352 self.assertTrue(result) 1353 1354 def test_ex_create_security_group_rule(self): 1355 security_group = OpenStackSecurityGroup( 1356 id=6, tenant_id=None, name=None, description=None, driver=self.driver) 1357 security_group_rule = self.driver.ex_create_security_group_rule( 1358 security_group, 'tcp', 14, 16, '0.0.0.0/0') 1359 1360 self.assertEqual(security_group_rule.id, 2) 1361 self.assertEqual(security_group_rule.parent_group_id, 6) 1362 self.assertEqual(security_group_rule.ip_protocol, 'tcp') 1363 self.assertEqual(security_group_rule.from_port, 14) 1364 self.assertEqual(security_group_rule.to_port, 16) 1365 self.assertEqual(security_group_rule.ip_range, '0.0.0.0/0') 1366 self.assertIsNone(security_group_rule.tenant_id) 1367 1368 def test_ex_delete_security_group_rule(self): 1369 security_group_rule = OpenStackSecurityGroupRule( 1370 id=2, parent_group_id=None, ip_protocol=None, from_port=None, to_port=None, driver=self.driver) 1371 result = self.driver.ex_delete_security_group_rule(security_group_rule) 1372 self.assertTrue(result) 1373 1374 def test_list_key_pairs(self): 1375 keypairs = self.driver.list_key_pairs() 1376 self.assertEqual(len(keypairs), 2, 'Wrong keypairs count') 1377 keypair = keypairs[1] 1378 self.assertEqual(keypair.name, 'key2') 1379 self.assertEqual( 1380 keypair.fingerprint, '5d:66:33:ae:99:0f:fb:cb:86:f2:bc:ae:53:99:b6:ed') 1381 self.assertTrue(len(keypair.public_key) > 10) 1382 self.assertIsNone(keypair.private_key) 1383 1384 def test_get_key_pair(self): 1385 key_pair = self.driver.get_key_pair(name='test-key-pair') 1386 1387 self.assertEqual(key_pair.name, 'test-key-pair') 1388 1389 def test_get_key_pair_doesnt_exist(self): 1390 self.assertRaises(KeyPairDoesNotExistError, 1391 self.driver.get_key_pair, 1392 name='doesnt-exist') 1393 1394 def test_create_key_pair(self): 1395 name = 'key0' 1396 keypair = self.driver.create_key_pair(name=name) 1397 self.assertEqual(keypair.name, name) 1398 1399 self.assertEqual(keypair.fingerprint, 1400 '80:f8:03:a7:8e:c1:c3:b1:7e:c5:8c:50:04:5e:1c:5b') 1401 self.assertTrue(len(keypair.public_key) > 10) 1402 self.assertTrue(len(keypair.private_key) > 10) 1403 1404 def test_import_key_pair_from_file(self): 1405 name = 'key3' 1406 path = os.path.join( 1407 os.path.dirname(__file__), 'fixtures', 'misc', 'test_rsa.pub') 1408 1409 with open(path, 'r') as fp: 1410 pub_key = fp.read() 1411 1412 keypair = self.driver.import_key_pair_from_file(name=name, 1413 key_file_path=path) 1414 self.assertEqual(keypair.name, name) 1415 self.assertEqual( 1416 keypair.fingerprint, '97:10:a6:e7:92:65:7e:69:fe:e6:81:8f:39:3c:8f:5a') 1417 self.assertEqual(keypair.public_key, pub_key) 1418 self.assertIsNone(keypair.private_key) 1419 1420 def test_import_key_pair_from_string(self): 1421 name = 'key3' 1422 path = os.path.join( 1423 os.path.dirname(__file__), 'fixtures', 'misc', 'test_rsa.pub') 1424 1425 with open(path, 'r') as fp: 1426 pub_key = fp.read() 1427 1428 keypair = self.driver.import_key_pair_from_string(name=name, 1429 key_material=pub_key) 1430 self.assertEqual(keypair.name, name) 1431 self.assertEqual( 1432 keypair.fingerprint, '97:10:a6:e7:92:65:7e:69:fe:e6:81:8f:39:3c:8f:5a') 1433 self.assertEqual(keypair.public_key, pub_key) 1434 self.assertIsNone(keypair.private_key) 1435 1436 def test_delete_key_pair(self): 1437 keypair = OpenStackKeyPair( 1438 name='key1', fingerprint=None, public_key=None, driver=self.driver) 1439 result = self.driver.delete_key_pair(key_pair=keypair) 1440 self.assertTrue(result) 1441 1442 def test_ex_list_floating_ip_pools(self): 1443 ret = self.driver.ex_list_floating_ip_pools() 1444 self.assertEqual(ret[0].name, 'public') 1445 self.assertEqual(ret[1].name, 'foobar') 1446 1447 def test_ex_attach_floating_ip_to_node(self): 1448 image = NodeImage( 1449 id=11, name='Ubuntu 8.10 (intrepid)', driver=self.driver) 1450 size = NodeSize( 1451 1, '256 slice', None, None, None, None, driver=self.driver) 1452 node = self.driver.create_node(name='racktest', image=image, size=size) 1453 node.id = 4242 1454 ip = '42.42.42.42' 1455 1456 self.assertTrue(self.driver.ex_attach_floating_ip_to_node(node, ip)) 1457 1458 def test_detach_floating_ip_from_node(self): 1459 image = NodeImage( 1460 id=11, name='Ubuntu 8.10 (intrepid)', driver=self.driver) 1461 size = NodeSize( 1462 1, '256 slice', None, None, None, None, driver=self.driver) 1463 node = self.driver.create_node(name='racktest', image=image, size=size) 1464 node.id = 4242 1465 ip = '42.42.42.42' 1466 1467 self.assertTrue(self.driver.ex_detach_floating_ip_from_node(node, ip)) 1468 1469 def test_OpenStack_1_1_FloatingIpPool_list_floating_ips(self): 1470 pool = OpenStack_1_1_FloatingIpPool('foo', self.driver.connection) 1471 ret = pool.list_floating_ips() 1472 1473 self.assertEqual(ret[0].id, '09ea1784-2f81-46dc-8c91-244b4df75bde') 1474 self.assertEqual(ret[0].pool, pool) 1475 self.assertEqual(ret[0].ip_address, '10.3.1.42') 1476 self.assertIsNone(ret[0].node_id) 1477 self.assertEqual(ret[1].id, '04c5336a-0629-4694-ba30-04b0bdfa88a4') 1478 self.assertEqual(ret[1].pool, pool) 1479 self.assertEqual(ret[1].ip_address, '10.3.1.1') 1480 self.assertEqual( 1481 ret[1].node_id, 'fcfc96da-19e2-40fd-8497-f29da1b21143') 1482 1483 def test_OpenStack_1_1_FloatingIpPool_get_floating_ip(self): 1484 pool = OpenStack_1_1_FloatingIpPool('foo', self.driver.connection) 1485 ret = pool.get_floating_ip('10.3.1.42') 1486 1487 self.assertEqual(ret.id, '09ea1784-2f81-46dc-8c91-244b4df75bde') 1488 self.assertEqual(ret.pool, pool) 1489 self.assertEqual(ret.ip_address, '10.3.1.42') 1490 self.assertIsNone(ret.node_id) 1491 1492 def test_OpenStack_1_1_FloatingIpPool_create_floating_ip(self): 1493 pool = OpenStack_1_1_FloatingIpPool('foo', self.driver.connection) 1494 ret = pool.create_floating_ip() 1495 1496 self.assertEqual(ret.id, '09ea1784-2f81-46dc-8c91-244b4df75bde') 1497 self.assertEqual(ret.pool, pool) 1498 self.assertEqual(ret.ip_address, '10.3.1.42') 1499 self.assertIsNone(ret.node_id) 1500 1501 def test_OpenStack_1_1_FloatingIpPool_delete_floating_ip(self): 1502 pool = OpenStack_1_1_FloatingIpPool('foo', self.driver.connection) 1503 ip = OpenStack_1_1_FloatingIpAddress('foo-bar-id', '42.42.42.42', pool) 1504 1505 self.assertTrue(pool.delete_floating_ip(ip)) 1506 1507 def test_OpenStack_1_1_FloatingIpAddress_delete(self): 1508 pool = OpenStack_1_1_FloatingIpPool('foo', self.driver.connection) 1509 pool.delete_floating_ip = Mock() 1510 ip = OpenStack_1_1_FloatingIpAddress('foo-bar-id', '42.42.42.42', pool) 1511 1512 ip.pool.delete_floating_ip() 1513 1514 self.assertEqual(pool.delete_floating_ip.call_count, 1) 1515 1516 def test_OpenStack_2_FloatingIpPool_list_floating_ips(self): 1517 pool = OpenStack_2_FloatingIpPool(1, 'foo', self.driver.connection) 1518 ret = pool.list_floating_ips() 1519 1520 self.assertEqual(ret[0].id, '09ea1784-2f81-46dc-8c91-244b4df75bde') 1521 self.assertEqual(ret[0].pool, pool) 1522 self.assertEqual(ret[0].ip_address, '10.3.1.42') 1523 self.assertEqual(ret[0].node_id, None) 1524 self.assertEqual(ret[1].id, '04c5336a-0629-4694-ba30-04b0bdfa88a4') 1525 self.assertEqual(ret[1].pool, pool) 1526 self.assertEqual(ret[1].ip_address, '10.3.1.1') 1527 self.assertEqual( 1528 ret[1].node_id, 'fcfc96da-19e2-40fd-8497-f29da1b21143') 1529 self.assertEqual(ret[2].id, '123c5336a-0629-4694-ba30-04b0bdfa88a4') 1530 self.assertEqual(ret[2].pool, pool) 1531 self.assertEqual(ret[2].ip_address, '10.3.1.2') 1532 self.assertEqual( 1533 ret[2].node_id, 'cb4fba64-19e2-40fd-8497-f29da1b21143') 1534 self.assertEqual(ret[3].id, '123c5336a-0629-4694-ba30-04b0bdfa88a4') 1535 self.assertEqual(ret[3].pool, pool) 1536 self.assertEqual(ret[3].ip_address, '10.3.1.3') 1537 self.assertEqual( 1538 ret[3].node_id, 'cb4fba64-19e2-40fd-8497-f29da1b21143') 1539 1540 def test_OpenStack_2_FloatingIpPool_get_floating_ip(self): 1541 pool = OpenStack_2_FloatingIpPool(1, 'foo', self.driver.connection) 1542 ret = pool.get_floating_ip('10.3.1.42') 1543 1544 self.assertEqual(ret.id, '09ea1784-2f81-46dc-8c91-244b4df75bde') 1545 self.assertEqual(ret.pool, pool) 1546 self.assertEqual(ret.ip_address, '10.3.1.42') 1547 self.assertEqual(ret.node_id, None) 1548 1549 def test_OpenStack_2_FloatingIpPool_create_floating_ip(self): 1550 pool = OpenStack_2_FloatingIpPool(1, 'foo', self.driver.connection) 1551 ret = pool.create_floating_ip() 1552 1553 self.assertEqual(ret.id, '09ea1784-2f81-46dc-8c91-244b4df75bde') 1554 self.assertEqual(ret.pool, pool) 1555 self.assertEqual(ret.ip_address, '10.3.1.42') 1556 self.assertEqual(ret.node_id, None) 1557 1558 def test_OpenStack_2_FloatingIpPool_delete_floating_ip(self): 1559 pool = OpenStack_2_FloatingIpPool(1, 'foo', self.driver.connection) 1560 ip = OpenStack_1_1_FloatingIpAddress('foo-bar-id', '42.42.42.42', pool) 1561 1562 self.assertTrue(pool.delete_floating_ip(ip)) 1563 1564 def test_OpenStack_2_FloatingIpAddress_delete(self): 1565 pool = OpenStack_2_FloatingIpPool(1, 'foo', self.driver.connection) 1566 pool.delete_floating_ip = Mock() 1567 ip = OpenStack_1_1_FloatingIpAddress('foo-bar-id', '42.42.42.42', pool) 1568 1569 ip.pool.delete_floating_ip() 1570 1571 self.assertEqual(pool.delete_floating_ip.call_count, 1) 1572 1573 def test_ex_get_metadata_for_node(self): 1574 image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', driver=self.driver) 1575 size = NodeSize(1, '256 slice', None, None, None, None, driver=self.driver) 1576 node = self.driver.create_node(name='foo', 1577 image=image, 1578 size=size) 1579 1580 metadata = self.driver.ex_get_metadata_for_node(node) 1581 self.assertEqual(metadata['My Server Name'], 'Apache1') 1582 self.assertEqual(len(metadata), 1) 1583 1584 def test_ex_pause_node(self): 1585 node = Node( 1586 id='12063', name=None, state=None, 1587 public_ips=None, private_ips=None, driver=self.driver, 1588 ) 1589 ret = self.driver.ex_pause_node(node) 1590 self.assertTrue(ret is True) 1591 1592 def test_ex_unpause_node(self): 1593 node = Node( 1594 id='12063', name=None, state=None, 1595 public_ips=None, private_ips=None, driver=self.driver, 1596 ) 1597 ret = self.driver.ex_unpause_node(node) 1598 self.assertTrue(ret is True) 1599 1600 def test_ex_stop_node(self): 1601 node = Node( 1602 id='12063', name=None, state=None, 1603 public_ips=None, private_ips=None, driver=self.driver, 1604 ) 1605 ret = self.driver.ex_stop_node(node) 1606 self.assertTrue(ret is True) 1607 1608 def test_ex_start_node(self): 1609 node = Node( 1610 id='12063', name=None, state=None, 1611 public_ips=None, private_ips=None, driver=self.driver, 1612 ) 1613 ret = self.driver.ex_start_node(node) 1614 self.assertTrue(ret is True) 1615 1616 def test_ex_suspend_node(self): 1617 node = Node( 1618 id='12063', name=None, state=None, 1619 public_ips=None, private_ips=None, driver=self.driver, 1620 ) 1621 ret = self.driver.ex_suspend_node(node) 1622 self.assertTrue(ret is True) 1623 1624 def test_ex_resume_node(self): 1625 node = Node( 1626 id='12063', name=None, state=None, 1627 public_ips=None, private_ips=None, driver=self.driver, 1628 ) 1629 ret = self.driver.ex_resume_node(node) 1630 self.assertTrue(ret is True) 1631 1632 def test_ex_get_console_output(self): 1633 node = Node( 1634 id='12086', name=None, state=None, 1635 public_ips=None, private_ips=None, driver=self.driver, 1636 ) 1637 resp = self.driver.ex_get_console_output(node) 1638 expected_output = 'FAKE CONSOLE OUTPUT\nANOTHER\nLAST LINE' 1639 self.assertEqual(resp['output'], expected_output) 1640 1641 def test_ex_list_snapshots(self): 1642 if self.driver_type.type == 'rackspace': 1643 self.conn_class.type = 'RACKSPACE' 1644 1645 snapshots = self.driver.ex_list_snapshots() 1646 self.assertEqual(len(snapshots), 3) 1647 self.assertEqual(snapshots[0].created, datetime.datetime(2012, 2, 29, 3, 50, 7, tzinfo=UTC)) 1648 self.assertEqual(snapshots[0].extra['created'], "2012-02-29T03:50:07Z") 1649 self.assertEqual(snapshots[0].extra['name'], 'snap-001') 1650 self.assertEqual(snapshots[0].name, 'snap-001') 1651 self.assertEqual(snapshots[0].state, VolumeSnapshotState.AVAILABLE) 1652 1653 # invalid date is parsed as None 1654 assert snapshots[2].created is None 1655 1656 def test_ex_get_snapshot(self): 1657 if self.driver_type.type == 'rackspace': 1658 self.conn_class.type = 'RACKSPACE' 1659 1660 snapshot = self.driver.ex_get_snapshot('3fbbcccf-d058-4502-8844-6feeffdf4cb5') 1661 self.assertEqual(snapshot.created, datetime.datetime(2012, 2, 29, 3, 50, 7, tzinfo=UTC)) 1662 self.assertEqual(snapshot.extra['created'], "2012-02-29T03:50:07Z") 1663 self.assertEqual(snapshot.extra['name'], 'snap-001') 1664 self.assertEqual(snapshot.name, 'snap-001') 1665 self.assertEqual(snapshot.state, VolumeSnapshotState.AVAILABLE) 1666 1667 def test_list_volume_snapshots(self): 1668 volume = self.driver.list_volumes()[0] 1669 1670 # rackspace needs a different mocked response for snapshots, but not for volumes 1671 if self.driver_type.type == 'rackspace': 1672 self.conn_class.type = 'RACKSPACE' 1673 1674 snapshots = self.driver.list_volume_snapshots(volume) 1675 self.assertEqual(len(snapshots), 1) 1676 self.assertEqual(snapshots[0].id, '4fbbdccf-e058-6502-8844-6feeffdf4cb5') 1677 1678 def test_create_volume_snapshot(self): 1679 volume = self.driver.list_volumes()[0] 1680 if self.driver_type.type == 'rackspace': 1681 self.conn_class.type = 'RACKSPACE' 1682 1683 ret = self.driver.create_volume_snapshot(volume, 1684 'Test Volume', 1685 ex_description='This is a test', 1686 ex_force=True) 1687 self.assertEqual(ret.id, '3fbbcccf-d058-4502-8844-6feeffdf4cb5') 1688 1689 def test_ex_create_snapshot(self): 1690 volume = self.driver.list_volumes()[0] 1691 if self.driver_type.type == 'rackspace': 1692 self.conn_class.type = 'RACKSPACE' 1693 1694 ret = self.driver.ex_create_snapshot(volume, 1695 'Test Volume', 1696 description='This is a test', 1697 force=True) 1698 self.assertEqual(ret.id, '3fbbcccf-d058-4502-8844-6feeffdf4cb5') 1699 1700 def test_ex_create_snapshot_does_not_post_optional_parameters_if_none(self): 1701 volume = self.driver.list_volumes()[0] 1702 with patch.object(self.driver, '_to_snapshot'): 1703 with patch.object(self.driver.connection, 'request') as mock_request: 1704 self.driver.create_volume_snapshot(volume, 1705 name=None, 1706 ex_description=None, 1707 ex_force=True) 1708 1709 name, args, kwargs = mock_request.mock_calls[0] 1710 self.assertFalse("display_name" in kwargs["data"]["snapshot"]) 1711 self.assertFalse("display_description" in kwargs["data"]["snapshot"]) 1712 1713 def test_destroy_volume_snapshot(self): 1714 if self.driver_type.type == 'rackspace': 1715 self.conn_class.type = 'RACKSPACE' 1716 1717 snapshot = self.driver.ex_list_snapshots()[0] 1718 ret = self.driver.destroy_volume_snapshot(snapshot) 1719 self.assertTrue(ret) 1720 1721 def test_ex_delete_snapshot(self): 1722 if self.driver_type.type == 'rackspace': 1723 self.conn_class.type = 'RACKSPACE' 1724 1725 snapshot = self.driver.ex_list_snapshots()[0] 1726 ret = self.driver.ex_delete_snapshot(snapshot) 1727 self.assertTrue(ret) 1728 1729 1730class OpenStack_2_Tests(OpenStack_1_1_Tests): 1731 driver_klass = OpenStack_2_NodeDriver 1732 driver_type = OpenStack_2_NodeDriver 1733 driver_kwargs = { 1734 'ex_force_auth_version': '2.0', 1735 'ex_force_auth_url': 'https://auth.api.example.com' 1736 } 1737 1738 def setUp(self): 1739 super(OpenStack_2_Tests, self).setUp() 1740 self.driver_klass.image_connectionCls.conn_class = OpenStack_2_0_MockHttp 1741 self.driver_klass.image_connectionCls.auth_url = "https://auth.api.example.com" 1742 # normally authentication happens lazily, but we force it here 1743 self.driver.image_connection._populate_hosts_and_request_paths() 1744 1745 self.driver_klass.network_connectionCls.conn_class = OpenStack_2_0_MockHttp 1746 self.driver_klass.network_connectionCls.auth_url = "https://auth.api.example.com" 1747 # normally authentication happens lazily, but we force it here 1748 self.driver.network_connection._populate_hosts_and_request_paths() 1749 1750 self.driver_klass.volumev2_connectionCls.conn_class = OpenStack_2_0_MockHttp 1751 self.driver_klass.volumev2_connectionCls.auth_url = "https://auth.api.example.com" 1752 # normally authentication happens lazily, but we force it here 1753 self.driver.volumev2_connection._populate_hosts_and_request_paths() 1754 1755 self.driver_klass.volumev3_connectionCls.conn_class = OpenStack_2_0_MockHttp 1756 self.driver_klass.volumev3_connectionCls.auth_url = "https://auth.api.example.com" 1757 # normally authentication happens lazily, but we force it here 1758 self.driver.volumev3_connection._populate_hosts_and_request_paths() 1759 1760 def test__paginated_request_single_page(self): 1761 snapshots = self.driver._paginated_request( 1762 '/snapshots/detail', 'snapshots', 1763 self.driver._get_volume_connection() 1764 )['snapshots'] 1765 1766 self.assertEqual(len(snapshots), 3) 1767 self.assertEqual(snapshots[0]['name'], 'snap-001') 1768 1769 def test__paginated_request_two_pages(self): 1770 snapshots = self.driver._paginated_request( 1771 '/snapshots/detail?unit_test=paginate', 'snapshots', 1772 self.driver._get_volume_connection() 1773 )['snapshots'] 1774 1775 self.assertEqual(len(snapshots), 6) 1776 self.assertEqual(snapshots[0]['name'], 'snap-101') 1777 self.assertEqual(snapshots[3]['name'], 'snap-001') 1778 1779 def test_list_images_with_pagination_invalid_response_no_infinite_loop(self): 1780 # "next" attribute matches the current page, but it shouldn't result in 1781 # an infite loop 1782 OpenStack_2_0_MockHttp.type = 'invalid_next' 1783 ret = self.driver.list_images() 1784 self.assertEqual(len(ret), 2) 1785 1786 # NOTE: We use a smaller limit to speed tests up. 1787 @mock.patch('libcloud.compute.drivers.openstack.PAGINATION_LIMIT', 10) 1788 def test__paginated_request_raises_if_stuck_in_a_loop(self): 1789 with pytest.raises(OpenStackException): 1790 self.driver._paginated_request( 1791 '/snapshots/detail?unit_test=pagination_loop', 'snapshots', 1792 self.driver._get_volume_connection() 1793 ) 1794 1795 def test_ex_force_auth_token_passed_to_connection(self): 1796 base_url = 'https://servers.api.rackspacecloud.com/v1.1/slug' 1797 kwargs = { 1798 'ex_force_auth_version': '2.0', 1799 'ex_force_auth_token': 'preset-auth-token', 1800 'ex_force_auth_url': 'https://auth.api.example.com', 1801 'ex_force_base_url': base_url 1802 } 1803 1804 driver = self.driver_type(*self.driver_args, **kwargs) 1805 driver.list_nodes() 1806 1807 self.assertEqual(kwargs['ex_force_auth_token'], 1808 driver.connection.auth_token) 1809 self.assertEqual('servers.api.rackspacecloud.com', 1810 driver.connection.host) 1811 self.assertEqual('/v1.1/slug', driver.connection.request_path) 1812 self.assertEqual(443, driver.connection.port) 1813 1814 def test_get_image(self): 1815 image_id = 'f24a3c1b-d52a-4116-91da-25b3eee8f55e' 1816 image = self.driver.get_image(image_id) 1817 self.assertEqual(image.id, image_id) 1818 self.assertEqual(image.name, 'hypernode') 1819 self.assertIsNone(image.extra['serverId']) 1820 self.assertEqual(image.extra['minDisk'], 40) 1821 self.assertEqual(image.extra['minRam'], 0) 1822 self.assertEqual(image.extra['visibility'], "shared") 1823 1824 def test_list_images(self): 1825 images = self.driver.list_images() 1826 self.assertEqual(len(images), 3, 'Wrong images count') 1827 1828 image = images[0] 1829 self.assertEqual(image.id, 'f24a3c1b-d52a-4116-91da-25b3eee8f55e') 1830 self.assertEqual(image.name, 'hypernode') 1831 self.assertEqual(image.extra['updated'], '2017-11-28T10:19:49Z') 1832 self.assertEqual(image.extra['created'], '2017-09-11T13:00:05Z') 1833 self.assertEqual(image.extra['status'], 'active') 1834 self.assertEqual(image.extra['os_type'], 'linux') 1835 self.assertIsNone(image.extra['serverId']) 1836 self.assertEqual(image.extra['minDisk'], 40) 1837 self.assertEqual(image.extra['minRam'], 0) 1838 1839 def test_ex_update_image(self): 1840 image_id = 'f24a3c1b-d52a-4116-91da-25b3eee8f55e' 1841 data = { 1842 'op': 'replace', 1843 'path': '/visibility', 1844 'value': 'shared' 1845 } 1846 image = self.driver.ex_update_image(image_id, data) 1847 self.assertEqual(image.name, 'hypernode') 1848 self.assertIsNone(image.extra['serverId']) 1849 self.assertEqual(image.extra['minDisk'], 40) 1850 self.assertEqual(image.extra['minRam'], 0) 1851 self.assertEqual(image.extra['visibility'], "shared") 1852 1853 def test_ex_list_image_members(self): 1854 image_id = 'd9a9cd9a-278a-444c-90a6-d24b8c688a63' 1855 image_member_id = '016926dff12345e8b10329f24c99745b' 1856 image_members = self.driver.ex_list_image_members(image_id) 1857 self.assertEqual(len(image_members), 30, 'Wrong image member count') 1858 1859 image_member = image_members[0] 1860 self.assertEqual(image_member.id, image_member_id) 1861 self.assertEqual(image_member.image_id, image_id) 1862 self.assertEqual(image_member.state, NodeImageMemberState.ACCEPTED) 1863 self.assertEqual(image_member.created, '2017-01-12T12:31:50Z') 1864 self.assertEqual(image_member.extra['updated'], '2017-01-12T12:31:54Z') 1865 self.assertEqual(image_member.extra['schema'], '/v2/schemas/member') 1866 1867 def test_ex_create_image_member(self): 1868 image_id = '9af1a54e-a1b2-4df8-b747-4bec97abc799' 1869 image_member_id = 'e2151b1fe02d4a8a2d1f5fc331522c0a' 1870 image_member = self.driver.ex_create_image_member( 1871 image_id, image_member_id 1872 ) 1873 1874 self.assertEqual(image_member.id, image_member_id) 1875 self.assertEqual(image_member.image_id, image_id) 1876 self.assertEqual(image_member.state, NodeImageMemberState.PENDING) 1877 self.assertEqual(image_member.created, '2018-03-02T14:19:38Z') 1878 self.assertEqual(image_member.extra['updated'], '2018-03-02T14:19:38Z') 1879 self.assertEqual(image_member.extra['schema'], '/v2/schemas/member') 1880 1881 def test_ex_get_image_member(self): 1882 image_id = 'd9a9cd9a-278a-444c-90a6-d24b8c688a63' 1883 image_member_id = '016926dff12345e8b10329f24c99745b' 1884 image_member = self.driver.ex_get_image_member( 1885 image_id, image_member_id 1886 ) 1887 1888 self.assertEqual(image_member.id, image_member_id) 1889 self.assertEqual(image_member.image_id, image_id) 1890 self.assertEqual(image_member.state, NodeImageMemberState.ACCEPTED) 1891 self.assertEqual(image_member.created, '2017-01-12T12:31:50Z') 1892 self.assertEqual(image_member.extra['updated'], '2017-01-12T12:31:54Z') 1893 self.assertEqual(image_member.extra['schema'], '/v2/schemas/member') 1894 1895 def test_ex_accept_image_member(self): 1896 image_id = '8af1a54e-a1b2-4df8-b747-4bec97abc799' 1897 image_member_id = 'e2151b1fe02d4a8a2d1f5fc331522c0a' 1898 image_member = self.driver.ex_accept_image_member( 1899 image_id, image_member_id 1900 ) 1901 1902 self.assertEqual(image_member.id, image_member_id) 1903 self.assertEqual(image_member.image_id, image_id) 1904 self.assertEqual(image_member.state, NodeImageMemberState.ACCEPTED) 1905 self.assertEqual(image_member.created, '2018-03-02T14:19:38Z') 1906 self.assertEqual(image_member.extra['updated'], '2018-03-02T14:20:37Z') 1907 self.assertEqual(image_member.extra['schema'], '/v2/schemas/member') 1908 1909 def test_ex_list_networks(self): 1910 networks = self.driver.ex_list_networks() 1911 network = networks[0] 1912 1913 self.assertEqual(len(networks), 2) 1914 self.assertEqual(network.name, 'net1') 1915 self.assertEqual(network.extra['subnets'], ['54d6f61d-db07-451c-9ab3-b9609b6b6f0b']) 1916 1917 def test_ex_get_network(self): 1918 network = self.driver.ex_get_network("cc2dad14-827a-feea-416b-f13e50511a0a") 1919 1920 self.assertEqual(network.id, "cc2dad14-827a-feea-416b-f13e50511a0a") 1921 self.assertTrue(isinstance(network, OpenStackNetwork)) 1922 self.assertEqual(network.name, 'net2') 1923 1924 def test_ex_list_subnets(self): 1925 subnets = self.driver.ex_list_subnets() 1926 subnet = subnets[0] 1927 1928 self.assertEqual(len(subnets), 2) 1929 self.assertEqual(subnet.name, 'private-subnet') 1930 self.assertEqual(subnet.cidr, '10.0.0.0/24') 1931 1932 def test_ex_create_subnet(self): 1933 network = self.driver.ex_list_networks()[0] 1934 subnet = self.driver.ex_create_subnet('name', network, '10.0.0.0/24', 1935 ip_version=4, 1936 dns_nameservers=["10.0.0.01"]) 1937 1938 self.assertEqual(subnet.name, 'name') 1939 self.assertEqual(subnet.cidr, '10.0.0.0/24') 1940 1941 def test_ex_delete_subnet(self): 1942 subnet = self.driver.ex_list_subnets()[0] 1943 self.assertTrue(self.driver.ex_delete_subnet(subnet=subnet)) 1944 1945 def test_ex_update_subnet(self): 1946 subnet = self.driver.ex_list_subnets()[0] 1947 subnet = self.driver.ex_update_subnet(subnet, name='net2') 1948 self.assertEqual(subnet.name, 'name') 1949 1950 def test_ex_list_network(self): 1951 networks = self.driver.ex_list_networks() 1952 network = networks[0] 1953 1954 self.assertEqual(len(networks), 2) 1955 self.assertEqual(network.name, 'net1') 1956 1957 def test_ex_create_network(self): 1958 network = self.driver.ex_create_network(name='net1', 1959 cidr='127.0.0.0/24') 1960 self.assertEqual(network.name, 'net1') 1961 1962 def test_ex_delete_network(self): 1963 network = self.driver.ex_list_networks()[0] 1964 self.assertTrue(self.driver.ex_delete_network(network=network)) 1965 1966 def test_ex_list_ports(self): 1967 ports = self.driver.ex_list_ports() 1968 1969 port = ports[0] 1970 self.assertEqual(port.id, '126da55e-cfcb-41c8-ae39-a26cb8a7e723') 1971 self.assertEqual(port.state, OpenStack_2_PortInterfaceState.BUILD) 1972 self.assertEqual(port.created, '2018-07-04T14:38:18Z') 1973 self.assertEqual( 1974 port.extra['network_id'], 1975 '123c8a8c-6427-4e8f-a805-2035365f4d43' 1976 ) 1977 self.assertEqual( 1978 port.extra['project_id'], 1979 'abcdec85bee34bb0a44ab8255eb36abc' 1980 ) 1981 self.assertEqual( 1982 port.extra['tenant_id'], 1983 'abcdec85bee34bb0a44ab8255eb36abc' 1984 ) 1985 self.assertEqual(port.extra['name'], '') 1986 1987 def test_ex_create_port(self): 1988 network = OpenStackNetwork(id='123c8a8c-6427-4e8f-a805-2035365f4d43', name='test-network', 1989 cidr='1.2.3.4', driver=self.driver) 1990 port = self.driver.ex_create_port(network=network, description='Some port description', name='Some port name', 1991 admin_state_up=True) 1992 1993 self.assertEqual(port.id, '126da55e-cfcb-41c8-ae39-a26cb8a7e723') 1994 self.assertEqual(port.state, OpenStack_2_PortInterfaceState.BUILD) 1995 self.assertEqual(port.created, '2018-07-04T14:38:18Z') 1996 self.assertEqual( 1997 port.extra['network_id'], 1998 '123c8a8c-6427-4e8f-a805-2035365f4d43' 1999 ) 2000 self.assertEqual( 2001 port.extra['project_id'], 2002 'abcdec85bee34bb0a44ab8255eb36abc' 2003 ) 2004 self.assertEqual( 2005 port.extra['tenant_id'], 2006 'abcdec85bee34bb0a44ab8255eb36abc' 2007 ) 2008 self.assertEqual(port.extra['admin_state_up'], True) 2009 self.assertEqual(port.extra['name'], 'Some port name') 2010 self.assertEqual(port.extra['description'], 'Some port description') 2011 2012 def test_ex_get_port(self): 2013 port = self.driver.ex_get_port('126da55e-cfcb-41c8-ae39-a26cb8a7e723') 2014 2015 self.assertEqual(port.id, '126da55e-cfcb-41c8-ae39-a26cb8a7e723') 2016 self.assertEqual(port.state, OpenStack_2_PortInterfaceState.BUILD) 2017 self.assertEqual(port.created, '2018-07-04T14:38:18Z') 2018 self.assertEqual( 2019 port.extra['network_id'], 2020 '123c8a8c-6427-4e8f-a805-2035365f4d43' 2021 ) 2022 self.assertEqual( 2023 port.extra['project_id'], 2024 'abcdec85bee34bb0a44ab8255eb36abc' 2025 ) 2026 self.assertEqual( 2027 port.extra['tenant_id'], 2028 'abcdec85bee34bb0a44ab8255eb36abc' 2029 ) 2030 self.assertEqual(port.extra['name'], 'Some port name') 2031 2032 def test_ex_delete_port(self): 2033 ports = self.driver.ex_list_ports() 2034 port = ports[0] 2035 2036 ret = self.driver.ex_delete_port(port) 2037 2038 self.assertTrue(ret) 2039 2040 def test_ex_update_port(self): 2041 port = self.driver.ex_get_port('126da55e-cfcb-41c8-ae39-a26cb8a7e723') 2042 ret = self.driver.ex_update_port(port, port_security_enabled=False) 2043 self.assertEqual(ret.extra['name'], 'Some port name') 2044 2045 def test_ex_update_port_allowed_address_pairs(self): 2046 allowed_address_pairs = [{'ip_address': '1.2.3.4'}, 2047 {'ip_address': '2.3.4.5'}] 2048 port = self.driver.ex_get_port('126da55e-cfcb-41c8-ae39-a26cb8a7e723') 2049 ret = self.driver.ex_update_port( 2050 port, allowed_address_pairs=allowed_address_pairs) 2051 self.assertEqual(ret.extra['allowed_address_pairs'], allowed_address_pairs) 2052 2053 def test_detach_port_interface(self): 2054 node = Node(id='1c01300f-ef97-4937-8f03-ac676d6234be', name=None, 2055 state=None, public_ips=None, private_ips=None, 2056 driver=self.driver) 2057 ports = self.driver.ex_list_ports() 2058 port = ports[0] 2059 2060 ret = self.driver.ex_detach_port_interface(node, port) 2061 2062 self.assertTrue(ret) 2063 2064 def test_attach_port_interface(self): 2065 node = Node(id='1c01300f-ef97-4937-8f03-ac676d6234be', name=None, 2066 state=None, public_ips=None, private_ips=None, 2067 driver=self.driver) 2068 ports = self.driver.ex_list_ports() 2069 port = ports[0] 2070 2071 ret = self.driver.ex_attach_port_interface(node, port) 2072 2073 self.assertTrue(ret) 2074 2075 def test_list_volumes(self): 2076 volumes = self.driver.list_volumes() 2077 self.assertEqual(len(volumes), 2) 2078 volume = volumes[0] 2079 2080 self.assertEqual('6edbc2f4-1507-44f8-ac0d-eed1d2608d38', volume.id) 2081 self.assertEqual('test-volume-attachments', volume.name) 2082 self.assertEqual(StorageVolumeState.INUSE, volume.state) 2083 self.assertEqual(2, volume.size) 2084 self.assertEqual(volume.extra, { 2085 'description': '', 2086 'attachments': [{ 2087 "attachment_id": "3b4db356-253d-4fab-bfa0-e3626c0b8405", 2088 "id": '6edbc2f4-1507-44f8-ac0d-eed1d2608d38', 2089 "device": "/dev/vdb", 2090 "server_id": "f4fda93b-06e0-4743-8117-bc8bcecd651b", 2091 "volume_id": "6edbc2f4-1507-44f8-ac0d-eed1d2608d38", 2092 }], 2093 'snapshot_id': None, 2094 'state': 'in-use', 2095 'location': 'nova', 2096 'volume_type': 'lvmdriver-1', 2097 'metadata': {}, 2098 'created_at': '2013-06-24T11:20:13.000000' 2099 }) 2100 2101 # also test that unknown state resolves to StorageVolumeState.UNKNOWN 2102 volume = volumes[1] 2103 self.assertEqual('cfcec3bc-b736-4db5-9535-4c24112691b5', volume.id) 2104 self.assertEqual('test_volume', volume.name) 2105 self.assertEqual(50, volume.size) 2106 self.assertEqual(StorageVolumeState.UNKNOWN, volume.state) 2107 self.assertEqual(volume.extra, { 2108 'description': 'some description', 2109 'attachments': [], 2110 'snapshot_id': '01f48111-7866-4cd2-986a-e92683c4a363', 2111 'state': 'some-unknown-state', 2112 'location': 'nova', 2113 'volume_type': None, 2114 'metadata': {}, 2115 'created_at': '2013-06-21T12:39:02.000000', 2116 }) 2117 2118 def test_create_volume_passes_location_to_request_only_if_not_none(self): 2119 with patch.object(self.driver._get_volume_connection(), 'request') as mock_request: 2120 self.driver.create_volume(1, 'test', location='mylocation') 2121 name, args, kwargs = mock_request.mock_calls[0] 2122 self.assertEqual(kwargs["data"]["volume"]["availability_zone"], "mylocation") 2123 2124 def test_create_volume_does_not_pass_location_to_request_if_none(self): 2125 with patch.object(self.driver._get_volume_connection(), 'request') as mock_request: 2126 self.driver.create_volume(1, 'test') 2127 name, args, kwargs = mock_request.mock_calls[0] 2128 self.assertFalse("availability_zone" in kwargs["data"]["volume"]) 2129 2130 def test_create_volume_passes_volume_type_to_request_only_if_not_none(self): 2131 with patch.object(self.driver._get_volume_connection(), 'request') as mock_request: 2132 self.driver.create_volume(1, 'test', ex_volume_type='myvolumetype') 2133 name, args, kwargs = mock_request.mock_calls[0] 2134 self.assertEqual(kwargs["data"]["volume"]["volume_type"], "myvolumetype") 2135 2136 def test_create_volume_does_not_pass_volume_type_to_request_if_none(self): 2137 with patch.object(self.driver._get_volume_connection(), 'request') as mock_request: 2138 self.driver.create_volume(1, 'test') 2139 name, args, kwargs = mock_request.mock_calls[0] 2140 self.assertFalse("volume_type" in kwargs["data"]["volume"]) 2141 2142 def test_create_volume_passes_image_ref_to_request_only_if_not_none(self): 2143 with patch.object(self.driver._get_volume_connection(), 'request') as mock_request: 2144 self.driver.create_volume( 2145 1, 'test', ex_image_ref='353c4bd2-b28f-4857-9b7b-808db4397d03') 2146 name, args, kwargs = mock_request.mock_calls[0] 2147 self.assertEqual( 2148 kwargs["data"]["volume"]["imageRef"], 2149 "353c4bd2-b28f-4857-9b7b-808db4397d03") 2150 2151 def test_create_volume_does_not_pass_image_ref_to_request_if_none(self): 2152 with patch.object(self.driver._get_volume_connection(), 'request') as mock_request: 2153 self.driver.create_volume(1, 'test') 2154 name, args, kwargs = mock_request.mock_calls[0] 2155 self.assertFalse("imageRef" in kwargs["data"]["volume"]) 2156 2157 def test_ex_create_snapshot_does_not_post_optional_parameters_if_none(self): 2158 volume = self.driver.list_volumes()[0] 2159 with patch.object(self.driver, '_to_snapshot'): 2160 with patch.object(self.driver._get_volume_connection(), 'request') as mock_request: 2161 self.driver.create_volume_snapshot(volume, 2162 name=None, 2163 ex_description=None, 2164 ex_force=True) 2165 2166 name, args, kwargs = mock_request.mock_calls[0] 2167 self.assertFalse("display_name" in kwargs["data"]["snapshot"]) 2168 self.assertFalse("display_description" in kwargs["data"]["snapshot"]) 2169 2170 def test_ex_list_routers(self): 2171 routers = self.driver.ex_list_routers() 2172 router = routers[0] 2173 2174 self.assertEqual(len(routers), 2) 2175 self.assertEqual(router.name, 'router2') 2176 self.assertEqual(router.status, 'ACTIVE') 2177 self.assertEqual(router.extra['routes'], [{'destination': '179.24.1.0/24', 2178 'nexthop': '172.24.3.99'}]) 2179 2180 def test_ex_create_router(self): 2181 router = self.driver.ex_create_router('router1', admin_state_up = True) 2182 2183 self.assertEqual(router.name, 'router1') 2184 2185 def test_ex_delete_router(self): 2186 router = self.driver.ex_list_routers()[1] 2187 self.assertTrue(self.driver.ex_delete_router(router=router)) 2188 2189 def test_manage_router_interfaces(self): 2190 router = self.driver.ex_list_routers()[1] 2191 port = self.driver.ex_list_ports()[0] 2192 subnet = self.driver.ex_list_subnets()[0] 2193 self.assertTrue(self.driver.ex_add_router_port(router, port)) 2194 self.assertTrue(self.driver.ex_del_router_port(router, port)) 2195 self.assertTrue(self.driver.ex_add_router_subnet(router, subnet)) 2196 self.assertTrue(self.driver.ex_del_router_subnet(router, subnet)) 2197 2198 def test_detach_volume(self): 2199 node = self.driver.list_nodes()[0] 2200 volume = self.driver.ex_get_volume( 2201 'abc6a3a1-c4ce-40f6-9b9f-07a61508938d') 2202 self.assertEqual( 2203 self.driver.attach_volume(node, volume, '/dev/sdb'), True) 2204 self.assertEqual(self.driver.detach_volume(volume), True) 2205 2206 def test_ex_remove_security_group_from_node(self): 2207 security_group = OpenStackSecurityGroup("sgid", None, "sgname", "", self.driver) 2208 node = Node("1000", "node", None, [], [], self.driver) 2209 ret = self.driver.ex_remove_security_group_from_node(security_group, node) 2210 self.assertTrue(ret) 2211 2212 def test_force_net_url(self): 2213 d = OpenStack_2_NodeDriver( 2214 'user', 'correct_password', 2215 ex_force_auth_version='2.0_password', 2216 ex_force_auth_url='http://x.y.z.y:5000', 2217 ex_force_network_url='http://network.com:9696', 2218 ex_tenant_name='admin') 2219 self.assertEqual(d._ex_force_base_url, None) 2220 2221 def test_ex_get_quota_set(self): 2222 quota_set = self.driver.ex_get_quota_set("tenant_id") 2223 self.assertEqual(quota_set.cores.limit, 20) 2224 self.assertEqual(quota_set.cores.in_use, 1) 2225 self.assertEqual(quota_set.cores.reserved, 0) 2226 2227 def test_ex_get_network_quota(self): 2228 quota_set = self.driver.ex_get_network_quotas("tenant_id") 2229 self.assertEqual(quota_set.floatingip.limit, 2) 2230 self.assertEqual(quota_set.floatingip.in_use, 1) 2231 self.assertEqual(quota_set.floatingip.reserved, 0) 2232 2233 def test_ex_get_volume_quota(self): 2234 quota_set = self.driver.ex_get_volume_quotas("tenant_id") 2235 self.assertEqual(quota_set.gigabytes.limit, 1000) 2236 self.assertEqual(quota_set.gigabytes.in_use, 10) 2237 self.assertEqual(quota_set.gigabytes.reserved, 0) 2238 2239class OpenStack_1_1_FactoryMethodTests(OpenStack_1_1_Tests): 2240 should_list_locations = False 2241 should_list_volumes = True 2242 2243 driver_klass = OpenStack_1_1_NodeDriver 2244 driver_type = get_driver(Provider.OPENSTACK) 2245 driver_args = OPENSTACK_PARAMS + ('1.1',) 2246 driver_kwargs = {'ex_force_auth_version': '2.0'} 2247 2248 2249class OpenStack_1_1_MockHttp(MockHttp, unittest.TestCase): 2250 fixtures = ComputeFileFixtures('openstack_v1.1') 2251 auth_fixtures = OpenStackFixtures() 2252 json_content_headers = {'content-type': 'application/json; charset=UTF-8'} 2253 2254 def _v2_0_tokens(self, method, url, body, headers): 2255 body = self.auth_fixtures.load('_v2_0__auth.json') 2256 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2257 2258 def _v1_0(self, method, url, body, headers): 2259 headers = { 2260 'x-auth-token': 'FE011C19-CF86-4F87-BE5D-9229145D7A06', 2261 'x-server-management-url': 'https://api.example.com/v1.1/slug', 2262 } 2263 return (httplib.NO_CONTENT, "", headers, httplib.responses[httplib.NO_CONTENT]) 2264 2265 def _v1_1_slug_servers_detail(self, method, url, body, headers): 2266 body = self.fixtures.load('_servers_detail.json') 2267 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2268 2269 def _v1_1_slug_servers_detail_ERROR_STATE_NO_IMAGE_ID(self, method, url, body, headers): 2270 body = self.fixtures.load('_servers_detail_ERROR_STATE.json') 2271 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2272 2273 def _v2_1337_servers_detail_UNAUTHORIZED(self, method, url, body, headers): 2274 return (httplib.UNAUTHORIZED, "", {}, httplib.responses[httplib.UNAUTHORIZED]) 2275 2276 def _v2_1337_servers_does_not_exist(self, *args, **kwargs): 2277 return httplib.NOT_FOUND, None, {}, httplib.responses[httplib.NOT_FOUND] 2278 2279 def _v1_1_slug_flavors_detail(self, method, url, body, headers): 2280 body = self.fixtures.load('_flavors_detail.json') 2281 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2282 2283 def _v1_1_slug_images_detail(self, method, url, body, headers): 2284 body = self.fixtures.load('_images_detail.json') 2285 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2286 2287 def _v1_1_slug_servers(self, method, url, body, headers): 2288 if method == "POST": 2289 body = self.fixtures.load('_servers_create.json') 2290 elif method == "GET": 2291 body = self.fixtures.load('_servers.json') 2292 else: 2293 raise NotImplementedError() 2294 2295 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2296 2297 def _v1_1_slug_servers_26f7fbee_8ce1_4c28_887a_bfe8e4bb10fe(self, method, url, body, headers): 2298 if method == "GET": 2299 body = self.fixtures.load( 2300 '_servers_26f7fbee_8ce1_4c28_887a_bfe8e4bb10fe.json') 2301 else: 2302 raise NotImplementedError() 2303 2304 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2305 2306 def _v1_1_slug_servers_12065_action(self, method, url, body, headers): 2307 if method != "POST": 2308 self.fail('HTTP method other than POST to action URL') 2309 2310 return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) 2311 2312 def _v1_1_slug_servers_12064_action(self, method, url, body, headers): 2313 if method != "POST": 2314 self.fail('HTTP method other than POST to action URL') 2315 if "createImage" in json.loads(body): 2316 return (httplib.ACCEPTED, "", 2317 {"location": "http://127.0.0.1/v1.1/68/images/4949f9ee-2421-4c81-8b49-13119446008b"}, 2318 httplib.responses[httplib.ACCEPTED]) 2319 elif "rescue" in json.loads(body): 2320 return (httplib.OK, '{"adminPass": "foo"}', {}, 2321 httplib.responses[httplib.OK]) 2322 2323 return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) 2324 2325 def _v1_1_slug_servers_12066_action(self, method, url, body, headers): 2326 if method != "POST": 2327 self.fail('HTTP method other than POST to action URL') 2328 if "rebuild" not in json.loads(body): 2329 self.fail("Did not get expected action (rebuild) in action URL") 2330 2331 self.assertTrue('\"OS-DCF:diskConfig\": \"MANUAL\"' in body, 2332 msg="Manual disk configuration option was not specified in rebuild body: " + body) 2333 2334 return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) 2335 2336 def _v1_1_slug_servers_12065(self, method, url, body, headers): 2337 if method == "DELETE": 2338 return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) 2339 else: 2340 raise NotImplementedError() 2341 2342 def _v1_1_slug_servers_12064(self, method, url, body, headers): 2343 if method == "GET": 2344 body = self.fixtures.load('_servers_12064.json') 2345 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2346 elif method == "PUT": 2347 body = self.fixtures.load('_servers_12064_updated_name_bob.json') 2348 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2349 elif method == "DELETE": 2350 return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) 2351 else: 2352 raise NotImplementedError() 2353 2354 def _v1_1_slug_servers_12062(self, method, url, body, headers): 2355 if method == "GET": 2356 body = self.fixtures.load('_servers_12064.json') 2357 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2358 2359 def _v1_1_slug_servers_12063_metadata(self, method, url, body, headers): 2360 if method == "GET": 2361 body = self.fixtures.load('_servers_12063_metadata_two_keys.json') 2362 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2363 elif method == "PUT": 2364 body = self.fixtures.load('_servers_12063_metadata_two_keys.json') 2365 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2366 2367 def _v1_1_slug_servers_EX_DISK_CONFIG(self, method, url, body, headers): 2368 if method == "POST": 2369 body = u(body) 2370 self.assertTrue(body.find('\"OS-DCF:diskConfig\": \"AUTO\"')) 2371 body = self.fixtures.load('_servers_create_disk_config.json') 2372 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2373 2374 def _v1_1_slug_flavors_7(self, method, url, body, headers): 2375 if method == "GET": 2376 body = self.fixtures.load('_flavors_7.json') 2377 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2378 else: 2379 raise NotImplementedError() 2380 2381 def _v1_1_slug_images_13(self, method, url, body, headers): 2382 if method == "GET": 2383 body = self.fixtures.load('_images_13.json') 2384 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2385 else: 2386 raise NotImplementedError() 2387 2388 def _v2_1337_v2_images_f24a3c1b_d52a_4116_91da_25b3eee8f55e(self, method, url, body, headers): 2389 if method == "GET" or method == "PATCH": 2390 body = self.fixtures.load('_images_f24a3c1b-d52a-4116-91da-25b3eee8f55e.json') 2391 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2392 else: 2393 raise NotImplementedError() 2394 2395 def _v2_1337_v2_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members(self, method, url, body, headers): 2396 if method == "GET": 2397 body = self.fixtures.load('_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members.json') 2398 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2399 else: 2400 raise NotImplementedError() 2401 2402 def _v2_1337_v2_images_9af1a54e_a1b2_4df8_b747_4bec97abc799_members(self, method, url, body, headers): 2403 if method == "POST": 2404 body = self.fixtures.load('_images_9af1a54e_a1b2_4df8_b747_4bec97abc799_members.json') 2405 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2406 else: 2407 raise NotImplementedError() 2408 2409 def _v2_1337_v2_images_8af1a54e_a1b2_4df8_b747_4bec97abc799_members_e2151b1fe02d4a8a2d1f5fc331522c0a(self, method, url, body, headers): 2410 if method == "PUT": 2411 body = self.fixtures.load('_images_8af1a54e_a1b2_4df8_b747_4bec97abc799_members.json') 2412 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2413 else: 2414 raise NotImplementedError() 2415 2416 def _v2_1337_v2_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members_016926dff12345e8b10329f24c99745b(self, method, url, body, headers): 2417 if method == "GET": 2418 body = self.fixtures.load( 2419 '_images_d9a9cd9a_278a_444c_90a6_d24b8c688a63_members_016926dff12345e8b10329f24c99745b.json' 2420 ) 2421 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2422 else: 2423 raise NotImplementedError() 2424 2425 def _v2_1337_v2_images(self, method, url, body, headers): 2426 if method == "GET": 2427 # 2nd (and last) page of images 2428 if 'marker=e7a40226-3523-4f0f-87d8-d8dc91bbf4a3' in url: 2429 body = self.fixtures.load('_images_v2_page2.json') 2430 else: 2431 # first page of images 2432 body = self.fixtures.load('_images_v2.json') 2433 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2434 else: 2435 raise NotImplementedError() 2436 2437 def _v2_1337_v2_images_invalid_next(self, method, url, body, headers): 2438 if method == "GET": 2439 body = self.fixtures.load('_images_v2_invalid_next.json') 2440 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2441 else: 2442 raise NotImplementedError() 2443 2444 def _v1_1_slug_images_26365521_8c62_11f9_2c33_283d153ecc3a(self, method, url, body, headers): 2445 if method == "DELETE": 2446 return (httplib.NO_CONTENT, "", {}, httplib.responses[httplib.NO_CONTENT]) 2447 else: 2448 raise NotImplementedError() 2449 2450 def _v1_1_slug_images_4949f9ee_2421_4c81_8b49_13119446008b(self, method, url, body, headers): 2451 if method == "GET": 2452 body = self.fixtures.load( 2453 '_images_4949f9ee_2421_4c81_8b49_13119446008b.json') 2454 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2455 else: 2456 raise NotImplementedError() 2457 2458 def _v2_1337_v2_images_4949f9ee_2421_4c81_8b49_13119446008b(self, method, url, body, headers): 2459 if method == "GET": 2460 body = self.fixtures.load( 2461 '_images_f24a3c1b-d52a-4116-91da-25b3eee8f55d.json') 2462 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2463 else: 2464 raise NotImplementedError() 2465 2466 def _v2_1337_v2_0_ports(self, method, url, body, headers): 2467 if method == "GET": 2468 body = self.fixtures.load('_ports_v2.json') 2469 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2470 elif method == "POST": 2471 body = self.fixtures.load('_port_v2.json') 2472 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2473 else: 2474 raise NotImplementedError() 2475 2476 def _v2_1337_v2_0_ports_126da55e_cfcb_41c8_ae39_a26cb8a7e723(self, method, url, body, headers): 2477 if method == "DELETE": 2478 return (httplib.NO_CONTENT, "", {}, httplib.responses[httplib.NO_CONTENT]) 2479 elif method == "GET": 2480 body = self.fixtures.load('_port_v2.json') 2481 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2482 elif method == "PUT": 2483 if body: 2484 body = self.fixtures.load('_port_v2.json') 2485 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2486 else: 2487 return (httplib.INTERNAL_SERVER_ERROR, "", {}, httplib.responses[httplib.INTERNAL_SERVER_ERROR]) 2488 else: 2489 raise NotImplementedError() 2490 2491 def _v2_1337_servers_12065_os_volume_attachments_DEVICE_AUTO(self, method, url, body, headers): 2492 # test_attach_volume_device_auto 2493 if method == "POST": 2494 if 'rackspace' not in self.__class__.__name__.lower(): 2495 body = json.loads(body) 2496 self.assertEqual(body['volumeAttachment']['device'], None) 2497 2498 return (httplib.NO_CONTENT, "", {}, httplib.responses[httplib.NO_CONTENT]) 2499 else: 2500 raise NotImplementedError() 2501 2502 def _v2_1337_servers_1c01300f_ef97_4937_8f03_ac676d6234be_os_interface_126da55e_cfcb_41c8_ae39_a26cb8a7e723(self, method, url, body, headers): 2503 if method == "DELETE": 2504 return (httplib.NO_CONTENT, "", {}, httplib.responses[httplib.NO_CONTENT]) 2505 else: 2506 raise NotImplementedError() 2507 2508 def _v2_1337_servers_1c01300f_ef97_4937_8f03_ac676d6234be_os_interface(self, method, url, body, headers): 2509 if method == "POST": 2510 return (httplib.NO_CONTENT, "", {}, httplib.responses[httplib.NO_CONTENT]) 2511 else: 2512 raise NotImplementedError() 2513 2514 def _v1_1_slug_servers_1c01300f_ef97_4937_8f03_ac676d6234be_os_security_groups(self, method, url, body, headers): 2515 if method == "GET": 2516 body = self.fixtures.load( 2517 '_servers_1c01300f-ef97-4937-8f03-ac676d6234be_os-security-groups.json') 2518 else: 2519 raise NotImplementedError() 2520 2521 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2522 2523 def _v1_1_slug_os_security_groups(self, method, url, body, headers): 2524 if method == "GET": 2525 body = self.fixtures.load('_os_security_groups.json') 2526 elif method == "POST": 2527 body = self.fixtures.load('_os_security_groups_create.json') 2528 else: 2529 raise NotImplementedError() 2530 2531 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2532 2533 def _v1_1_slug_os_security_groups_6(self, method, url, body, headers): 2534 if method == "DELETE": 2535 return (httplib.NO_CONTENT, "", {}, httplib.responses[httplib.NO_CONTENT]) 2536 else: 2537 raise NotImplementedError() 2538 2539 def _v1_1_slug_os_security_group_rules(self, method, url, body, headers): 2540 if method == "POST": 2541 body = self.fixtures.load('_os_security_group_rules_create.json') 2542 else: 2543 raise NotImplementedError() 2544 2545 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2546 2547 def _v1_1_slug_os_security_group_rules_2(self, method, url, body, headers): 2548 if method == "DELETE": 2549 return (httplib.NO_CONTENT, "", {}, httplib.responses[httplib.NO_CONTENT]) 2550 else: 2551 raise NotImplementedError() 2552 2553 def _v1_1_slug_os_keypairs(self, method, url, body, headers): 2554 if method == "GET": 2555 body = self.fixtures.load('_os_keypairs.json') 2556 elif method == "POST": 2557 if 'public_key' in body: 2558 body = self.fixtures.load('_os_keypairs_create_import.json') 2559 else: 2560 body = self.fixtures.load('_os_keypairs_create.json') 2561 else: 2562 raise NotImplementedError() 2563 2564 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2565 2566 def _v1_1_slug_os_keypairs_test_key_pair(self, method, url, body, headers): 2567 if method == 'GET': 2568 body = self.fixtures.load('_os_keypairs_get_one.json') 2569 else: 2570 raise NotImplementedError() 2571 2572 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2573 2574 def _v1_1_slug_os_keypairs_doesnt_exist(self, method, url, body, headers): 2575 if method == 'GET': 2576 body = self.fixtures.load('_os_keypairs_not_found.json') 2577 else: 2578 raise NotImplementedError() 2579 2580 return (httplib.NOT_FOUND, body, self.json_content_headers, 2581 httplib.responses[httplib.NOT_FOUND]) 2582 2583 def _v1_1_slug_os_keypairs_key1(self, method, url, body, headers): 2584 if method == "DELETE": 2585 return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED]) 2586 else: 2587 raise NotImplementedError() 2588 2589 def _v1_1_slug_os_volumes(self, method, url, body, headers): 2590 if method == "GET": 2591 body = self.fixtures.load('_os_volumes.json') 2592 elif method == "POST": 2593 body = self.fixtures.load('_os_volumes_create.json') 2594 else: 2595 raise NotImplementedError() 2596 2597 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2598 2599 def _v1_1_slug_os_volumes_cd76a3a1_c4ce_40f6_9b9f_07a61508938d(self, method, url, body, headers): 2600 if method == "GET": 2601 body = self.fixtures.load( 2602 '_os_volumes_cd76a3a1_c4ce_40f6_9b9f_07a61508938d.json') 2603 elif method == "DELETE": 2604 body = '' 2605 else: 2606 raise NotImplementedError() 2607 2608 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2609 2610 def _v1_1_slug_servers_12065_os_volume_attachments(self, method, url, body, headers): 2611 if method == "POST": 2612 if 'rackspace' not in self.__class__.__name__.lower(): 2613 body = json.loads(body) 2614 self.assertEqual(body['volumeAttachment']['device'], '/dev/sdb') 2615 2616 body = self.fixtures.load( 2617 '_servers_12065_os_volume_attachments.json') 2618 else: 2619 raise NotImplementedError() 2620 2621 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2622 2623 def _v1_1_slug_servers_12065_os_volume_attachments_cd76a3a1_c4ce_40f6_9b9f_07a61508938d(self, method, url, body, 2624 headers): 2625 if method == "DELETE": 2626 body = '' 2627 else: 2628 raise NotImplementedError() 2629 2630 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2631 2632 def _v1_1_slug_os_floating_ip_pools(self, method, url, body, headers): 2633 if method == "GET": 2634 body = self.fixtures.load('_floating_ip_pools.json') 2635 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2636 else: 2637 raise NotImplementedError() 2638 2639 def _v1_1_slug_os_floating_ips_foo_bar_id(self, method, url, body, headers): 2640 if method == "DELETE": 2641 body = '' 2642 return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2643 else: 2644 raise NotImplementedError() 2645 2646 def _v1_1_slug_os_floating_ips(self, method, url, body, headers): 2647 if method == "GET": 2648 body = self.fixtures.load('_floating_ips.json') 2649 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2650 elif method == "POST": 2651 body = self.fixtures.load('_floating_ip.json') 2652 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2653 else: 2654 raise NotImplementedError() 2655 2656 def _v1_1_slug_servers_4242_action(self, method, url, body, headers): 2657 if method == "POST": 2658 body = '' 2659 return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2660 else: 2661 raise NotImplementedError() 2662 2663 def _v1_1_slug_os_networks(self, method, url, body, headers): 2664 if method == 'GET': 2665 body = self.fixtures.load('_os_networks.json') 2666 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2667 elif method == 'POST': 2668 body = self.fixtures.load('_os_networks_POST.json') 2669 return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2670 raise NotImplementedError() 2671 2672 def _v1_1_slug_os_networks_f13e5051_feea_416b_827a_1a0acc2dad14(self, method, url, body, headers): 2673 if method == 'DELETE': 2674 body = '' 2675 return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2676 raise NotImplementedError() 2677 2678 def _v1_1_slug_servers_72258_action(self, method, url, body, headers): 2679 if method == 'POST': 2680 body = self.fixtures.load('_servers_suspend.json') 2681 return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2682 else: 2683 raise NotImplementedError() 2684 2685 def _v1_1_slug_servers_12063_action(self, method, url, body, headers): 2686 if method == 'POST': 2687 body = self.fixtures.load('_servers_unpause.json') 2688 return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2689 else: 2690 raise NotImplementedError() 2691 2692 def _v1_1_slug_servers_12086_action(self, method, url, body, headers): 2693 if method == 'POST': 2694 body = self.fixtures.load('_servers_12086_console_output.json') 2695 return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2696 else: 2697 raise NotImplementedError() 2698 2699 def _v1_1_slug_os_snapshots(self, method, url, body, headers): 2700 if method == 'GET': 2701 body = self.fixtures.load('_os_snapshots.json') 2702 elif method == 'POST': 2703 body = self.fixtures.load('_os_snapshots_create.json') 2704 else: 2705 raise NotImplementedError() 2706 2707 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2708 2709 def _v1_1_slug_os_snapshots_3fbbcccf_d058_4502_8844_6feeffdf4cb5(self, method, url, body, headers): 2710 if method == 'GET': 2711 body = self.fixtures.load('_os_snapshot.json') 2712 status_code = httplib.OK 2713 elif method == 'DELETE': 2714 body = '' 2715 status_code = httplib.NO_CONTENT 2716 else: 2717 raise NotImplementedError() 2718 2719 return (status_code, body, self.json_content_headers, httplib.responses[httplib.OK]) 2720 2721 def _v1_1_slug_os_snapshots_3fbbcccf_d058_4502_8844_6feeffdf4cb5_RACKSPACE(self, method, url, body, headers): 2722 if method == 'GET': 2723 body = self.fixtures.load('_os_snapshot_rackspace.json') 2724 status_code = httplib.OK 2725 elif method == 'DELETE': 2726 body = '' 2727 status_code = httplib.NO_CONTENT 2728 else: 2729 raise NotImplementedError() 2730 2731 return (status_code, body, self.json_content_headers, httplib.responses[httplib.OK]) 2732 2733 def _v1_1_slug_os_snapshots_RACKSPACE(self, method, url, body, headers): 2734 if method == 'GET': 2735 body = self.fixtures.load('_os_snapshots_rackspace.json') 2736 elif method == 'POST': 2737 body = self.fixtures.load('_os_snapshots_create_rackspace.json') 2738 else: 2739 raise NotImplementedError() 2740 2741 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2742 2743 def _v2_1337_v2_0_networks(self, method, url, body, headers): 2744 if method == 'GET': 2745 if "router:external=True" in url: 2746 body = self.fixtures.load('_v2_0__networks_public.json') 2747 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2748 else: 2749 body = self.fixtures.load('_v2_0__networks.json') 2750 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2751 elif method == 'POST': 2752 body = self.fixtures.load('_v2_0__networks_POST.json') 2753 return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2754 raise NotImplementedError() 2755 2756 def _v2_1337_v2_0_networks_cc2dad14_827a_feea_416b_f13e50511a0a(self, method, url, body, headers): 2757 if method == "GET": 2758 body = self.fixtures.load('_v2_0__network.json') 2759 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2760 raise NotImplementedError() 2761 2762 def _v2_1337_v2_0_networks_d32019d3_bc6e_4319_9c1d_6722fc136a22(self, method, url, body, headers): 2763 if method == 'GET': 2764 body = self.fixtures.load('_v2_0__networks_POST.json') 2765 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2766 elif method == 'DELETE': 2767 body = '' 2768 return (httplib.NO_CONTENT, body, self.json_content_headers, httplib.responses[httplib.OK]) 2769 2770 def _v2_1337_v2_0_subnets_08eae331_0402_425a_923c_34f7cfe39c1b(self, method, url, body, headers): 2771 if method == 'GET': 2772 body = self.fixtures.load('_v2_0__subnet.json') 2773 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2774 if method == 'DELETE': 2775 body = '' 2776 return (httplib.NO_CONTENT, body, self.json_content_headers, httplib.responses[httplib.OK]) 2777 elif method == 'PUT': 2778 body = self.fixtures.load('_v2_0__subnet.json') 2779 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2780 2781 def _v2_1337_v2_0_subnets(self, method, url, body, headers): 2782 if method == 'POST': 2783 body = self.fixtures.load('_v2_0__subnet.json') 2784 return (httplib.CREATED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2785 else: 2786 body = self.fixtures.load('_v2_0__subnets.json') 2787 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2788 2789 def _v3_1337_volumes_detail(self, method, url, body, headers): 2790 body = self.fixtures.load('_v2_0__volumes.json') 2791 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2792 2793 def _v3_1337_volumes(self, method, url, body, headers): 2794 if method == 'POST': 2795 body = self.fixtures.load('_v2_0__volume.json') 2796 return (httplib.CREATED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2797 2798 def _v3_1337_volumes_cd76a3a1_c4ce_40f6_9b9f_07a61508938d(self, method, url, body, headers): 2799 if method == 'GET': 2800 body = self.fixtures.load('_v2_0__volume.json') 2801 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2802 if method == 'DELETE': 2803 body = '' 2804 return (httplib.NO_CONTENT, body, self.json_content_headers, httplib.responses[httplib.OK]) 2805 2806 def _v3_1337_volumes_abc6a3a1_c4ce_40f6_9b9f_07a61508938d(self, method, url, body, headers): 2807 if method == 'GET': 2808 body = self.fixtures.load('_v2_0__volume_abc6a3a1_c4ce_40f6_9b9f_07a61508938d.json') 2809 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2810 if method == 'DELETE': 2811 body = '' 2812 return (httplib.NO_CONTENT, body, self.json_content_headers, httplib.responses[httplib.OK]) 2813 2814 def _v3_1337_snapshots_detail(self, method, url, body, headers): 2815 if ('unit_test=paginate' in url and 'marker' not in url) or \ 2816 'unit_test=pagination_loop' in url: 2817 body = self.fixtures.load('_v2_0__snapshots_paginate_start.json') 2818 else: 2819 body = self.fixtures.load('_v2_0__snapshots.json') 2820 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2821 2822 def _v3_1337_snapshots(self, method, url, body, headers): 2823 if method == 'POST': 2824 body = self.fixtures.load('_v2_0__snapshot.json') 2825 return (httplib.CREATED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2826 2827 def _v3_1337_snapshots_3fbbcccf_d058_4502_8844_6feeffdf4cb5(self, method, url, body, headers): 2828 if method == 'GET': 2829 body = self.fixtures.load('_v2_0__snapshot.json') 2830 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2831 if method == 'DELETE': 2832 body = '' 2833 return (httplib.NO_CONTENT, body, self.json_content_headers, httplib.responses[httplib.OK]) 2834 2835 def _v2_1337_v2_0_security_groups(self, method, url, body, headers): 2836 if method == 'POST': 2837 body = self.fixtures.load('_v2_0__security_group.json') 2838 return (httplib.CREATED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2839 if method == 'GET': 2840 body = self.fixtures.load('_v2_0__security_groups.json') 2841 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2842 2843 def _v2_1337_v2_0_security_groups_6(self, method, url, body, headers): 2844 if method == 'GET': 2845 body = self.fixtures.load('_v2_0__security_group.json') 2846 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2847 if method == 'DELETE': 2848 body = '' 2849 return (httplib.NO_CONTENT, body, self.json_content_headers, httplib.responses[httplib.OK]) 2850 2851 def _v2_1337_v2_0_security_group_rules(self, method, url, body, headers): 2852 if method == 'POST': 2853 body = self.fixtures.load('_v2_0__security_group_rule.json') 2854 return (httplib.CREATED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2855 2856 def _v2_1337_v2_0_security_group_rules_2(self, method, url, body, headers): 2857 if method == 'DELETE': 2858 body = '' 2859 return (httplib.NO_CONTENT, body, self.json_content_headers, httplib.responses[httplib.OK]) 2860 2861 def _v2_1337_v2_0_floatingips(self, method, url, body, headers): 2862 if method == 'POST': 2863 body = self.fixtures.load('_v2_0__floatingip.json') 2864 return (httplib.CREATED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2865 if method == 'GET': 2866 body = self.fixtures.load('_v2_0__floatingips.json') 2867 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2868 2869 def _v2_1337_v2_0_floatingips_foo_bar_id(self, method, url, body, headers): 2870 if method == 'DELETE': 2871 body = '' 2872 return (httplib.NO_CONTENT, body, self.json_content_headers, httplib.responses[httplib.OK]) 2873 2874 def _v2_1337_v2_0_routers_f8a44de0_fc8e_45df_93c7_f79bf3b01c95(self, method, url, body, headers): 2875 if method == 'GET': 2876 body = self.fixtures.load('_v2_0__router.json') 2877 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2878 if method == 'DELETE': 2879 body = '' 2880 return (httplib.NO_CONTENT, body, self.json_content_headers, httplib.responses[httplib.OK]) 2881 2882 def _v2_1337_v2_0_routers(self, method, url, body, headers): 2883 if method == 'POST': 2884 body = self.fixtures.load('_v2_0__router.json') 2885 return (httplib.CREATED, body, self.json_content_headers, httplib.responses[httplib.OK]) 2886 else: 2887 body = self.fixtures.load('_v2_0__routers.json') 2888 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2889 2890 def _v2_1337_v2_0_routers_f8a44de0_fc8e_45df_93c7_f79bf3b01c95_add_router_interface(self, method, url, 2891 body, headers): 2892 if method == 'PUT': 2893 body = self.fixtures.load('_v2_0__router_interface.json') 2894 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2895 2896 def _v2_1337_v2_0_routers_f8a44de0_fc8e_45df_93c7_f79bf3b01c95_remove_router_interface(self, method, url, 2897 body, headers): 2898 if method == 'PUT': 2899 body = self.fixtures.load('_v2_0__router_interface.json') 2900 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2901 2902 def _v2_1337_os_quota_sets_tenant_id_detail(self, method, url, body, headers): 2903 if method == 'GET': 2904 body = self.fixtures.load('_v2_0__quota_set.json') 2905 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2906 2907 def _v2_1337_flavors_7_os_extra_specs(self, method, url, body, headers): 2908 if method == "GET": 2909 body = self.fixtures.load('_flavor_extra_specs.json') 2910 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2911 else: 2912 raise NotImplementedError() 2913 2914 def _v2_1337_servers_1000_action(self, method, url, body, headers): 2915 if method != 'POST' or body != '{"removeSecurityGroup": {"name": "sgname"}}': 2916 raise NotImplementedError(body) 2917 return httplib.ACCEPTED, None, {}, httplib.responses[httplib.ACCEPTED] 2918 2919 def _v2_1337_v2_0_quotas_tenant_id_details_json(self, method, url, body, headers): 2920 if method == 'GET': 2921 body = self.fixtures.load('_v2_0__network_quota.json') 2922 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2923 2924 def _v3_1337_os_quota_sets_tenant_id(self, method, url, body, headers): 2925 if method == 'GET': 2926 body = self.fixtures.load('_v3_0__volume_quota.json') 2927 return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) 2928# This exists because the nova compute url in devstack has v2 in there but the v1.1 fixtures 2929# work fine. 2930 2931 2932class OpenStack_2_0_MockHttp(OpenStack_1_1_MockHttp): 2933 2934 def __init__(self, *args, **kwargs): 2935 super(OpenStack_2_0_MockHttp, self).__init__(*args, **kwargs) 2936 2937 methods1 = OpenStack_1_1_MockHttp.__dict__ 2938 2939 names1 = [m for m in methods1 if m.find('_v1_1') == 0] 2940 2941 for name in names1: 2942 method = methods1[name] 2943 new_name = name.replace('_v1_1_slug_', '_v2_1337_') 2944 setattr(self, new_name, method_type(method, self, 2945 OpenStack_2_0_MockHttp)) 2946 2947 def _v2_0_tenants_UNAUTHORIZED(self, method, url, body, headers): 2948 return (httplib.UNAUTHORIZED, "", {}, httplib.responses[httplib.UNAUTHORIZED]) 2949 2950 2951class OpenStack_1_1_Auth_2_0_Tests(OpenStack_1_1_Tests): 2952 driver_args = OPENSTACK_PARAMS + ('1.1',) 2953 driver_kwargs = {'ex_force_auth_version': '2.0'} 2954 2955 def setUp(self): 2956 self.driver_klass.connectionCls.conn_class = OpenStack_2_0_MockHttp 2957 self.driver_klass.connectionCls.auth_url = "https://auth.api.example.com" 2958 OpenStackMockHttp.type = None 2959 OpenStack_1_1_MockHttp.type = None 2960 OpenStack_2_0_MockHttp.type = None 2961 self.driver = self.create_driver() 2962 # normally authentication happens lazily, but we force it here 2963 self.driver.connection._populate_hosts_and_request_paths() 2964 clear_pricing_data() 2965 self.node = self.driver.list_nodes()[1] 2966 2967 def test_auth_user_info_is_set(self): 2968 self.driver.connection._populate_hosts_and_request_paths() 2969 self.assertEqual(self.driver.connection.auth_user_info, { 2970 'id': '7', 2971 'name': 'testuser', 2972 'roles': [{'description': 'Default Role.', 2973 'id': 'identity:default', 2974 'name': 'identity:default'}]}) 2975 2976 2977class OpenStackMockAuthCache(OpenStackAuthenticationCache): 2978 def __init__(self): 2979 self.reset() 2980 2981 def get(self, key): 2982 return self.store.get(key) 2983 2984 def put(self, key, context): 2985 self.store[key] = context 2986 2987 def clear(self, key): 2988 if key in self.store: 2989 del self.store[key] 2990 2991 def reset(self): 2992 self.store = {} 2993 2994 def __len__(self): 2995 return len(self.store) 2996 2997 2998if __name__ == '__main__': 2999 sys.exit(unittest.main()) 3000