2
0
Эх сурвалжийг харах

Add a router object and basic controls for AWS and OpenStack providers.

Enis Afgan 9 жил өмнө
parent
commit
017c509332

+ 17 - 0
cloudbridge/cloud/base/resources.py

@@ -29,6 +29,7 @@ from cloudbridge.cloud.interfaces.resources import ObjectLifeCycleMixin
 from cloudbridge.cloud.interfaces.resources import PageableObjectMixin
 from cloudbridge.cloud.interfaces.resources import PlacementZone
 from cloudbridge.cloud.interfaces.resources import Region
+from cloudbridge.cloud.interfaces.resources import Router
 from cloudbridge.cloud.interfaces.resources import ResultList
 from cloudbridge.cloud.interfaces.resources import SecurityGroup
 from cloudbridge.cloud.interfaces.resources import SecurityGroupRule
@@ -690,3 +691,19 @@ class BaseFloatingIP(FloatingIP, BaseCloudResource):
                 # pylint:disable=protected-access
                 self._provider == other._provider and
                 self.id == other.id)
+
+
+class BaseRouter(Router, BaseCloudResource):
+
+    def __init__(self, provider):
+        super(BaseRouter, self).__init__(provider)
+
+    def __repr__(self):
+        return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__, self.id,
+                                            self.name)
+
+    def __eq__(self, other):
+        return (isinstance(other, Router) and
+                # pylint:disable=protected-access
+                self._provider == other._provider and
+                self.id == other.id)

+ 73 - 0
cloudbridge/cloud/interfaces/resources.py

@@ -1034,6 +1034,79 @@ class FloatingIP(CloudResource):
         pass
 
 
