Browse Source

Fixed up networking implementation

Nuwan Goonasekera 8 years ago
parent
commit
691208a3f2

+ 5 - 1
cloudbridge/cloud/base/resources.py

@@ -819,6 +819,10 @@ class BaseNetwork(BaseCloudResource, BaseObjectLifeCycleMixin, Network):
             timeout=timeout,
             interval=interval)
 
+    def create_subnet(self, name, cidr_block, zone=None):
+        return self._provider.networking.subnets.create(name, self, cidr_block,
+                                                        zone=zone)
+
     def __eq__(self, other):
         return (isinstance(other, Network) and
                 # pylint:disable=protected-access
@@ -900,7 +904,7 @@ class BaseInternetGateway(BaseCloudResource, BaseObjectLifeCycleMixin,
         'CB_DEFAULT_INET_GATEWAY_NAME', 'CloudBridgeInetGateway')
 
     def __init__(self, provider):
-        super(BaseRouter, self).__init__(provider)
+        super(BaseInternetGateway, self).__init__(provider)
 
     def __repr__(self):
         return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__, self.id,

+ 16 - 19
cloudbridge/cloud/interfaces/resources.py

@@ -849,7 +849,7 @@ class Network(ObjectLifeCycleMixin, CloudResource):
         """
         pass
 
-    @abstractmethod
+    @abstractproperty
     def subnets(self):
         """
         The associated subnets.
@@ -1049,12 +1049,12 @@ class Router(CloudResource):
         pass
 
     @abstractmethod
-    def attach_network(self, network_id):
+    def attach_subnet(self, subnet):
         """
-        Attach this router to a network.
+        Attach this router to a subnet.
 
-        :type network_id: ``str``
-        :param network_id: The ID of a network to which to attach this router.
+        :type subnet: ``Subnet`` or ``str``
+        :param subnet: The subnet to which to attach this router.
 
         :rtype: ``bool``
         :return: ``True`` if successful.
@@ -1062,9 +1062,12 @@ class Router(CloudResource):
         pass
 
     @abstractmethod
-    def detach_network(self):
+    def detach_subnet(self, subnet):
         """
-        Detach this router from a network.
+        Detach this subnet from a network.
+
+        :type subnet: ``Subnet`` or ``str``
+        :param subnet: The subnet to detach from this router.
 
         :rtype: ``bool``
         :return: ``True`` if successful.
@@ -1072,15 +1075,12 @@ class Router(CloudResource):
         pass
 
     @abstractmethod
-    def add_route(self, subnet_id):
+    def attach_gateway(self, gateway):
         """
-        Add a route to this router.
+        Attach a gateway to this router.
 
-        Note that a router must be attached to a network (to which the supplied
-        subnet belongs to) before a route can be added.
-
-        :type subnet_id: ``str``
-        :param subnet_id: The ID of a subnet to add to this router.
+        :type gateway: ``Gateway``
+        :param gateway: The Gateway to attach to this router.
 
         :rtype: ``bool``
         :return: ``True`` if successful.
@@ -1088,12 +1088,9 @@ class Router(CloudResource):
         pass
 
     @abstractmethod
-    def remove_route(self, subnet_id):
+    def detach_gateway(self, gateway):
         """
-        Remove a route from this router.
-
-        :type subnet_id: ``str``
-        :param subnet_id: The ID of a subnet to remove to this router.
+        Detach this router from a gateway.
 
         :rtype: ``bool``
         :return: ``True`` if successful.

+ 32 - 37
cloudbridge/cloud/interfaces/services.py

@@ -593,12 +593,12 @@ class NetworkService(PageableObjectMixin, CloudService):
         pass
 
     @abstractmethod
-    def create(self, name=None):
+    def create(self, name):
         """
         Create a new network.
 
         :type name: ``str``
-        :param name: An optional network name. The name will be set if the
+        :param name: A network name. The name will be set if the
                      provider supports it.
 
         :rtype: ``object`` of :class:`.Network`
@@ -643,6 +643,16 @@ class NetworkService(PageableObjectMixin, CloudService):
         """
         pass
 
+    @abstractproperty
+    def routers(self):
+        """
+        Returns a list of routers connected to this network.
+
+        :rtype: ``list`` of :class: `Router`
+        :return: list of routers
+        """
+        pass
+
     @abstractmethod
     def floating_ips(self, network_id=None):
         """
@@ -670,30 +680,6 @@ class NetworkService(PageableObjectMixin, CloudService):
         """
         pass
 
-    @abstractmethod
-    def routers(self):
-        """
-        Get a list of available routers.
-
-        :rtype: ``list`` of :class: `Router`
-        :return: list of routers
-        """
-        pass
-
-    @abstractmethod
-    def create_router(self, name=None):
-        """
-        Create a new router.
-
-        :type name: ``str``
-        :param name: An optional router name. The name will be set if the
-                     provider supports it.
-
-        :rtype: :class:`Router`
-        :return: a newly created router object
-        """
-        pass
-
 
 class SubnetService(PageableObjectMixin, CloudService):
 
@@ -729,10 +715,14 @@ class SubnetService(PageableObjectMixin, CloudService):
         pass
 
     @abstractmethod
-    def create(self, network_id, cidr_block, name=None, zone=None):
+    def create(self, name, network_id, cidr_block, zone=None):
         """
         Create a new subnet within the supplied network.
 
+        :type name: ``str``
+        :param name: The subnet name. The name will be set if the
+                     provider supports it.
+
         :type network: :class:`.Network` object or ``str``
         :param network: Network object or ID under which to create the subnet.
 
@@ -740,10 +730,6 @@ class SubnetService(PageableObjectMixin, CloudService):
         :param cidr_block: CIDR block within the Network to assign to the
                            subnet.
 
-        :type name: ``str``
-        :param name: An optional subnet name. The name will be set if the
-                     provider supports it.
-
         :type zone: ``str``
         :param zone: An optional placement zone for the subnet. Some providers
                      may not support this, in which case the value is ignored.
@@ -819,6 +805,16 @@ class RouterService(PageableObjectMixin, CloudService):
         """
         pass
 
+    @abstractmethod
+    def find(self, name, limit=None, marker=None):
+        """
+        Searches for a router by a given list of attributes.
+
+        :rtype: List of ``object`` of :class:`.Router`
+        :return: A list of Router objects matching the supplied attributes.
+        """
+        pass
+
     @abstractmethod
     def create(self, network, name=None):
         """
@@ -843,11 +839,6 @@ class RouterService(PageableObjectMixin, CloudService):
 
         :type router: :class:`.Router` object or ``str``
         :param router: Router object or ID of the router to delete.
-
-        :rtype: ``bool``
-        :return:  ``True`` if the router does not exist, ``False`` otherwise.
-                  Note that this implies that the router may not have been
-                  deleted by this method but instead has not existed at all.
         """
         pass
 
@@ -860,7 +851,7 @@ class GatewayService(CloudService):
     __metaclass__ = ABCMeta
 
     @abstractmethod
-    def get_or_create_inet_gateway(self):
+    def get_or_create_inet_gateway(self, name):
         """
         Creates and returns a new internet gateway or returns an existing
         singleton gateway, depending on the cloud provider. The returned
@@ -871,6 +862,10 @@ class GatewayService(CloudService):
         others, it will result in a no-op if the cloud has only a single/public
         gateway.
 
+        :type  name: ``str``
+        :param name: The gateway name. The name will be set if the provider
+                     supports it.
+
         :rtype: ``object``  of :class:`.InternetGateway` or ``None``
         :return: an InternetGateway object of ``None`` if not found.
         """

+ 25 - 68
cloudbridge/cloud/providers/aws/resources.py

@@ -1027,19 +1027,12 @@ class AWSNetwork(BaseNetwork):
     def delete(self):
         return self._vpc.delete()
 
+    @property
     def subnets(self):
         flter = {'vpc-id': self.id}
         subnets = self._provider.vpc_conn.get_all_subnets(filters=flter)
         return [AWSSubnet(self._provider, subnet) for subnet in subnets]
 
-    def create_subnet(self, cidr_block, name=None, zone=None):
-        subnet = self._provider.vpc_conn.create_subnet(self.id, cidr_block,
-                                                       availability_zone=zone)
-        cb_subnet = AWSSubnet(self._provider, subnet)
-        if name:
-            cb_subnet.name = name
-        return cb_subnet
-
     def refresh(self):
         """
         Refreshes the state of this instance by re-querying the cloud provider
@@ -1147,32 +1140,14 @@ class AWSFloatingIP(BaseFloatingIP):
 
 class AWSRouter(BaseRouter):
 
-    def __init__(self, provider, router):
+    def __init__(self, provider, route_table):
         super(AWSRouter, self).__init__(provider)
-        self._router = router
+        self._route_table = route_table
         self._ROUTE_CIDR = '0.0.0.0/0'
 
-    def _route_table(self, subnet_id):
-        """
-        Get the route table for the VPC to which the supplied subnet belongs.
-
-        Note that only the first route table will be returned in case more
-        exist.
-
-        :type subnet_id: ``str``
-        :param subnet_id: Filter the route table by the network in which the
-                          given subnet belongs.
-
-        :rtype: :class:`boto.vpc.routetable.RouteTable`
-        :return: A RouteTable object.
-        """
-        sn = self._provider.vpc_conn.get_all_subnets([subnet_id])[0]
-        return self._provider.vpc_conn.get_all_route_tables(
-            filters={'vpc-id': sn.vpc_id})[0]
-
     @property
     def id(self):
-        return self._router.id
+        return self._route_table.id
 
     @property
     def name(self):
@@ -1181,7 +1156,7 @@ class AWSRouter(BaseRouter):
 
         .. note:: the router must have a (case sensitive) tag ``Name``
         """
-        return self._router.tags.get('Name')
+        return self._route_table.tags.get('Name')
 
     @name.setter
     # pylint:disable=arguments-differ
@@ -1190,63 +1165,45 @@ class AWSRouter(BaseRouter):
         Set the router name.
         """
         if self.is_valid_resource_name(value):
-            self._router.add_tag('Name', value)
+            self._route_table.add_tag('Name', value)
         else:
             raise InvalidNameException(value)
 
     def refresh(self):
-        self._router = self._provider.vpc_conn.get_all_internet_gateways(
+        self._route_table = self._provider.vpc_conn.get_all_route_tables(
             [self.id])[0]
 
     @property
     def state(self):
-        self.refresh()  # Explicitly refresh the local object
-        if self._router.attachments and \
-           self._router.attachments[0].state == 'available':
+        if self._route_table.associations:
             return RouterState.ATTACHED
         return RouterState.DETACHED
 
     @property
     def network_id(self):
-        if self.state == RouterState.ATTACHED:
-            return self._router.attachments[0].vpc_id
-        return None
+        return self._route_table.vpc_id
 
     def delete(self):
-        return self._provider._vpc_conn.delete_internet_gateway(self.id)
+        return self._provider.networking.routers.delete(self)
 
-    def attach_network(self, network_id):
-        return self._provider.vpc_conn.attach_internet_gateway(
-            self.id, network_id)
+    def attach_subnet(self, subnet):
+        subnet_id = subnet.id if isinstance(subnet, AWSSubnet) else subnet
+        self._provider.vpc_conn.associate_route_table(self.id, subnet_id)
 
-    def detach_network(self):
-        return self._provider.vpc_conn.detach_internet_gateway(
-            self.id, self.network_id)
-
-    def add_route(self, subnet_id):
-        """
-        Add a default route to this router.
-
-        For AWS, routes are added to a route table. A route table is assoc.
-        with a network vs. a subnet so we retrieve the network via the subnet.
-        Note that the subnet must belong to the same network as the router
-        is attached to.
-
-        Further, only a single route can be added, targeting the Internet
-        (i.e., destination CIDR block ``0.0.0.0/0``).
-        """
-        rt = self._route_table(subnet_id)
-        return self._provider.vpc_conn.create_route(
-            rt.id, self._ROUTE_CIDR, self.id)
+    def detach_subnet(self, subnet):
+        subnet_id = subnet.id if isinstance(subnet, AWSSubnet) else subnet
+        association_ids = [a for a in self._route_table.associations
+                           if a.subnet_id == subnet_id]
+        for a in association_ids:
+            self._provider.vpc_conn.disassociate_route_table(a)
 
-    def remove_route(self, subnet_id):
-        """
-        Remove the default Internet route from this router.
+    def attach_gateway(self, gateway):
+        return self._provider.vpc_conn.attach_internet_gateway(
+            gateway.id, self.network_id)
 
-        .. seealso:: ``add_route`` method
-        """
-        rt = self._route_table(subnet_id)
-        return self._provider.vpc_conn.delete_route(rt.id, self._ROUTE_CIDR)
+    def detach_gateway(self, gateway):
+        return self._provider.vpc_conn.detach_internet_gateway(
+            gateway.id, self.network_id)
 
 
 class AWSInternetGateway(BaseInternetGateway):

+ 12 - 5
cloudbridge/cloud/providers/aws/services.py

@@ -796,7 +796,7 @@ class AWSNetworkService(BaseNetworkService):
         return ClientPagedResultList(self.provider, networks,
                                      limit=limit, marker=marker)
 
-    def create(self, name=None):
+    def create(self, name):
         # AWS requires CIDR block to be specified when creating a network
         # so set a default one and use the largest allowed netmask.
         default_cidr = '10.0.0.0/16'
@@ -865,7 +865,7 @@ class AWSSubnetService(BaseSubnetService):
         return ClientPagedResultList(self.provider, subnets,
                                      limit=limit, marker=marker)
 
-    def create(self, network, cidr_block, name=None, zone=None):
+    def create(self, name, network, cidr_block, zone=None):
         network_id = network.id if isinstance(network, AWSNetwork) else network
         subnet = self.provider.vpc_conn.create_subnet(network_id, cidr_block,
                                                       availability_zone=zone)
@@ -895,8 +895,8 @@ class AWSSubnetService(BaseSubnetService):
             self.provider.vpc_conn.region.name)
         default_sn = None
         for i, z in enumerate(region.zones):
-            sn = self.create(default_net, '10.0.{0}.0/24'.format(i),
-                             AWSSubnet.CB_DEFAULT_SUBNET_NAME, z.name)
+            sn = self.create(AWSSubnet.CB_DEFAULT_SUBNET_NAME, default_net,
+                             '10.0.{0}.0/24'.format(i), z.name)
             if zone and zone == z.name:
                 default_sn = sn
         # No specific zone was supplied; return the last created subnet
@@ -927,13 +927,20 @@ class AWSRouterService(BaseRouterService):
                 return None
             raise ec2e
 
+    def find(self, name, limit=None, marker=None):
+        filtr = {'tag:Name': name}
+        routers = self.provider.vpc_conn.get_all_route_tables(filters=filtr)
+        aws_routers = [AWSRouter(self.provider, r) for r in routers]
+        return ClientPagedResultList(self.provider, aws_routers, limit=limit,
+                                     marker=marker)
+
     def list(self, limit=None, marker=None):
         routers = self.provider.vpc_conn.get_all_route_tables()
         aws_routers = [AWSRouter(self.provider, r) for r in routers]
         return ClientPagedResultList(self.provider, aws_routers, limit=limit,
                                      marker=marker)
 
-    def create(self, network, name=None):
+    def create(self, name, network):
         network_id = network.id if isinstance(network, AWSNetwork) else network
         router = self.provider.vpc_conn.create_route_table(vpc_id=network_id)
         cb_router = AWSRouter(self.provider, router)

+ 20 - 26
cloudbridge/cloud/providers/openstack/resources.py

@@ -744,19 +744,12 @@ class OpenStackNetwork(BaseNetwork):
         if self.id not in str(self._provider.neutron.list_networks()):
             return True
 
+    @property
     def subnets(self):
         subnets = (self._provider.neutron.list_subnets(network_id=self.id)
                    .get('subnets', []))
         return [OpenStackSubnet(self._provider, subnet) for subnet in subnets]
 
-    def create_subnet(self, cidr_block, name='', zone=None):
-        """OpenStack has no support for subnet zones so the value is ignored"""
-        subnet_info = {'name': name, 'network_id': self.id,
-                       'cidr': cidr_block, 'ip_version': 4}
-        subnet = (self._provider.neutron.create_subnet({'subnet': subnet_info})
-                  .get('subnet'))
-        return OpenStackSubnet(self._provider, subnet)
-
     def refresh(self):
         """Refresh the state of this network by re-querying the provider."""
         network = self._provider.networking.networks.get(self.id)
@@ -910,33 +903,34 @@ class OpenStackRouter(BaseRouter):
         if self.id not in str(self._provider.neutron.list_routers()):
             return True
 
-    def attach_network(self, network_id):
-        self._router = self._provider.neutron.add_gateway_router(
-            self.id, {'network_id': network_id}).get('router', self._router)
-        if self.network_id and self.network_id == network_id:
+    def attach_subnet(self, subnet):
+        router_interface = {'subnet_id': subnet.id}
+        ret = self._provider.neutron.add_interface_router(
+            self.id, router_interface)
+        if subnet.id in ret.get('subnet_ids', ""):
             return True
         return False
 
-    def detach_network(self):
-        self._router = self._provider.neutron.remove_gateway_router(
-            self.id).get('router', self._router)
-        if not self.network_id:
+    def detach_subnet(self, subnet):
+        router_interface = {'subnet_id': subnet.id}
+        ret = self._provider.neutron.remove_interface_router(
+            self.id, router_interface)
+        if subnet.id in ret.get('subnet_ids', ""):
             return True
         return False
 
-    def add_route(self, subnet_id):
-        router_interface = {'subnet_id': subnet_id}
-        ret = self._provider.neutron.add_interface_router(
-            self.id, router_interface)
-        if subnet_id in ret.get('subnet_ids', ""):
+    def attach_gateway(self, gateway):
+        self._router = self._provider.neutron.add_gateway_router(
+            self.id, {'network_id': self.network_id}).get('router',
+                                                          self._router)
+        if self.network_id and self.network_id == self.network_id:
             return True
         return False
 
-    def remove_route(self, subnet_id):
-        router_interface = {'subnet_id': subnet_id}
-        ret = self._provider.neutron.remove_interface_router(
-            self.id, router_interface)
-        if subnet_id in ret.get('subnet_ids', ""):
+    def detach_gateway(self, gateway):
+        self._router = self._provider.neutron.remove_gateway_router(
+            self.id).get('router', self._router)
+        if not self.network_id:
             return True
         return False
 

+ 7 - 6
cloudbridge/cloud/providers/openstack/services.py

@@ -765,7 +765,7 @@ class OpenStackNetworkService(BaseNetworkService):
         return ClientPagedResultList(self.provider, networks,
                                      limit=limit, marker=marker)
 
-    def create(self, name=''):
+    def create(self, name):
         net_info = {'name': name}
         network = self.provider.neutron.create_network({'network': net_info})
         return OpenStackNetwork(self.provider, network.get('network'))
@@ -822,7 +822,7 @@ class OpenStackSubnetService(BaseSubnetService):
         return ClientPagedResultList(self.provider, subnets,
                                      limit=limit, marker=marker)
 
-    def create(self, network, cidr_block, name='', zone=None):
+    def create(self, network, name, cidr_block, zone=None):
         """zone param is ignored."""
         network_id = (network.id if isinstance(network, OpenStackNetwork)
                       else network)
@@ -882,6 +882,11 @@ class OpenStackRouterService(BaseRouterService):
         return ClientPagedResultList(self.provider, os_routers, limit=limit,
                                      marker=marker)
 
+    def find(self, name, limit=None, marker=None):
+        aws_routers = [r for r in self if r.name == name]
+        return ClientPagedResultList(self.provider, aws_routers, limit=limit,
+                                     marker=marker)
+
     def create(self, network, name=None):
         """
         ``network`` is not used by OpenStack.
@@ -898,10 +903,6 @@ class OpenStackRouterService(BaseRouterService):
         router_id = (router.id if isinstance(router, OpenStackRouter)
                      else router)
         self.provider.neutron.delete_router(router_id)
-        # Adhere to the interface docs
-        if router_id not in self.list():
-            return True
-        return False
 
 
 class OpenStackGatewayService(BaseGatewayService):