merge upstream
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/5728c2be Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/5728c2be Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/5728c2be Branch: refs/heads/trunk Commit: 5728c2be2916c5c1d2bd6ccc1495e7fb26318cef Parents: a5b8b97 7d5eb6f Author: micafer <[email protected]> Authored: Wed Sep 5 15:47:57 2018 +0200 Committer: micafer <[email protected]> Committed: Wed Sep 5 15:47:57 2018 +0200 ---------------------------------------------------------------------- .pylintrc | 1 + .travis.yml | 21 +- CHANGES.rst | 60 +- README.rst | 13 +- contrib/scrap-ec2-sizes.py | 281 ++ .../images/misc/azure_blobs_account_kind.png | Bin 0 -> 292732 bytes .../misc/azure_blobs_manage_access_keys_1.png | Bin 64259 -> 223140 bytes .../misc/azure_blobs_manage_access_keys_2.png | Bin 26247 -> 388303 bytes docs/_static/images/provider_logos/scaleway.png | Bin 0 -> 11527 bytes docs/committer_guide.rst | 10 +- docs/compute/drivers/scaleway.rst | 30 + docs/conf.py | 4 +- docs/examples/compute/scaleway/create_node.py | 16 + docs/examples/compute/scaleway/list_nodes.py | 9 + docs/examples/compute/scaleway/list_volumes.py | 12 + docs/other/changes_in_2_0.rst | 2 +- docs/storage/drivers/azure_blobs.rst | 12 +- libcloud/common/dimensiondata.py | 2 +- libcloud/common/openstack.py | 1 + libcloud/common/openstack_identity.py | 14 + libcloud/compute/constants.py | 4429 ++++++++++++++++++ libcloud/compute/drivers/azure.py | 9 +- libcloud/compute/drivers/digitalocean.py | 16 + libcloud/compute/drivers/ec2.py | 2124 +-------- libcloud/compute/drivers/gce.py | 15 +- libcloud/compute/drivers/openstack.py | 239 +- libcloud/compute/drivers/scaleway.py | 663 +++ libcloud/compute/providers.py | 2 + libcloud/compute/types.py | 1 + libcloud/storage/drivers/azure_blobs.py | 29 +- libcloud/storage/drivers/dummy.py | 2 - libcloud/test/__init__.py | 7 +- libcloud/test/common/test_openstack_identity.py | 11 + .../fixtures/digitalocean_v2/list_node.json | 112 + .../fixtures/openstack_v1.1/_port_v2.json | 32 + .../fixtures/openstack_v1.1/_ports_v2.json | 185 + .../compute/fixtures/scaleway/create_image.json | 21 + .../compute/fixtures/scaleway/create_node.json | 40 + .../fixtures/scaleway/create_volume.json | 13 + .../scaleway/create_volume_snapshot.json | 15 + .../test/compute/fixtures/scaleway/error.json | 1 + .../fixtures/scaleway/error_invalid_image.json | 1 + .../compute/fixtures/scaleway/get_image.json | 21 + .../fixtures/scaleway/list_availability.json | 13 + .../compute/fixtures/scaleway/list_images.json | 42 + .../compute/fixtures/scaleway/list_nodes.json | 74 + .../fixtures/scaleway/list_nodes_empty.json | 3 + .../compute/fixtures/scaleway/list_sizes.json | 76 + .../scaleway/list_volume_snapshots.json | 30 + .../compute/fixtures/scaleway/list_volumes.json | 26 + .../fixtures/scaleway/list_volumes_empty.json | 3 + .../compute/fixtures/scaleway/reboot_node.json | 9 + .../compute/fixtures/scaleway/token_info.json | 14 + .../compute/fixtures/scaleway/user_info.json | 15 + libcloud/test/compute/test_digitalocean_v2.py | 12 + libcloud/test/compute/test_ec2.py | 40 +- libcloud/test/compute/test_openstack.py | 129 +- libcloud/test/compute/test_scaleway.py | 334 ++ libcloud/test/secrets.py-dist | 5 +- libcloud/test/storage/test_azure_blobs.py | 4 +- libcloud/test/test_connection.py | 16 + libcloud/test/test_utils.py | 8 +- libcloud/utils/files.py | 4 +- setup.cfg | 4 + setup.py | 8 +- tox.ini | 11 +- 66 files changed, 7151 insertions(+), 2205 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/5728c2be/libcloud/common/openstack.py ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/5728c2be/libcloud/compute/drivers/openstack.py ---------------------------------------------------------------------- diff --cc libcloud/compute/drivers/openstack.py index 44342d5,2ce9f55..024961d --- a/libcloud/compute/drivers/openstack.py +++ b/libcloud/compute/drivers/openstack.py @@@ -2535,9 -2560,15 +2561,16 @@@ class OpenStack_2_NodeDriver(OpenStack_ 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' + PORT_INTERFACE_MAP = { + 'BUILD': OpenStack_2_PortInterfaceState.BUILD, + 'ACTIVE': OpenStack_2_PortInterfaceState.ACTIVE, + 'DOWN': OpenStack_2_PortInterfaceState.DOWN, + 'UNKNOWN': OpenStack_2_PortInterfaceState.UNKNOWN + } + def __init__(self, *args, **kwargs): original_connectionCls = self.connectionCls self._ex_force_api_version = str(kwargs.pop('ex_force_api_version', @@@ -2567,11 -2588,8 +2600,11 @@@ super(OpenStack_2_NodeDriver, self).__init__(*args, **kwargs) self.network_connection = self.connection - # We run the init again to get the compute API connection + # We run the init once 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) @@@ -2734,57 -2783,121 +2798,172 @@@ ) 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=obj['cidr'], + 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) + def ex_list_ports(self): + """ + List all OpenStack_2_PortInterfaces + + https://developer.openstack.org/api-ref/network/v2/#list-ports + + :rtype: ``list`` of :class:`OpenStack_2_PortInterface` + """ + response = self.network_connection.request( + '/v2.0/ports' + ) + return [self._to_port(port) for port in response.object['ports']] + + def ex_delete_port(self, port): + """ + Delete an OpenStack_2_PortInterface + + https://developer.openstack.org/api-ref/network/v2/#delete-port + + :param port: port interface to remove + :type port: :class:`OpenStack_2_PortInterface` + + :rtype: ``bool`` + """ + response = self.network_connection.request( + '/v2.0/ports/%s' % port.id, method='DELETE' + ) + return response.success() + + def ex_detach_port_interface(self, node, port): + """ + Detaches an OpenStack_2_PortInterface interface from a Node. + :param node: node + :type node: :class:`Node` + + :param port: port interface to remove + :type port: :class:`OpenStack_2_PortInterface` + + :rtype: ``bool`` + """ + return self.connection.request( + '/servers/%s/os-interface/%s' % (node.id, port.id), + method='DELETE' + ).success() + + def ex_attach_port_interface(self, node, port): + """ + Attaches an OpenStack_2_PortInterface to a Node. + + :param node: node + :type node: :class:`Node` + + :param port: port interface to remove + :type port: :class:`OpenStack_2_PortInterface` + + :rtype: ``bool`` + """ + data = { + 'interfaceAttachment': { + 'port_id': port.id + } + } + return self.connection.request( + '/servers/{}/os-interface'.format(node.id), + method='POST', data=data + ).success() + + def ex_create_port(self, network, description=None, + admin_state_up=True, name=None): + """ + Creates a new OpenStack_2_PortInterface + + :param network: ID of the network where the newly created + port should be attached to + :type network: :class:`OpenStackNetwork` + + :param description: Description of the port + :type description: str + + :param admin_state_up: The administrative state of the + resource, which is up or down + :type admin_state_up: bool + + :param name: Human-readable name of the resource + :type name: str + + :rtype: :class:`OpenStack_2_PortInterface` + """ + data = { + 'port': + { + 'description': description or '', + 'admin_state_up': admin_state_up, + 'name': name or '', + 'network_id': network.id, + } + } + response = self.network_connection.request( + '/v2.0/ports', method='POST', data=data + ) + return self._to_port(response.object['port']) + + def ex_get_port(self, port_interface_id): + """ + Retrieve the OpenStack_2_PortInterface with the given ID + + :param port_interface_id: ID of the requested port + :type port_interface_id: str + + :return: :class:`OpenStack_2_PortInterface` + """ + response = self.network_connection.request( + '/v2.0/ports/{}'.format(port_interface_id), method='GET' + ) + return self._to_port(response.object['port']) class OpenStack_1_1_FloatingIpPool(object): @@@ -2892,19 -3005,51 +3071,69 @@@ class OpenStack_1_1_FloatingIpAddress(o % (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) ++ ++ + class OpenStack_2_PortInterface(UuidMixin): + """ + Port Interface info. Similar in functionality to a floating IP (can be + attached / detached from a compute instance) but implementation-wise a + bit different. + + > A port is a connection point for attaching a single device, such as the + > NIC of a server, to a network. The port also describes the associated + > network configuration, such as the MAC and IP addresses to be used on + > that port. + https://docs.openstack.org/python-openstackclient/pike/cli/command-objects/port.html + + Also see: + https://developer.openstack.org/api-ref/compute/#port-interfaces-servers-os-interface + """ + + def __init__(self, id, state, driver, created=None, extra=None): + """ + :param id: Port Interface ID. + :type id: ``str`` + :param state: State of the OpenStack_2_PortInterface. + :type state: :class:`.OpenStack_2_PortInterfaceState` + :param created: A datetime object that represents when the + port interface was created + :type created: ``datetime.datetime`` + :param extra: Optional provided specific attributes associated with + this image. + :type extra: ``dict`` + """ + self.id = str(id) + self.state = state + self.driver = driver + self.created = created + self.extra = extra or {} + UuidMixin.__init__(self) + + def delete(self): + """ + Delete this Port Interface + + :rtype: ``bool`` + """ + return self.driver.ex_delete_port(self) + + def __repr__(self): + return (('<OpenStack_2_PortInterface: id=%s, state=%s, ' + 'driver=%s ...>') + % (self.id, self.state, self.driver.name)) http://git-wip-us.apache.org/repos/asf/libcloud/blob/5728c2be/libcloud/test/compute/test_openstack.py ---------------------------------------------------------------------- diff --cc libcloud/test/compute/test_openstack.py index d8ac0b0,4462b34..c9be795 --- a/libcloud/test/compute/test_openstack.py +++ b/libcloud/test/compute/test_openstack.py @@@ -1679,38 -1697,102 +1679,134 @@@ class OpenStack_2_Tests(OpenStack_1_1_T 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), 2) + self.assertEqual(network.name, 'net1') + 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') + + def test_ex_list_network(self): + networks = self.driver.ex_list_networks() + network = networks[0] + + self.assertEqual(len(networks), 2) + self.assertEqual(network.name, 'net1') + + def test_ex_create_network(self): + network = self.driver.ex_create_network(name='net1', + cidr='127.0.0.0/24') + self.assertEqual(network.name, 'net1') + + def test_ex_delete_network(self): + network = self.driver.ex_list_networks()[0] + self.assertTrue(self.driver.ex_delete_network(network=network)) + + def test_ex_list_ports(self): + ports = self.driver.ex_list_ports() + + port = ports[0] + self.assertEqual(port.id, '126da55e-cfcb-41c8-ae39-a26cb8a7e723') + self.assertEqual(port.state, OpenStack_2_PortInterfaceState.BUILD) + self.assertEqual(port.created, '2018-07-04T14:38:18Z') + self.assertEqual( + port.extra['network_id'], + '123c8a8c-6427-4e8f-a805-2035365f4d43' + ) + self.assertEqual( + port.extra['project_id'], + 'abcdec85bee34bb0a44ab8255eb36abc' + ) + self.assertEqual( + port.extra['tenant_id'], + 'abcdec85bee34bb0a44ab8255eb36abc' + ) + self.assertEqual(port.extra['name'], '') + + def test_ex_create_port(self): + network = OpenStackNetwork(id='123c8a8c-6427-4e8f-a805-2035365f4d43', name='test-network', + cidr='1.2.3.4', driver=self.driver) + port = self.driver.ex_create_port(network=network, description='Some port description', name='Some port name', + admin_state_up=True) + + self.assertEqual(port.id, '126da55e-cfcb-41c8-ae39-a26cb8a7e723') + self.assertEqual(port.state, OpenStack_2_PortInterfaceState.BUILD) + self.assertEqual(port.created, '2018-07-04T14:38:18Z') + self.assertEqual( + port.extra['network_id'], + '123c8a8c-6427-4e8f-a805-2035365f4d43' + ) + self.assertEqual( + port.extra['project_id'], + 'abcdec85bee34bb0a44ab8255eb36abc' + ) + self.assertEqual( + port.extra['tenant_id'], + 'abcdec85bee34bb0a44ab8255eb36abc' + ) + self.assertEqual(port.extra['admin_state_up'], True) + self.assertEqual(port.extra['name'], 'Some port name') + self.assertEqual(port.extra['description'], 'Some port description') + + def test_ex_get_port(self): + port = self.driver.ex_get_port('126da55e-cfcb-41c8-ae39-a26cb8a7e723') + + self.assertEqual(port.id, '126da55e-cfcb-41c8-ae39-a26cb8a7e723') + self.assertEqual(port.state, OpenStack_2_PortInterfaceState.BUILD) + self.assertEqual(port.created, '2018-07-04T14:38:18Z') + self.assertEqual( + port.extra['network_id'], + '123c8a8c-6427-4e8f-a805-2035365f4d43' + ) + self.assertEqual( + port.extra['project_id'], + 'abcdec85bee34bb0a44ab8255eb36abc' + ) + self.assertEqual( + port.extra['tenant_id'], + 'abcdec85bee34bb0a44ab8255eb36abc' + ) + self.assertEqual(port.extra['name'], 'Some port name') + + def test_ex_delete_port(self): + ports = self.driver.ex_list_ports() + port = ports[0] + + ret = self.driver.ex_delete_port(port) + + self.assertTrue(ret) + + def test_detach_port_interface(self): + node = Node(id='1c01300f-ef97-4937-8f03-ac676d6234be', name=None, + state=None, public_ips=None, private_ips=None, + driver=self.driver) + ports = self.driver.ex_list_ports() + port = ports[0] + + ret = self.driver.ex_detach_port_interface(node, port) + + self.assertTrue(ret) + + def test_attach_port_interface(self): + node = Node(id='1c01300f-ef97-4937-8f03-ac676d6234be', name=None, + state=None, public_ips=None, private_ips=None, + driver=self.driver) + ports = self.driver.ex_list_ports() + port = ports[0] + + ret = self.driver.ex_attach_port_interface(node, port) + + self.assertTrue(ret) + class OpenStack_1_1_FactoryMethodTests(OpenStack_1_1_Tests): should_list_locations = False