+class RouterState(object):
+
+    """
+    Standard states for a router.
+
+    :cvar UNKNOWN: Router state unknown.
+    :cvar ATTACHED: Router is attached to a network and should be operational.
+    :cvar DETACHED: Router is detached from a network.
+
+    """
+    UNKNOWN = "unknown"
+    ATTACHED = "attached"
+    DETACHED = "detached"
+
+
+class Router(CloudResource):
+    """
+    Represents a private network router.
+    """
+    __metaclass__ = ABCMeta
+
+    @abstractproperty
+    def id(self):
+        """
+        Get the router identifier.
+
+        :rtype: ``str``
+        :return: ID for this router. Will generally correspond to the cloud
+                 middleware's ID, but should be treated as an opaque value.
+        """
+        pass
+
+    @abstractproperty
+    def name(self):
+        """
+        Get the router name, if available.
+
+        :rtype: ``str``
+        :return: Name for this router.
+        """
+        pass
+
+    @abstractproperty
+    def state(self):
+        """
+        Router state: attached or detached to a network.
+
+        :rtype: ``str``
+        :return: ``attached`` or ``detached``.
+        """
+        pass
+
+    @abstractproperty
+    def network_id(self):
+        """
+        ID of the network to which the router is attached.
+
+        :rtype: ``str``
+        :return: ID for the attached network or ``None``.
+        """
+        pass
+
+    @abstractmethod
+    def delete(self):
+        """
+        Delete this router.
+
+        :rtype: ``bool``
+        :return: ``True`` if successful.
+        """
+        pass
+
+
 class AttachmentInfo(object):
     """
     Contains attachment information for a volume.

+ 24 - 0
cloudbridge/cloud/interfaces/services.py

@@ -582,6 +582,30 @@ 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/gateway.
+
+        :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):
 

+ 46 - 0
cloudbridge/cloud/providers/aws/resources.py

@@ -12,6 +12,7 @@ from cloudbridge.cloud.base.resources import BaseMachineImage
 from cloudbridge.cloud.base.resources import BaseNetwork
 from cloudbridge.cloud.base.resources import BasePlacementZone
 from cloudbridge.cloud.base.resources import BaseRegion
+from cloudbridge.cloud.base.resources import BaseRouter
 from cloudbridge.cloud.base.resources import BaseSecurityGroup
 from cloudbridge.cloud.base.resources import BaseSecurityGroupRule
 from cloudbridge.cloud.base.resources import BaseSnapshot
@@ -23,6 +24,7 @@ from cloudbridge.cloud.interfaces.resources import SecurityGroup
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
 from cloudbridge.cloud.interfaces.resources import NetworkState
+from cloudbridge.cloud.interfaces.resources import RouterState
 from cloudbridge.cloud.interfaces.resources import SnapshotState
 from cloudbridge.cloud.interfaces.resources import VolumeState
 from datetime import datetime
@@ -1019,6 +1021,50 @@ class AWSFloatingIP(BaseFloatingIP):
         return self._ip.delete()
 
 
+class AWSRouter(BaseRouter):
+
+    def __init__(self, provider, router):
+        super(AWSRouter, self).__init__(provider)
+        self._router = router
+
+    @property
+    def id(self):
+        return self._router.id
+
+    @property
+    def name(self):
+        """
+        Get the router name.
+
+        .. note:: the router must have a (case sensitive) tag ``Name``
+        """
+        return self._router.tags.get('Name')
+
+    @name.setter
+    # pylint:disable=arguments-differ
+    def name(self, value):
+        """
+        Set the router name.
+        """
+        self._router.add_tag('Name', value)
+
+    @property
+    def state(self):
+        if self._router.attachments and \
+           self._router.attachments[0].state == 'available':
+            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
+
+    def delete(self):
+        return self._provider._vpc_conn.delete_internet_gateway(self.id)
+
+
 class AWSLaunchConfig(BaseLaunchConfig):
 
     def __init__(self, provider):

+ 13 - 0
cloudbridge/cloud/providers/aws/services.py

@@ -46,6 +46,7 @@ from .resources import AWSLaunchConfig
 from .resources import AWSMachineImage
 from .resources import AWSNetwork
 from .resources import AWSRegion
+from .resources import AWSRouter
 from .resources import AWSSecurityGroup
 from .resources import AWSSnapshot
 from .resources import AWSSubnet
@@ -807,6 +808,18 @@ class AWSNetworkService(BaseNetworkService):
         ip = self.provider.ec2_conn.allocate_address(domain='vpc')
         return AWSFloatingIP(self.provider, ip)
 
+    def routers(self):
+        routers = self.provider.vpc_conn.get_all_internet_gateways()
+        return [AWSRouter(self.provider, r) for r in routers]
+
+    def create_router(self, name=None):
+        router = self.provider.vpc_conn.create_internet_gateway()
+        cb_router = AWSRouter(self.provider, router)
+        if name:
+            time.sleep(2)  # Some time is required
+            cb_router.name = name
+        return cb_router
+
 
 class AWSSubnetService(BaseSubnetService):
 

+ 37 - 1
cloudbridge/cloud/providers/openstack/resources.py

@@ -11,6 +11,7 @@ from cloudbridge.cloud.base.resources import BaseMachineImage
 from cloudbridge.cloud.base.resources import BaseNetwork
 from cloudbridge.cloud.base.resources import BasePlacementZone
 from cloudbridge.cloud.base.resources import BaseRegion
+from cloudbridge.cloud.base.resources import BaseRouter
 from cloudbridge.cloud.base.resources import BaseSecurityGroup
 from cloudbridge.cloud.base.resources import BaseSecurityGroupRule
 from cloudbridge.cloud.base.resources import BaseSnapshot
@@ -20,6 +21,7 @@ from cloudbridge.cloud.base.resources import BaseVolume
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
 from cloudbridge.cloud.interfaces.resources import NetworkState
+from cloudbridge.cloud.interfaces.resources import RouterState
 from cloudbridge.cloud.interfaces.resources import SnapshotState
 from cloudbridge.cloud.interfaces.resources import VolumeState
 from cloudbridge.cloud.interfaces.resources import SecurityGroup
@@ -758,6 +760,40 @@ class OpenStackFloatingIP(BaseFloatingIP):
             return True
 
 
+class OpenStackRouter(BaseRouter):
+
+    def __init__(self, provider, router):
+        super(OpenStackRouter, self).__init__(provider)
+        self._router = router
+
+    @property
+    def id(self):
+        return self._router.get('id', None)
+
+    @property
+    def name(self):
+        return self._router.get('name', None)
+
+    @property
+    def state(self):
+        if self._router.get('external_gateway_info'):
+            return RouterState.ATTACHED
+        return RouterState.DETACHED
+
+    @property
+    def network_id(self):
+        if self.state == RouterState.ATTACHED:
+            return self._router.get('external_gateway_info', {}).get(
+                'network_id', None)
+        return None
+
+    def delete(self):
+        self._provider.neutron.delete_router(self.id)
+        # Adhere to the interface docs
+        if self.id not in str(self._provider.neutron.list_routers()):
+            return True
+
+
 class OpenStackKeyPair(BaseKeyPair):
 
     def __init__(self, provider, key_pair):
@@ -819,7 +855,7 @@ class OpenStackSecurityGroup(BaseSecurityGroup):
         if src_group:
             if not isinstance(src_group, SecurityGroup):
                 src_group = self._provider.security.security_groups.get(
-                                src_group)
+                    src_group)
             for protocol in ['udp', 'tcp']:
                 existing_rule = self.get_rule(ip_protocol=ip_protocol,
                                               from_port=1,

+ 10 - 0
cloudbridge/cloud/providers/openstack/services.py

@@ -41,6 +41,7 @@ from .resources import OpenStackKeyPair
 from .resources import OpenStackMachineImage
 from .resources import OpenStackNetwork
 from .resources import OpenStackRegion
+from .resources import OpenStackRouter
 from .resources import OpenStackSecurityGroup
 from .resources import OpenStackSnapshot
 from .resources import OpenStackSubnet
@@ -746,6 +747,15 @@ class OpenStackNetworkService(BaseNetworkService):
         ip = self.provider.neutron.list_floatingips(id=ip.id)['floatingips'][0]
         return OpenStackFloatingIP(self.provider, ip)
 
+    def routers(self):
+        routers = self.provider.neutron.list_routers().get('routers')
+        return [OpenStackRouter(self.provider, r) for r in routers]
+
+    def create_router(self, name=None):
+        router = self.provider.neutron.create_router(
+            {'router': {'name': name}})
+        return OpenStackRouter(self.provider, router.get('router'))
+
 
 class OpenStackSubnetService(BaseSubnetService):