Repository: libcloud
Updated Branches:
  refs/heads/trunk f8c1a1646 -> fcd353d51


[LIBCLOUD-574] Add methods for routes and route table management to the EC2 
driver.

Closes #313

Signed-off-by: Tomaz Muraus <[email protected]>


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

Branch: refs/heads/trunk
Commit: fcd353d51c9929e4849de47a26a2ab603d8f7216
Parents: f8c1a16
Author: Lior Goikhburg <[email protected]>
Authored: Thu Jun 5 21:28:02 2014 +0400
Committer: Tomaz Muraus <[email protected]>
Committed: Tue Jun 17 00:34:15 2014 +0200

----------------------------------------------------------------------
 CHANGES.rst                     |  10 +-
 libcloud/compute/drivers/ec2.py | 546 +++++++++++++++++++++++++++++++++++
 2 files changed, 555 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/fcd353d5/CHANGES.rst
----------------------------------------------------------------------
diff --git a/CHANGES.rst b/CHANGES.rst
index 8a4e152..0c8b306 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -218,11 +218,19 @@ Compute
   [LIBCLOUD-575, Zak Estrada]
 
 - Add support for network management for advanced zones
-  (ex_list_network_offerings, ex_create_network, ex_delete_network) in the 
+  (ex_list_network_offerings, ex_create_network, ex_delete_network) in the
   CloudStack driver.
   (GITHUB-316)
   [Roeland Kuipers]
 
+- Add extension methods for routes and route table management to the EC2
+  driver (ex_list_route_tables, ex_create_route_table, ex_delete_route_table,
+  ex_associate_route_table, ex_dissociate_route_table,
+  ex_replace_route_table_association, ex_create_route, ex_delete_route,
+  ex_replace_route)
+  (LIBCLOUD-574, GITHUB-313)
+  [Lior Goikhburg]
+
 Storage
 ~~~~~~~
 

http://git-wip-us.apache.org/repos/asf/libcloud/blob/fcd353d5/libcloud/compute/drivers/ec2.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py
index 5f9fb9b..9128c66 100644
--- a/libcloud/compute/drivers/ec2.py
+++ b/libcloud/compute/drivers/ec2.py
@@ -68,6 +68,9 @@ __all__ = [
     'EC2Network',
     'EC2NetworkSubnet',
     'EC2NetworkInterface',
+    'EC2RouteTable',
+    'EC2Route',
+    'EC2SubnetAssociation',
     'ExEC2AvailabilityZone',
 
     'IdempotentParamError'
@@ -1495,6 +1498,12 @@ RESOURCE_EXTRA_ATTRIBUTES_MAP = {
             'xpath': 'attachmentSet/item/deleteOnTermination',
             'transform_func': str
         }
+    },
+    'route_table': {
+        'vpc_id': {
+            'xpath': 'vpcId',
+            'transform_func': str
+        }
     }
 }
 
@@ -1730,6 +1739,124 @@ class VPCInternetGateway(object):
         return (('<VPCInternetGateway: id=%s>') % (self.id))
 
 
