libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From to...@apache.org
Subject [1/2] git commit: Issue LIBCLOUD-474: Add Network Interface Support to the EC2 driver.
Date Fri, 03 Jan 2014 02:36:09 GMT
Updated Branches:
  refs/heads/trunk 621ccdd9a -> b7f709f14


Issue LIBCLOUD-474: Add Network Interface Support to the EC2 driver.

Signed-off-by: Tomaz Muraus <tomaz@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/8c948876
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/8c948876
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/8c948876

Branch: refs/heads/trunk
Commit: 8c94887679102266c1f810b4f4e9a6d3df84542f
Parents: 621ccdd
Author: Chris DeRamus <chris@divvycloud.com>
Authored: Fri Dec 27 17:02:18 2013 -0500
Committer: Tomaz Muraus <tomaz@apache.org>
Committed: Fri Jan 3 03:17:52 2014 +0100

----------------------------------------------------------------------
 libcloud/compute/drivers/ec2.py                 | 297 +++++++++++++++++++
 .../fixtures/ec2/attach_network_interface.xml   |   4 +
 .../fixtures/ec2/create_network_interface.xml   |  30 ++
 .../fixtures/ec2/delete_network_interface.xml   |   4 +
 .../ec2/describe_network_interfaces.xml         |  83 ++++++
 .../fixtures/ec2/detach_network_interface.xml   |   4 +
 libcloud/test/compute/test_ec2.py               |  62 ++++
 7 files changed, 484 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/8c948876/libcloud/compute/drivers/ec2.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py
index 3d52214..c116ea5 100644
--- a/libcloud/compute/drivers/ec2.py
+++ b/libcloud/compute/drivers/ec2.py
@@ -840,6 +840,26 @@ class EC2NetworkSubnet(object):
         return (('<EC2NetworkSubnet: id=%s, name=%s') % (self.id, self.name))
 
 
+class EC2NetworkInterface(object):
+    """
+    Represents information about a VPC network interface
+
+    Note: This class is EC2 specific. The state parameter denotes the current
+    status of the interface. Valid values for state are attaching, attached,
+    detaching and detached.
+    """
+
+    def __init__(self, id, name, state, extra=None):
+        self.id = id
+        self.name = name
+        self.state = state
+        self.extra = extra or {}
+
+    def __repr__(self):
+        return (('<EC2NetworkInterface: id=%s, name=%s')
+                % (self.id, self.name))
+
+
 class BaseEC2NodeDriver(NodeDriver):
     """
     Base Amazon EC2 node driver.
@@ -1182,6 +1202,148 @@ class BaseEC2NodeDriver(NodeDriver):
 
         return EC2NetworkSubnet(subnet_id, name, state, extra=extra)
 
+    def _to_interfaces(self, response):
+        return [self._to_interface(el) for el in response.findall(
+            fixxpath(xpath='networkInterfaceSet/item', namespace=NAMESPACE))
+        ]
+
+    def _to_interface(self, element, name=None):
+        """
+        Parse the XML element and return a EC2NetworkInterface object.
+
+        :param      name: An optional name for the interface. If not provided
+                          then either tag with a key "Name" or the interface ID
+                          will be used (whichever is available first in that
+                          order).
+        :type       name: ``str``
+
+        :rtype:     :class: `EC2NetworkInterface`
+        """
+
+        interface_id = findtext(element=element,
+                                xpath='networkInterfaceId',
+                                namespace=NAMESPACE)
+
+        state = findtext(element=element,
+                         xpath='status',
+                         namespace=NAMESPACE)
+
+        # Get tags
+        tags = self._get_resource_tags(element)
+
+        name = name if name else tags.get('Name', interface_id)
+
+        # Build security groups
+        groups = []
+        for item in findall(element=element,
+                            xpath='groupSet/item',
+                            namespace=NAMESPACE):
+
+            groups.append({'group_id': findtext(element=item,
+                                                xpath='groupId',
+                                                namespace=NAMESPACE),
+                           'group_name': findtext(element=item,
+                                                  xpath='groupName',
+                                                  namespace=NAMESPACE)})
+
+        # Build private IPs
+        priv_ips = []
+        for item in findall(element=element,
+                            xpath='privateIpAddressesSet/item',
+                            namespace=NAMESPACE):
+
+            priv_ips.append({'private_ip': findtext(element=item,
+                                                    xpath='privateIpAddress',
+                                                    namespace=NAMESPACE),
+                            'private_dns': findtext(element=item,
+                                                    xpath='privateDnsName',
+                                                    namespace=NAMESPACE),
+                            'primary': findtext(element=item,
+                                                xpath='primary',
+                                                namespace=NAMESPACE)})
+
+        # Build our attachment extra attributes map
+        attachment_attributes_map = {
+            'attachment_id': {
+                'xpath': 'attachment/attachmentId',
+                'transform_func': str
+            },
+            'instance_id': {
+                'xpath': 'attachment/instanceId',
+                'transform_func': str
+            },
+            'owner_id': {
+                'xpath': 'attachment/instanceOwnerId',
+                'transform_func': str
+            },
+            'device_index': {
+                'xpath': 'attachment/deviceIndex',
+                'transform_func': int
+            },
+            'status': {
+                'xpath': 'attachment/status',
+                'transform_func': str
+            },
+            'attach_time': {
+                'xpath': 'attachment/attachTime',
+                'transform_func': parse_date
+            },
+            'delete': {
+                'xpath': 'attachment/deleteOnTermination',
+                'transform_func': str
+            },
+        }
+
+        # Build our attachment dictionary which we will add into extra later
+        attachment = self._get_extra_dict(element, attachment_attributes_map)
+
+        # Build our extra attributes map
+        extra_attributes_map = {
+            'subnet_id': {
+                'xpath': 'subnetId',
+                'transform_func': str
+            },
+            'vpc_id': {
+                'xpath': 'vpcId',
+                'transform_func': str
+            },
+            'zone': {
+                'xpath': 'availabilityZone',
+                'transform_func': str
+            },
+            'description': {
+                'xpath': 'description',
+                'transform_func': str
+            },
+            'owner_id': {
+                'xpath': 'ownerId',
+                'transform_func': str
+            },
+            'mac_address': {
+                'xpath': 'macAddress',
+                'transform_func': str
+            },
+            'private_dns_name': {
+                'xpath': 'privateIpAddressesSet/privateDnsName',
+                'transform_func': str
+            },
+            'source_dest_check': {
+                'xpath': 'sourceDestCheck',
+                'transform_func': str
+            }
+        }
+
+        # Build our extra dict
+        extra = self._get_extra_dict(element, extra_attributes_map)
+
+        # Include our previously built items as well
+        extra['tags'] = tags
+        extra['attachment'] = attachment
+        extra['private_ips'] = priv_ips
+        extra['groups'] = groups
+
+        return EC2NetworkInterface(interface_id, name, state, extra=extra)
+
     def ex_list_reserved_nodes(self):
         """
         List all reserved instances/nodes which can be purchased from Amazon
