Fix LIBCLOUD-997, implements LIBCLOUD-998 and LIBCLOUD-604
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/c7464b57 Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/c7464b57 Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/c7464b57 Branch: refs/heads/trunk Commit: c7464b57dcc7e09fdfa01348de85f85046dac3a1 Parents: 271813f Author: micafer <[email protected]> Authored: Fri May 25 09:05:49 2018 +0200 Committer: micafer <[email protected]> Committed: Fri May 25 09:05:49 2018 +0200 ---------------------------------------------------------------------- docs/compute/drivers/openstack.rst | 10 +- libcloud/common/openstack.py | 1 + libcloud/compute/drivers/openstack.py | 110 ++++++++++++++++++- .../fixtures/openstack/_v2_0__networks.json | 33 ++++++ .../fixtures/openstack/_v2_0__subnets.json | 62 +++++++++++ libcloud/test/compute/test_openstack.py | 26 ++++- 6 files changed, 239 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/c7464b57/docs/compute/drivers/openstack.rst ---------------------------------------------------------------------- diff --git a/docs/compute/drivers/openstack.rst b/docs/compute/drivers/openstack.rst index 199fa38..887057a 100644 --- a/docs/compute/drivers/openstack.rst +++ b/docs/compute/drivers/openstack.rst @@ -88,9 +88,17 @@ Available arguments: * ``ex_force_service_type`` * ``ex_force_service_name`` * ``ex_force_service_region`` -* ``ex_force_base_url`` - Base URL to the OpenStack API endpoint. By default, +* ``ex_force_base_url`` - Base URL to the OpenStack nova API endpoint. By default, driver obtains API endpoint URL from the server catalog, but if this argument is provided, this step is skipped and the provided value is used directly. +* ``ex_force_network_url`` - Base URL to the OpenStack neutron API endpoint. By default, + driver obtains API endpoint URL from the server catalog, but if this argument + is provided, this step is skipped and the provided value is used directly. Only valid + in case of api_version >= 2.0. +* ``ex_force_image_url`` - Base URL to the OpenStack glance API endpoint. By default, + driver obtains API endpoint URL from the server catalog, but if this argument + is provided, this step is skipped and the provided value is used directly. Only valid + in case of api_version >= 2.0. Some examples which show how to use this arguments can be found in the section below. http://git-wip-us.apache.org/repos/asf/libcloud/blob/c7464b57/libcloud/common/openstack.py ---------------------------------------------------------------------- diff --git a/libcloud/common/openstack.py b/libcloud/common/openstack.py index 954a15d..6b7327a 100644 --- a/libcloud/common/openstack.py +++ b/libcloud/common/openstack.py @@ -156,6 +156,7 @@ class OpenStackBaseConnection(ConnectionUserAndKey): if ex_force_auth_version: self._auth_version = ex_force_auth_version + self.base_url = ex_force_base_url self._ex_force_base_url = ex_force_base_url self._ex_force_auth_url = ex_force_auth_url self._ex_force_auth_token = ex_force_auth_token http://git-wip-us.apache.org/repos/asf/libcloud/blob/c7464b57/libcloud/compute/drivers/openstack.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/openstack.py b/libcloud/compute/drivers/openstack.py index 9bacad2..dc164fb 100644 --- a/libcloud/compute/drivers/openstack.py +++ b/libcloud/compute/drivers/openstack.py @@ -2479,6 +2479,13 @@ class OpenStack_2_Connection(OpenStackComputeConnection): accept_format = 'application/json' default_content_type = 'application/json; charset=UTF-8' + def __init__(self, *args, **kwargs): + if 'ex_force_image_url' in kwargs: + del kwargs['ex_force_image_url'] + if 'ex_force_network_url' in kwargs: + del kwargs['ex_force_network_url'] + super(OpenStack_2_Connection, self).__init__(*args, **kwargs) + def encode_data(self, data): return json.dumps(data) @@ -2492,6 +2499,18 @@ class OpenStack_2_ImageConnection(OpenStackImageConnection): return json.dumps(data) +class OpenStack_2_NetworkConnection(OpenStackBaseConnection): + service_type = 'network' + service_name = 'neutron' + service_region = 'RegionOne' + responseCls = OpenStack_1_1_Response + accept_format = 'application/json' + default_content_type = 'application/json; charset=UTF-8' + + def encode_data(self, data): + return json.dumps(data) + + class OpenStack_2_NodeDriver(OpenStack_1_1_NodeDriver): """ OpenStack node driver. @@ -2514,11 +2533,14 @@ class OpenStack_2_NodeDriver(OpenStack_1_1_NodeDriver): # See https://developer.openstack.org/api-ref/ # image/v2/index.html#list-image-members image_connectionCls = OpenStack_2_ImageConnection + network_connectionCls = OpenStack_2_NetworkConnection image_connection = None + network_connection = None type = Provider.OPENSTACK features = {"create_node": ["generates_password"]} - _networks_url_prefix = '/os-networks' + _networks_url_prefix = '/v2.0/networks' + _subnets_url_prefix = '/v2.0/subnets' def __init__(self, *args, **kwargs): original_connectionCls = self.connectionCls @@ -2527,14 +2549,31 @@ class OpenStack_2_NodeDriver(OpenStack_1_1_NodeDriver): if 'ex_force_auth_version' not in kwargs: kwargs['ex_force_auth_version'] = '3.x_password' + original_ex_force_base_url = kwargs.get('ex_force_base_url', None) + # We run the init once to get the Glance V2 API connection # and put that on the object under self.image_connection. + self._ex_force_base_url = str(kwargs.pop('ex_force_image_url', + None)) + kwargs['ex_force_base_url'] = self._ex_force_base_url self.connectionCls = self.image_connectionCls super(OpenStack_2_NodeDriver, self).__init__(*args, **kwargs) self.image_connection = self.connection + # We run the init once to get the Neutron V2 API connection + # and put that on the object under self.image_connection. + self._ex_force_base_url = str(kwargs.pop('ex_force_network_url', + None)) + kwargs['ex_force_base_url'] = self._ex_force_base_url + self.connectionCls = self.network_connectionCls + super(OpenStack_2_NodeDriver, self).__init__(*args, **kwargs) + self.network_connection = self.connection + # We run the init again to get the compute API connection # and that's put under self.connection as normal. + self._ex_force_base_url = original_ex_force_base_url + if original_ex_force_base_url: + kwargs['ex_force_base_url'] = self._ex_force_base_url self.connectionCls = original_connectionCls super(OpenStack_2_NodeDriver, self).__init__(*args, **kwargs) @@ -2698,6 +2737,57 @@ class OpenStack_2_NodeDriver(OpenStack_1_1_NodeDriver): return self._to_image_member(response.object) + def _to_networks(self, obj): + networks = obj['networks'] + return [self._to_network(network) for network in networks] + + def _to_network(self, obj): + extra = {} + if obj.get('router:external', None): + extra['router:external'] = obj.get('router:external') + if obj.get('subnets', None): + extra['subnets'] = obj.get('subnets') + return OpenStackNetwork(id=obj['id'], + name=obj['name'], + cidr=None, + driver=self, + extra=extra) + + def ex_list_networks(self): + """ + Get a list of Networks that are available. + + :rtype: ``list`` of :class:`OpenStackNetwork` + """ + response = self.network_connection.request(self._networks_url_prefix).object + return self._to_networks(response) + + def _to_subnets(self, obj): + subnets = obj['subnets'] + return [self._to_subnet(subnet) for subnet in subnets] + + def _to_subnet(self, obj): + extra = {} + if obj.get('router:external', None): + extra['router:external'] = obj.get('router:external') + if obj.get('subnets', None): + extra['subnets'] = obj.get('subnets') + return OpenStack_2_SubNet(id=obj['id'], + name=obj['name'], + cidr=None, + driver=self, + extra=extra) + + def ex_list_subnets(self): + """ + Get a list of Subnet that are available. + + :rtype: ``list`` of :class:`OpenStack_2_SubNet` + """ + response = self.network_connection.request(self._subnets_url_prefix).object + return self._to_subnets(response) + + class OpenStack_1_1_FloatingIpPool(object): """ Floating IP Pool info. @@ -2801,3 +2891,21 @@ class OpenStack_1_1_FloatingIpAddress(object): return ('<OpenStack_1_1_FloatingIpAddress: id=%s, ip_addr=%s,' ' pool=%s, driver=%s>' % (self.id, self.ip_address, self.pool, self.driver)) + + +class OpenStack_2_SubNet(object): + """ + A Virtual SubNet. + """ + + def __init__(self, id, name, cidr, driver, extra=None): + self.id = str(id) + self.name = name + self.cidr = cidr + self.driver = driver + self.extra = extra or {} + + def __repr__(self): + return '<OpenStack_2_SubNet id="%s" name="%s" cidr="%s">' % (self.id, + self.name, + self.cidr) \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/c7464b57/libcloud/test/compute/fixtures/openstack/_v2_0__networks.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack/_v2_0__networks.json b/libcloud/test/compute/fixtures/openstack/_v2_0__networks.json new file mode 100644 index 0000000..7c54850 --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack/_v2_0__networks.json @@ -0,0 +1,33 @@ +{ + "network": { + "admin_state_up": true, + "availability_zone_hints": [], + "availability_zones": [ + "nova" + ], + "created_at": "2016-03-08T20:19:41", + "dns_domain": "my-domain.org.", + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "ipv4_address_scope": null, + "ipv6_address_scope": null, + "l2_adjacency": false, + "mtu": 1500, + "name": "private-network", + "port_security_enabled": true, + "project_id": "4fd44f30292945e481c7b8a0c8908869", + "qos_policy_id": "6a8454ade84346f59e8d40665f878b2e", + "revision_number": 1, + "router:external": false, + "shared": true, + "status": "ACTIVE", + "subnets": [ + "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" + ], + "tags": ["tag1,tag2"], + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "updated_at": "2016-03-08T20:19:41", + "vlan_transparent": false, + "description": "", + "is_default": true + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/c7464b57/libcloud/test/compute/fixtures/openstack/_v2_0__subnets.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack/_v2_0__subnets.json b/libcloud/test/compute/fixtures/openstack/_v2_0__subnets.json new file mode 100644 index 0000000..d3f2763 --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack/_v2_0__subnets.json @@ -0,0 +1,62 @@ +{ + "subnets": [ + { + "name": "private-subnet", + "enable_dhcp": true, + "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "segment_id": null, + "project_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "10.0.0.2", + "end": "10.0.0.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": "10.0.0.1", + "cidr": "10.0.0.0/24", + "id": "08eae331-0402-425a-923c-34f7cfe39c1b", + "created_at": "2016-10-10T14:35:34Z", + "description": "", + "ipv6_address_mode": null, + "ipv6_ra_mode": null, + "revision_number": 2, + "service_types": [], + "subnetpool_id": null, + "tags": ["tag1,tag2"], + "updated_at": "2016-10-10T14:35:34Z" + }, + { + "name": "my_subnet", + "enable_dhcp": true, + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "segment_id": null, + "project_id": "4fd44f30292945e481c7b8a0c8908869", + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "192.0.0.2", + "end": "192.255.255.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": "192.0.0.1", + "cidr": "192.0.0.0/8", + "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b", + "created_at": "2016-10-10T14:35:47Z", + "description": "", + "ipv6_address_mode": null, + "ipv6_ra_mode": null, + "revision_number": 2, + "service_types": [], + "subnetpool_id": null, + "tags": ["tag1,tag2"], + "updated_at": "2016-10-10T14:35:47Z" + } + ] +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/c7464b57/libcloud/test/compute/test_openstack.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_openstack.py b/libcloud/test/compute/test_openstack.py index 54606ef..e1dd271 100644 --- a/libcloud/test/compute/test_openstack.py +++ b/libcloud/test/compute/test_openstack.py @@ -1568,7 +1568,9 @@ class OpenStack_2_Tests(OpenStack_1_1_Tests): driver_type = OpenStack_2_NodeDriver driver_kwargs = { 'ex_force_auth_version': '2.0', - 'ex_force_auth_url': 'https://auth.api.example.com' + 'ex_force_auth_url': 'https://auth.api.example.com:8774', + 'ex_force_image_url': 'https://auth.api.example.com:9292', + 'ex_force_network_url': 'https://auth.api.example.com:9696' } def setUp(self): @@ -1692,6 +1694,21 @@ class OpenStack_2_Tests(OpenStack_1_1_Tests): self.assertEqual(image_member.extra['updated'], '2018-03-02T14:20:37Z') self.assertEqual(image_member.extra['schema'], '/v2/schemas/member') + def test_ex_list_networks(self): + networks = self.driver.ex_list_networks() + network = networks[0] + + self.assertEqual(len(networks), 1) + self.assertEqual(network.name, 'private-network') + self.assertEqual(network.extra['subnets'], ['54d6f61d-db07-451c-9ab3-b9609b6b6f0b']) + + def test_ex_list_subnets(self): + subnets = self.driver.ex_list_subnets() + subnet = subnets[0] + + self.assertEqual(len(subnets), 2) + self.assertEqual(subnet.name, 'private-subnet') + self.assertEqual(subnet.cidr, '10.0.0.0/24') class OpenStack_1_1_FactoryMethodTests(OpenStack_1_1_Tests): should_list_locations = False @@ -2144,6 +2161,13 @@ class OpenStack_2_0_MockHttp(OpenStack_1_1_MockHttp): setattr(self, new_name, method_type(method, self, OpenStack_2_0_MockHttp)) + def _v2_0_networks(self, method, url, body, headers): + body = self.auth_fixtures.load('_v2_0__networks.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + + def _v2_0_subnets(self, method, url, body, headers): + body = self.auth_fixtures.load('_v2_0__subnets.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) class OpenStack_1_1_Auth_2_0_Tests(OpenStack_1_1_Tests): driver_args = OPENSTACK_PARAMS + ('1.1',)