+class EC2RouteTable(object):
+    """
+    Class which stores information about VPC Route Tables.
+
+    Note: This class is VPC specific.
+    """
+
+    def __init__(self, id, routes, subnet_associations,
+                 propagating_gateway_ids, extra=None):
+        """
+        :param      id: The ID of the route table.
+        :type       id: ``str``
+
+        :param      routes: A list of routes in the route table.
+        :type       routes: ``list`` of :class:`EC2Route`
+
+        :param      subnet_associations: A list of associations between the
+                                         route table and one or more subnets.
+        :type       subnet_associations: ``list`` of
+                                         :class:`EC2SubnetAssociation`
+
+        :param      propagating_gateway_ids: The list of IDs of any virtual
+                                             private gateways propagating the
+                                             routes.
+        :type       propagating_gateway_ids: ``list``
+        """
+
+        self.id = id
+        self.routes = routes
+        self.subnet_associations = subnet_associations
+        self.propagating_gateway_ids = propagating_gateway_ids
+        self.extra = extra or {}
+
+    def __repr__(self):
+        return (('<EC2RouteTable: id=%s>') % (self.id))
+
+
+class EC2Route(object):
+    """
+    Class which stores information about a Route.
+
+    Note: This class is VPC specific.
+    """
+
+    def __init__(self, cidr, gateway_id, instance_id, owner_id,
+                 interface_id, state, origin, vpc_peering_connection_id):
+        """
+        :param      cidr: The CIDR block used for the destination match.
+        :type       cidr: ``str``
+
+        :param      gateway_id: The ID of a gateway attached to the VPC.
+        :type       gateway_id: ``str``
+
+        :param      instance_id: The ID of a NAT instance in the VPC.
+        :type       instance_id: ``str``
+
+        :param      owner_id: The AWS account ID of the owner of the instance.
+        :type       owner_id: ``str``
+
+        :param      interface_id: The ID of the network interface.
+        :type       interface_id: ``str``
+
+        :param      state: The state of the route (active | blackhole).
+        :type       state: ``str``
+
+        :param      origin: Describes how the route was created.
+        :type       origin: ``str``
+
+        :param      vpc_peering_connection_id: The ID of the VPC
+                                               peering connection.
+        :type       vpc_peering_connection_id: ``str``
+        """
+
+        self.cidr = cidr
+        self.gateway_id = gateway_id
+        self.instance_id = instance_id
+        self.owner_id = owner_id
+        self.interface_id = interface_id
+        self.state = state
+        self.origin = origin
+        self.vpc_peering_connection_id = vpc_peering_connection_id
+
+    def __repr__(self):
+        return (('<EC2Route: cidr=%s>') % (self.cidr))
+
+
+class EC2SubnetAssociation(object):
+    """
+    Class which stores information about Route Table associated with
+    a given Subnet in a VPC
+
+    Note: This class is VPC specific.
+    """
+
+    def __init__(self, id, route_table_id, subnet_id, main=False):
+        """
+        :param      id: The ID of the subent association in the VPC.
+        :type       id: ``str``
+
+        :param      route_table_id: The ID of a route table in the VPC.
+        :type       route_table_id: ``str``
+
+        :param      subnet_id: The ID of a subnet in the VPC.
+        :type       subnet_id: ``str``
+
+        :param      main: If true, means this is a main VPC route table.
+        :type       main: ``bool``
+        """
+
+        self.id = id
+        self.route_table_id = route_table_id
+        self.subnet_id = subnet_id
+        self.main = main
+
+    def __repr__(self):
+        return (('<EC2SubnetAssociation: id=%s>') % (self.id))
+
+
 class BaseEC2NodeDriver(NodeDriver):
     """
     Base Amazon EC2 node driver.
@@ -3927,6 +4054,306 @@ class BaseEC2NodeDriver(NodeDriver):
 
         return element == 'true'
 
+    def ex_list_route_tables(self, route_table_ids=None, filters=None):
+        """
+        Describes one or more of a VPC's route tables.
+        These are are used to determine where network traffic is directed.
+
+        :param      route_table_ids: Return only route tables matching the
+                                provided route table IDs. If not specified,
+                                a list of all the route tables in the
+                                corresponding region is returned.
+        :type       route_table_ids: ``list``
+
+        :param      filters: The filters so that the response includes
+                             information for only certain route tables.
+        :type       filters: ``dict``
+
+        :rtype: ``list`` of :class:`.EC2RouteTable`
+        """
+        params = {'Action': 'DescribeRouteTables'}
+
+        if route_table_ids:
+            for route_table_idx, route_table_id in enumerate(route_table_ids):
+                route_table_idx += 1  # We want 1-based indexes
+                route_table_key = 'RouteTableId.%s' % route_table_idx
+                params[route_table_key] = route_table_id
+
+        if filters:
+            params.update(self._build_filters(filters))
+
+        response = self.connection.request(self.path, params=params)
+
+        return self._to_route_tables(response.object)
+
+    def ex_create_route_table(self, network, name=None):
+        """
+        Create a route table within a VPC.
+
+        :param      vpc_id: The VPC that the subnet should be created in.
+        :type       vpc_id: :class:`.EC2Network`
+
+        :rtype:     :class: `.EC2RouteTable`
+        """
+        params = {'Action': 'CreateRouteTable',
+                  'VpcId': network.id}
+
+        response = self.connection.request(self.path, params=params).object
+        element = response.findall(fixxpath(xpath='routeTable',
+                                            namespace=NAMESPACE))[0]
+
+        route_table = self._to_route_table(element)
+
+        if name:
+            self.ex_create_tags(route_table, {'Name': name})
+
+        return route_table
+
+    def ex_delete_route_table(self, route_table):
+        """
+        Deletes a VPC route table.
+
+        :param      route_table: The route table to delete.
+        :type       route_table: :class:`.EC2RouteTable`
+
+        :rtype:     ``bool``
+        """
+
+        params = {'Action': 'DeleteRouteTable',
+                  'RouteTableId': route_table.id}
+
+        result = self.connection.request(self.path, params=params).object
+        element = findtext(element=result,
+                           xpath='return',
+                           namespace=NAMESPACE)
+
+        return element == 'true'
+
+    def ex_associate_route_table(self, route_table, subnet):
+        """
+        Associates a route table with a subnet within a VPC.
+
+        Note: A route table can be associated with multiple subnets.
+
+        :param      route_table: The route table to associate.
+        :type       route_table: :class:`.EC2RouteTable`
+
+        :param      subnet: The subnet to associate with.
+        :type       subnet: :class:`.EC2Subnet`
+
+        :return:    Route table association ID.
+        :rtype:     ``str``
+        """
+
+        params = {'Action': 'AssociateRouteTable',
+                  'RouteTableId': route_table.id,
+                  'SubnetId': subnet.id}
+
+        result = self.connection.request(self.path, params=params).object
+        association_id = findtext(element=result,
+                                  xpath='associationId',
+                                  namespace=NAMESPACE)
+
+        return association_id
+
+    def ex_dissociate_route_table(self, subnet_association):
+        """
+        Dissociates a subnet from a route table.
+
+        :param      subnet_association: The subnet association object or
+                                        subnet association ID.
+        :type       subnet_association: :class:`.EC2SubnetAssociation` or
+                                        ``str``
+
+        :rtype:     ``bool``
+        """
+
+        if isinstance(subnet_association, EC2SubnetAssociation):
+            subnet_association_id = subnet_association.id
+        else:
+            subnet_association_id = subnet_association
+
+        params = {'Action': 'DisassociateRouteTable',
+                  'AssociationId': subnet_association_id}
+
+        result = self.connection.request(self.path, params=params).object
+        element = findtext(element=result,
+                           xpath='return',
+                           namespace=NAMESPACE)
+
+        return element == 'true'
+
+    def ex_replace_route_table_association(self, subnet_association,
+                                           route_table):
+        """
+        Changes the route table associated with a given subnet in a VPC.
+
+        Note: This method can be used to change which table is the main route
+              table in the VPC (Specify the main route table's association ID
+              and the route table to be the new main route table).
+
+        :param      subnet_association: The subnet association object or
+                                        subnet association ID.
+        :type       subnet_association: :class:`.EC2SubnetAssociation` or
+                                        ``str``
+
+        :param      route_table: The new route table to associate.
+        :type       route_table: :class:`.EC2RouteTable`
+
+        :return:    New route table association ID.
+        :rtype:     ``str``
+        """
+
+        if isinstance(subnet_association, EC2SubnetAssociation):
+            subnet_association_id = subnet_association.id
+        else:
+            subnet_association_id = subnet_association
+
+        params = {'Action': 'ReplaceRouteTableAssociation',
+                  'AssociationId': subnet_association_id,
+                  'RouteTableId': route_table.id}
+
+        result = self.connection.request(self.path, params=params).object
+        new_association_id = findtext(element=result,
+                                      xpath='newAssociationId',
+                                      namespace=NAMESPACE)
+
+        return new_association_id
+
+    def ex_create_route(self, route_table, cidr,
+                        internet_gateway=None, node=None,
+                        network_interface=None, vpc_peering_connection=None):
+        """
+        Creates a route entry in the route table.
+
+        :param      route_table: The route table to create the route in.
+        :type       route_table: :class:`.EC2RouteTable`
+
+        :param      cidr: The CIDR block used for the destination match.
+        :type       cidr: ``str``
+
+        :param      internet_gateway: The internet gateway to route
+                                      traffic through.
+        :type       internet_gateway: :class:`.VPCInternetGateway`
+
+        :param      node: The NAT instance to route traffic through.
+        :type       node: :class:`Node`
+
+        :param      network_interface: The network interface of the node
+                                       to route traffic through.
+        :type       network_interface: :class:`.EC2NetworkInterface`
+
+        :param      vpc_peering_connection: The VPC peering connection.
+        :type       vpc_peering_connection: :class:`.VPCPeeringConnection`
+
+        :rtype:     ``bool``
+
+        Note: You must specify one of the following: internet_gateway,
+              node, network_interface, vpc_peering_connection.
+        """
+
+        params = {'Action': 'CreateRoute',
+                  'RouteTableId': route_table.id,
+                  'DestinationCidrBlock': cidr}
+
+        if internet_gateway:
+            params['GatewayId'] = internet_gateway.id
+
+        if node:
+            params['InstanceId'] = node.id
+
+        if network_interface:
+            params['NetworkInterfaceId'] = network_interface.id
+
+        if vpc_peering_connection:
+            params['VpcPeeringConnectionId'] = vpc_peering_connection.id
+
+        result = self.connection.request(self.path, params=params).object
+        element = findtext(element=result,
+                           xpath='return',
+                           namespace=NAMESPACE)
+
+        return element == 'true'
+
+    def ex_delete_route(self, route_table, cidr):
+        """
+        Deletes a route entry from the route table.
+
+        :param      route_table: The route table to delete the route from.
+        :type       route_table: :class:`.EC2RouteTable`
+
+        :param      cidr: The CIDR block used for the destination match.
+        :type       cidr: ``str``
+
+        :rtype:     ``bool``
+        """
+
+        params = {'Action': 'DeleteRoute',
+                  'RouteTableId': route_table.id,
+                  'DestinationCidrBlock': cidr}
+
+        result = self.connection.request(self.path, params=params).object
+        element = findtext(element=result,
+                           xpath='return',
+                           namespace=NAMESPACE)
+
+        return element == 'true'
+
+    def ex_replace_route(self, route_table, cidr,
+                         internet_gateway=None, node=None,
+                         network_interface=None, vpc_peering_connection=None):
+        """
+        Replaces an existing route entry within a route table in a VPC.
+
+        :param      route_table: The route table to replace the route in.
+        :type       route_table: :class:`.EC2RouteTable`
+
+        :param      cidr: The CIDR block used for the destination match.
+        :type       cidr: ``str``
+
+        :param      internet_gateway: The new internet gateway to route
+                                       traffic through.
+        :type       internet_gateway: :class:`.VPCInternetGateway`
+
+        :param      node: The new NAT instance to route traffic through.
+        :type       node: :class:`Node`
+
+        :param      network_interface: The new network interface of the node
+                                       to route traffic through.
+        :type       network_interface: :class:`.EC2NetworkInterface`
+
+        :param      vpc_peering_connection: The new VPC peering connection.
+        :type       vpc_peering_connection: :class:`.VPCPeeringConnection`
+
+        :rtype:     ``bool``
+
+        Note: You must specify one of the following: internet_gateway,
+              node, network_interface, vpc_peering_connection.
+        """
+
+        params = {'Action': 'ReplaceRoute',
+                  'RouteTableId': route_table.id,
+                  'DestinationCidrBlock': cidr}
+
+        if internet_gateway:
+            params['GatewayId'] = internet_gateway.id
+
+        if node:
+            params['InstanceId'] = node.id
+
+        if network_interface:
+            params['NetworkInterfaceId'] = network_interface.id
+
+        if vpc_peering_connection:
+            params['VpcPeeringConnectionId'] = vpc_peering_connection.id
+
+        result = self.connection.request(self.path, params=params).object
+        element = findtext(element=result,
+                           xpath='return',
+                           namespace=NAMESPACE)
+
+        return element == 'true'
+
     def _to_nodes(self, object, xpath):
         return [self._to_node(el)
                 for el in object.findall(fixxpath(xpath=xpath,
@@ -4451,6 +4878,125 @@ class BaseEC2NodeDriver(NodeDriver):
                                   state=state, driver=self.connection.driver,
                                   extra={'tags': tags})
 
+    def _to_route_tables(self, response):
+        return [self._to_route_table(el) for el in response.findall(
+            fixxpath(xpath='routeTableSet/item', namespace=NAMESPACE))
+        ]
+
+    def _to_route_table(self, element):
+        # route table id
+        route_table_id = findtext(element=element,
+                                  xpath='routeTableId',
+                                  namespace=NAMESPACE)
+
+        # Get our tags
+        tags = self._get_resource_tags(element)
+
+        # Get our extra dictionary
+        extra = self._get_extra_dict(
+            element, RESOURCE_EXTRA_ATTRIBUTES_MAP['route_table'])
+
+        # Add tags to the extra dict
+        extra['tags'] = tags
+
+        # Get routes
+        routes = self._to_routes(element, 'routeSet/item')
+
+        # Get subnet associations
+        subnet_associations = self._to_subnet_associations(
+            element, 'associationSet/item')
+
+        # Get propagating routes virtual private gateways (VGW) IDs
+        propagating_gateway_ids = []
+        for el in element.findall(fixxpath(xpath='propagatingVgwSet/item',
+                                           namespace=NAMESPACE)):
+            propagating_gateway_ids.append(findtext(element=el,
+                                                    xpath='gatewayId',
+                                                    namespace=NAMESPACE))
+
+        return EC2RouteTable(route_table_id, routes, subnet_associations,
+                             propagating_gateway_ids, extra=extra)
+
+    def _to_routes(self, element, xpath):
+        return [self._to_route(el) for el in element.findall(
+            fixxpath(xpath=xpath, namespace=NAMESPACE))
+        ]
+
+    def _to_route(self, element):
+        """
+        Parse the XML element and return a route object
+
+        :rtype:     :class: `EC2Route`
+        """
+
+        destination_cidr = findtext(element=element,
+                                    xpath='destinationCidrBlock',
+                                    namespace=NAMESPACE)
+
+        gateway_id = findtext(element=element,
+                              xpath='gatewayId',
+                              namespace=NAMESPACE)
+
+        instance_id = findtext(element=element,
+                               xpath='instanceId',
+                               namespace=NAMESPACE)
+
+        owner_id = findtext(element=element,
+                            xpath='instanceOwnerId',
+                            namespace=NAMESPACE)
+
+        interface_id = findtext(element=element,
+                                xpath='networkInterfaceId',
+                                namespace=NAMESPACE)
+
+        state = findtext(element=element,
+                         xpath='state',
+                         namespace=NAMESPACE)
+
+        origin = findtext(element=element,
+                          xpath='origin',
+                          namespace=NAMESPACE)
+
+        vpc_peering_connection_id = findtext(element=element,
+                                             xpath='vpcPeeringConnectionId',
+                                             namespace=NAMESPACE)
+
+        return EC2Route(destination_cidr, gateway_id, instance_id, owner_id,
+                        interface_id, state, origin, vpc_peering_connection_id)
+
+    def _to_subnet_associations(self, element, xpath):
+        return [self._to_subnet_association(el) for el in element.findall(
+            fixxpath(xpath=xpath, namespace=NAMESPACE))
+        ]
+
+    def _to_subnet_association(self, element):
+        """
+        Parse the XML element and return a route table association object
+
+        :rtype:     :class: `EC2SubnetAssociation`
+        """
+
+        association_id = findtext(element=element,
+                                  xpath='routeTableAssociationId',
+                                  namespace=NAMESPACE)
+
+        route_table_id = findtext(element=element,
+                                  xpath='routeTableId',
+                                  namespace=NAMESPACE)
+
+        subnet_id = findtext(element=element,
+                             xpath='subnetId',
+                             namespace=NAMESPACE)
+
+        main = findtext(element=element,
+                        xpath='main',
+                        namespace=NAMESPACE)
+
+        main = True if main else False
+
+        return EC2SubnetAssociation(association_id, route_table_id,
+                                    subnet_id, main)
+
     def _pathlist(self, key, arr):
         """
         Converts a key and an array of values into AWS query param format.

Reply via email to