@@ -2584,6 +2746,141 @@ class BaseEC2NodeDriver(NodeDriver):
         node_elastic_ips = self.ex_describe_addresses([node])
         return node_elastic_ips[node.id]
 
+    def ex_list_network_interfaces(self):
+        """
+        Return all network interfaces
+
+        :return:    List of EC2NetworkInterface instances
+        :rtype:     ``list`` of :class `EC2NetworkInterface`
+        """
+        params = {'Action': 'DescribeNetworkInterfaces'}
+
+        return self._to_interfaces(
+            self.connection.request(self.path, params=params).object
+        )
+
+    def ex_create_network_interface(self, subnet, name=None,
+                                    description=None,
+                                    private_ip_address=None):
+        """
+        Create a network interface within a VPC subnet.
+
+        :param      node: EC2NetworkSubnet instance
+        :type       node: :class:`EC2NetworkSubnet`
+
+        :param      name:  Optional name of the interface
+        :type       name:  ``str``
+
+        :param      description:  Optional description of the network interface
+        :type       description:  ``str``
+
+        :param      private_ip_address: Optional address to assign as the
+                                        primary private IP address of the
+                                        interface. If one is not provided then
+                                        Amazon will automatically auto-assign
+                                        an available IP. EC2 allows assignment
+                                        of multiple IPs, but this will be
+                                        the primary.
+        :type       private_ip_address: ``str``
+
+        :return:    EC2NetworkInterface instance
+        :rtype:     :class `EC2NetworkInterface`
+        """
+        params = {'Action': 'CreateNetworkInterface',
+                  'SubnetId': subnet.id}
+
+        if description:
+            params['Description'] = description
+
+        if private_ip_address:
+            params['PrivateIpAddress'] = private_ip_address
+
+        response = self.connection.request(self.path, params=params).object
+
+        element = response.findall(fixxpath(xpath='networkInterface',
+                                            namespace=NAMESPACE))[0]
+
+        interface = self._to_interface(element, name)
+
+        if name is not None:
+            tags = {'Name': name}
+            self.ex_create_tags(resource=interface, tags=tags)
+
+        return interface
+
+    def ex_delete_network_interface(self, network_interface):
+        """
+        Deletes a network interface.
+
+        :param      network_interface: EC2NetworkInterface instance
+        :type       network_interface: :class:`EC2NetworkInterface`
+
+        :rtype:     ``bool``
+        """
+        params = {'Action': 'DeleteNetworkInterface',
+                  'NetworkInterfaceId': network_interface.id}
+
+        result = self.connection.request(self.path, params=params).object
+        element = findtext(element=result, xpath='return',
+                           namespace=NAMESPACE)
+
+        return element == 'true'
+
+    def ex_attach_network_interface_to_node(self, network_interface,
+                                            node, device_index):
+        """
+        Attatch a network interface to an instance.
+
+        :param      network_interface: EC2NetworkInterface instance
+        :type       network_interface: :class:`EC2NetworkInterface`
+
+        :param      node: Node instance
+        :type       node: :class:`Node`
+
+        :param      device_index: The interface device index
+        :type       device_index: ``int``
+
+        :return:    String representation of the attachment id.
+                    This is required to detach the interface.
+        :rtype:     ``str``
+        """
+        params = {'Action': 'AttachNetworkInterface',
+                  'NetworkInterfaceId': network_interface.id,
+                  'InstanceId': node.id,
+                  'DeviceIndex': device_index}
+
+        response = self.connection.request(self.path, params=params).object
+        attachment_id = findattr(element=response, xpath='attachmentId',
+                                 namespace=NAMESPACE)
+
+        return attachment_id
+
+    def ex_detach_network_interface(self, attachment_id, force=False):
+        """
+        Detatch a network interface from an instance.
+
+        :param      attachment_id: The attachment ID associated with the
+                                   interface
+        :type       attachment_id: ``str``
+
+        :param      force: Forces the detachment.
+        :type       force: ``bool``
+
+        :return:    ``True`` on successful detachment, ``False`` otherwise.
+        :rtype:     ``bool``
+        """
+        params = {'Action': 'DetachNetworkInterface',
+                  'AttachmentId': attachment_id}
+
+        if force:
+            params['Force'] = True
+
+        result = self.connection.request(self.path, params=params).object
+
+        element = findtext(element=result, xpath='return',
+                           namespace=NAMESPACE)
+        return element == 'true'
+
     def ex_modify_instance_attribute(self, node, attributes):
         """
         Modify node attributes.

http://git-wip-us.apache.org/repos/asf/libcloud/blob/8c948876/libcloud/test/compute/fixtures/ec2/attach_network_interface.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/ec2/attach_network_interface.xml b/libcloud/test/compute/fixtures/ec2/attach_network_interface.xml
new file mode 100644
index 0000000..7b6baa8
--- /dev/null
+++ b/libcloud/test/compute/fixtures/ec2/attach_network_interface.xml
@@ -0,0 +1,4 @@
+<AttachNetworkInterfaceResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
+    <requestId>e46c7abc-8b14-4315-99cb-773a0f95d833</requestId>
+    <attachmentId>eni-attach-2b588b47</attachmentId>
+</AttachNetworkInterfaceResponse>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/8c948876/libcloud/test/compute/fixtures/ec2/create_network_interface.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/ec2/create_network_interface.xml b/libcloud/test/compute/fixtures/ec2/create_network_interface.xml
new file mode 100644
index 0000000..416477e
--- /dev/null
+++ b/libcloud/test/compute/fixtures/ec2/create_network_interface.xml
@@ -0,0 +1,30 @@
+<CreateNetworkInterfaceResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
+    <requestId>ca764ebe-8abc-4d37-9995-b9e88c086fa8</requestId>
+    <networkInterface>
+        <networkInterfaceId>eni-2b36086d</networkInterfaceId>
+        <subnetId>subnet-5ed9d432</subnetId>
+        <vpcId>vpc-62ded30e</vpcId>
+        <availabilityZone>us-east-1d</availabilityZone>
+        <description>My Test</description>
+        <ownerId>123456789098</ownerId>
+        <requesterManaged>false</requesterManaged>
+        <status>pending</status>
+        <macAddress>0e:bd:49:3e:11:74</macAddress>
+        <privateIpAddress>172.16.4.144</privateIpAddress>
+        <privateDnsName>ip-172-16-4-144.ec2.internal</privateDnsName>
+        <sourceDestCheck>true</sourceDestCheck>
+        <groupSet>
+            <item>
+                <groupId>sg-495a9926</groupId>
+                <groupName>default</groupName>
+            </item>
+        </groupSet>
+        <tagSet/>
+        <privateIpAddressesSet>
+            <item>
+                <privateIpAddress>172.16.4.144</privateIpAddress>
+                <primary>true</primary>
+            </item>
+        </privateIpAddressesSet>
+    </networkInterface>
+</CreateNetworkInterfaceResponse>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/8c948876/libcloud/test/compute/fixtures/ec2/delete_network_interface.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/ec2/delete_network_interface.xml b/libcloud/test/compute/fixtures/ec2/delete_network_interface.xml
new file mode 100644
index 0000000..07bc619
--- /dev/null
+++ b/libcloud/test/compute/fixtures/ec2/delete_network_interface.xml
@@ -0,0 +1,4 @@
+<DeleteNetworkInterfaceResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
+    <requestId>c0bc0036-e328-47c6-bd6d-318b007f66ee</requestId>
+    <return>true</return>
+</DeleteNetworkInterfaceResponse>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/8c948876/libcloud/test/compute/fixtures/ec2/describe_network_interfaces.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/ec2/describe_network_interfaces.xml b/libcloud/test/compute/fixtures/ec2/describe_network_interfaces.xml
new file mode 100644
index 0000000..f520877
--- /dev/null
+++ b/libcloud/test/compute/fixtures/ec2/describe_network_interfaces.xml
@@ -0,0 +1,83 @@
+<DescribeNetworkInterfacesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
+    <requestId>e8fc6c0b-d6f8-4b85-aa29-e6a097eb4631</requestId>
+    <networkInterfaceSet>
+        <item>
+            <networkInterfaceId>eni-18e6c05e</networkInterfaceId>
+            <subnetId>subnet-5ed9d432</subnetId>
+            <vpcId>vpc-62ded30e</vpcId>
+            <availabilityZone>us-east-1d</availabilityZone>
+            <description>Test Interface 1</description>
+            <ownerId>123456789098</ownerId>
+            <requesterManaged>false</requesterManaged>
+            <status>in-use</status>
+            <macAddress>0e:6e:df:72:78:af</macAddress>
+            <privateIpAddress>172.16.4.133</privateIpAddress>
+            <privateDnsName>ip-172-16-4-133.ec2.internal</privateDnsName>
+            <sourceDestCheck>true</sourceDestCheck>
+            <groupSet>
+                <item>
+                    <groupId>sg-495a9926</groupId>
+                    <groupName>default</groupName>
+                </item>
+            </groupSet>
+            <attachment>
+                <attachmentId>eni-attach-c87dd1a4</attachmentId>
+                <instanceId>i-caa71db1</instanceId>
+                <instanceOwnerId>123456789098</instanceOwnerId>
+                <deviceIndex>1</deviceIndex>
+                <status>attached</status>
+                <attachTime>2013-12-02T17:46:27.000Z</attachTime>
+                <deleteOnTermination>false</deleteOnTermination>
+            </attachment>
+            <tagSet/>
+            <privateIpAddressesSet>
+                <item>
+                    <privateIpAddress>172.16.4.133</privateIpAddress>
+                    <privateDnsName>ip-172-16-4-133.ec2.internal</privateDnsName>
+                    <primary>true</primary>
+                </item>
+            </privateIpAddressesSet>
+        </item>
+        <item>
+            <networkInterfaceId>eni-83e3c5c5</networkInterfaceId>
+            <subnetId>subnet-5ed9d432</subnetId>
+            <vpcId>vpc-62ded30e</vpcId>
+            <availabilityZone>us-east-1d</availabilityZone>
+            <description/>
+            <ownerId>123456789098</ownerId>
+            <requesterManaged>false</requesterManaged>
+            <status>in-use</status>
+            <macAddress>0e:93:0b:e9:e9:c4</macAddress>
+            <privateIpAddress>172.16.4.145</privateIpAddress>
+            <privateDnsName>ip-172-16-4-145.ec2.internal</privateDnsName>
+            <sourceDestCheck>true</sourceDestCheck>
+            <groupSet>
+                <item>
+                    <groupId>sg-13e4607c</groupId>
+                    <groupName>Test Group</groupName>
+                </item>
+                <item>
+                    <groupId>sg-495a9926</groupId>
+                    <groupName>default</groupName>
+                </item>
+            </groupSet>
+            <attachment>
+                <attachmentId>eni-attach-bae984d6</attachmentId>
+                <instanceId>i-caa71db1</instanceId>
+                <instanceOwnerId>123456789098</instanceOwnerId>
+                <deviceIndex>0</deviceIndex>
+                <status>attached</status>
+                <attachTime>2013-11-25T13:35:03.000Z</attachTime>
+                <deleteOnTermination>true</deleteOnTermination>
+            </attachment>
+            <tagSet/>
+            <privateIpAddressesSet>
+                <item>
+                    <privateIpAddress>172.16.4.145</privateIpAddress>
+                    <privateDnsName>ip-172-16-4-145.ec2.internal</privateDnsName>
+                    <primary>true</primary>
+                </item>
+            </privateIpAddressesSet>
+        </item>
+    </networkInterfaceSet>
+</DescribeNetworkInterfacesResponse>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/8c948876/libcloud/test/compute/fixtures/ec2/detach_network_interface.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/ec2/detach_network_interface.xml b/libcloud/test/compute/fixtures/ec2/detach_network_interface.xml
new file mode 100644
index 0000000..0f95a8b
--- /dev/null
+++ b/libcloud/test/compute/fixtures/ec2/detach_network_interface.xml
@@ -0,0 +1,4 @@
+<DetachNetworkInterfaceResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
+    <requestId>1a683cd6-58ea-4b93-a6e9-a23b56afddf0</requestId>
+    <return>true</return>
+</DetachNetworkInterfaceResponse>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/8c948876/libcloud/test/compute/test_ec2.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_ec2.py b/libcloud/test/compute/test_ec2.py
index b68f944..e8e4a14 100644
--- a/libcloud/test/compute/test_ec2.py
+++ b/libcloud/test/compute/test_ec2.py
@@ -868,6 +868,48 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin):
         resp = self.driver.ex_get_console_output(node)
         self.assertEqual('Test String', resp['output'])
 
+    def test_ex_list_network_interfaces(self):
+        interfaces = self.driver.ex_list_network_interfaces()
+
+        self.assertEqual(len(interfaces), 2)
+
+        self.assertEqual('eni-18e6c05e', interfaces[0].id)
+        self.assertEqual('in-use', interfaces[0].state)
+        self.assertEqual('0e:6e:df:72:78:af',
+                         interfaces[0].extra['mac_address'])
+
+        self.assertEqual('eni-83e3c5c5', interfaces[1].id)
+        self.assertEqual('in-use', interfaces[1].state)
+        self.assertEqual('0e:93:0b:e9:e9:c4',
+                         interfaces[1].extra['mac_address'])
+
+    def test_ex_create_network_interface(self):
+        subnet = self.driver.ex_list_subnets()[0]
+        interface = self.driver.ex_create_network_interface(
+            subnet,
+            name='Test Interface',
+            description='My Test')
+
+        self.assertEqual('eni-2b36086d', interface.id)
+        self.assertEqual('pending', interface.state)
+        self.assertEqual('0e:bd:49:3e:11:74', interface.extra['mac_address'])
+
+    def test_ex_delete_network_interface(self):
+        interface = self.driver.ex_list_network_interfaces()[0]
+        resp = self.driver.ex_delete_network_interface(interface)
+        self.assertTrue(resp)
+
+    def test_ex_attach_network_interface_to_node(self):
+        node = self.driver.list_nodes()[0]
+        interface = self.driver.ex_list_network_interfaces()[0]
+        resp = self.driver.ex_attach_network_interface_to_node(interface,
+                                                               node, 1)
+        self.assertTrue(resp)
+
+    def test_ex_detach_network_interface(self):
+        resp = self.driver.ex_detach_network_interface('eni-attach-2b588b47')
+        self.assertTrue(resp)
+
 
 class EC2USWest1Tests(EC2Tests):
     region = 'us-west-1'
@@ -1183,6 +1225,26 @@ class EC2MockHttp(MockHttpTestCase):
         body = self.fixtures.load('get_console_output.xml')
         return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
+    def _DescribeNetworkInterfaces(self, method, url, body, headers):
+        body = self.fixtures.load('describe_network_interfaces.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _CreateNetworkInterface(self, method, url, body, headers):
+        body = self.fixtures.load('create_network_interface.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _DeleteNetworkInterface(self, method, url, body, headers):
+        body = self.fixtures.load('delete_network_interface.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _AttachNetworkInterface(self, method, url, body, headers):
+        body = self.fixtures.load('attach_network_interface.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _DetachNetworkInterface(self, method, url, body, headers):
+        body = self.fixtures.load('detach_network_interface.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
 
 class EucMockHttp(EC2MockHttp):
     fixtures = ComputeFileFixtures('ec2')


Mime
View raw message