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