Просмотр исходного кода

Add gateway service implementation

Nuwan Goonasekera 8 лет назад
Родитель
Сommit
d4fcace5bf

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

@@ -17,9 +17,11 @@ from cloudbridge.cloud.interfaces.resources import Bucket
 from cloudbridge.cloud.interfaces.resources import BucketObject
 from cloudbridge.cloud.interfaces.resources import CloudResource
 from cloudbridge.cloud.interfaces.resources import FloatingIP
+from cloudbridge.cloud.interfaces.resources import GatewayState
 from cloudbridge.cloud.interfaces.resources import Instance
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import InstanceType
+from cloudbridge.cloud.interfaces.resources import InternetGateway
 from cloudbridge.cloud.interfaces.resources import KeyPair
 from cloudbridge.cloud.interfaces.resources import LaunchConfig
 from cloudbridge.cloud.interfaces.resources import MachineImage
@@ -889,3 +891,30 @@ class BaseRouter(BaseCloudResource, Router):
                 # pylint:disable=protected-access
                 self._provider == other._provider and
                 self.id == other.id)
+
+
+class BaseInternetGateway(BaseCloudResource, BaseObjectLifeCycleMixin,
+                          InternetGateway):
+
+    CB_DEFAULT_INET_GATEWAY_NAME = os.environ.get(
+        'CB_DEFAULT_INET_GATEWAY_NAME', 'CloudBridgeInetGateway')
+
+    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, InternetGateway) and
+                # pylint:disable=protected-access
+                self._provider == other._provider and
+                self.id == other.id)
+
+    def wait_till_ready(self, timeout=None, interval=None):
+        self.wait_for(
+            [GatewayState.AVAILABLE],
+            terminal_states=[GatewayState.ERROR, GatewayState.UNKNOWN],
+            timeout=timeout,
+            interval=interval)

+ 8 - 0
cloudbridge/cloud/base/services.py

@@ -4,6 +4,7 @@ Base implementation for services available through a provider
 from cloudbridge.cloud.interfaces.services import BlockStoreService
 from cloudbridge.cloud.interfaces.services import CloudService
 from cloudbridge.cloud.interfaces.services import ComputeService
+from cloudbridge.cloud.interfaces.services import GatewayService
 from cloudbridge.cloud.interfaces.services import ImageService
 from cloudbridge.cloud.interfaces.services import InstanceService
 from cloudbridge.cloud.interfaces.services import InstanceTypesService
@@ -184,3 +185,10 @@ class BaseRouterService(
 
     def __init__(self, provider):
         super(BaseRouterService, self).__init__(provider)
+
+
+class BaseGatewayService(
+        GatewayService, BaseCloudService):
+
+    def __init__(self, provider):
+        super(BaseGatewayService, self).__init__(provider)

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

@@ -1101,6 +1101,55 @@ class Router(CloudResource):
         pass
 
 
+class GatewayState(object):
+
+    """
+    Standard states for a gateway.
+
+    :cvar UNKNOWN: Gateway state unknown.
+    :cvar CONFIGURING: Gateway is being configured
+    :cvar AVAILABLE: Gateway is ready
+    :cvar ERROR: Gateway is ready
+
+    """
+    UNKNOWN = "unknown"
+    CONFIGURING = "configuring"
+    AVAILABLE = "available"
+    ERROR = "error"
+
+
+class Gateway(CloudResource):
+    """
+    Represents a gateway resource.
+    """
+    __metaclass__ = ABCMeta
+
+    @abstractproperty
+    def network_id(self):
+        """
+        ID of the network to which the gateway is attached.
+
+        :rtype: ``str``
+        :return: ID for the attached network or ``None``.
+        """
+        pass
+
+    @abstractmethod
+    def delete(self):
+        """
+        Delete this gateway. On some providers, if the gateway
+        is public/a singleton, this operation will do nothing.
+        """
+        pass
+
+
+class InternetGateway(ObjectLifeCycleMixin, Gateway):
+    """
+    Represents an Internet gateway resource.
+    """
+    __metaclass__ = ABCMeta
+
+
 class AttachmentInfo(object):
     """
     Contains attachment information for a volume.

+ 52 - 5
cloudbridge/cloud/interfaces/services.py

@@ -540,6 +540,17 @@ class NetworkingService(CloudService):
         """
         pass
 
+    @abstractproperty
+    def gateways(self):
+        """
+        Provides access to all Gateway related services, such as
+        Internet Gateways.
+
+        :rtype: :class:`.GatewayService`
+        :return: a Router service object
+        """
+        pass
+
 
 class NetworkService(PageableObjectMixin, CloudService):
 
@@ -672,7 +683,7 @@ class NetworkService(PageableObjectMixin, CloudService):
     @abstractmethod
     def create_router(self, name=None):
         """
-        Create a new router/gateway.
+        Create a new router.
 
         :type name: ``str``
         :param name: An optional router name. The name will be set if the
@@ -781,7 +792,7 @@ class SubnetService(PageableObjectMixin, CloudService):
 class RouterService(PageableObjectMixin, CloudService):
 
     """
-    Manage networking router actions and resoruces.
+    Manage networking router actions and resources.
     """
     __metaclass__ = ABCMeta
 
@@ -793,8 +804,8 @@ class RouterService(PageableObjectMixin, CloudService):
         :type router_id: ``str``
         :param router_id: The ID of the router to retrieve.
 
-        :rtyoe: ``object``  of :class:`.Router` or ``None``
-        :return: a Router object ot ``None`` if not found.
+        :rtype: ``object``  of :class:`.Router` or ``None``
+        :return: a Router object of ``None`` if not found.
         """
         pass
 
@@ -817,7 +828,8 @@ class RouterService(PageableObjectMixin, CloudService):
         :param network: Network object or ID under which to create the router.
 
         :type name: ``str``
-        :param name: A router name. The name will be set if the provider supports it.
+        :param name: A router name. The name will be set if the provider
+                     supports it.
 
         :rtype: ``object`` of :class:`.Router`
         :return:  A Router object
@@ -840,6 +852,41 @@ class RouterService(PageableObjectMixin, CloudService):
         pass
 
 
+class GatewayService(CloudService):
+
+    """
+    Manage internet gateway resources.
+    """
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
+    def get_or_create_inet_gateway(self):
+        """
+        Creates and returns a new internet gateway or returns an existing
+        singleton gateway, depending on the cloud provider. The returned
+        gateway object can subsequently be attached to a router to provide
+        internet routing to a network. If the gateway is no longer required,
+        clients should call gateway.delete() to delete the gateway. On some
+        cloud providers this will result in the gateway being deleted. On
+        others, it will result in a no-op if the cloud has only a single/public
+        gateway.
+
+        :rtype: ``object``  of :class:`.InternetGateway` or ``None``
+        :return: an InternetGateway object of ``None`` if not found.
+        """
+        pass
+
+    @abstractmethod
+    def delete(self, gateway):
+        """
+        Delete a gateway.
+
+        :type gateway: :class:`.Gateway` object
+        :param gateway: Gateway object to delete.
+        """
+        pass
+
+
 class ObjectStoreService(PageableObjectMixin, CloudService):
 
     """

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

@@ -15,6 +15,7 @@ from cloudbridge.cloud.base.resources import BaseBucketObject
 from cloudbridge.cloud.base.resources import BaseFloatingIP
 from cloudbridge.cloud.base.resources import BaseInstance
 from cloudbridge.cloud.base.resources import BaseInstanceType
+from cloudbridge.cloud.base.resources import BaseInternetGateway
 from cloudbridge.cloud.base.resources import BaseKeyPair
 from cloudbridge.cloud.base.resources import BaseLaunchConfig
 from cloudbridge.cloud.base.resources import BaseMachineImage
@@ -29,6 +30,7 @@ from cloudbridge.cloud.base.resources import BaseSubnet
 from cloudbridge.cloud.base.resources import BaseVolume
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.interfaces.exceptions import InvalidNameException
+from cloudbridge.cloud.interfaces.resources import GatewayState
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
 from cloudbridge.cloud.interfaces.resources import NetworkState
@@ -1247,6 +1249,61 @@ class AWSRouter(BaseRouter):
         return self._provider.vpc_conn.delete_route(rt.id, self._ROUTE_CIDR)
 
 
+class AWSInternetGateway(BaseInternetGateway):
+
+    def __init__(self, provider, gateway):
+        super(AWSInternetGateway, self).__init__(provider)
+        self._gateway = gateway
+        self._gateway.state = ''
+
+    @property
+    def id(self):
+        return self._gateway.id
+
+    @property
+    def name(self):
+        """
+        Get the gateway name.
+
+        .. note:: the gateway must have a (case sensitive) tag ``Name``
+        """
+        return self._gateway.tags.get('Name')
+
+    @name.setter
+    # pylint:disable=arguments-differ
+    def name(self, value):
+        """
+        Set the router name.
+        """
+        if self.is_valid_resource_name(value):
+            self._gateway.add_tag('Name', value)
+        else:
+            raise InvalidNameException(value)
+
+    def refresh(self):
+        gateways = self._provider.vpc_conn.get_all_internet_gateways([self.id])
+        if gateways:
+            self._gateway = gateways[0]
+        else:
+            self._gateway.state = GatewayState.UNKNOWN
+
+    @property
+    def state(self):
+        if self._gateway.state == GatewayState.UNKNOWN:
+            return GatewayState.UNKNOWN
+        else:
+            return GatewayState.AVAILABLE
+
+    @property
+    def network_id(self):
+        if self._gateway.attachments:
+            return self._gateway.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):

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

@@ -10,6 +10,7 @@ from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.resources import ServerPagedResultList
 from cloudbridge.cloud.base.services import BaseBlockStoreService
 from cloudbridge.cloud.base.services import BaseComputeService
+from cloudbridge.cloud.base.services import BaseGatewayService
 from cloudbridge.cloud.base.services import BaseImageService
 from cloudbridge.cloud.base.services import BaseInstanceService
 from cloudbridge.cloud.base.services import BaseInstanceTypesService
@@ -43,6 +44,7 @@ from .resources import AWSBucket
 from .resources import AWSFloatingIP
 from .resources import AWSInstance
 from .resources import AWSInstanceType
+from .resources import AWSInternetGateway
 from .resources import AWSKeyPair
 from .resources import AWSLaunchConfig
 from .resources import AWSMachineImage
@@ -743,6 +745,7 @@ class AWSNetworkingService(BaseNetworkingService):
         self._network_service = AWSNetworkService(self.provider)
         self._subnet_service = AWSSubnetService(self.provider)
         self._router_service = AWSRouterService(self.provider)
+        self._gateway_service = AWSGatewayService(self.provider)
 
     @property
     def networks(self):
@@ -756,6 +759,10 @@ class AWSNetworkingService(BaseNetworkingService):
     def routers(self):
         return self._router_service
 
+    @property
+    def gateways(self):
+        return self._gateway_service
+
 
 class AWSNetworkService(BaseNetworkService):
 
@@ -938,3 +945,19 @@ class AWSRouterService(BaseRouterService):
     def delete(self, router):
         router_id = router.id if isinstance(router, AWSRouter) else router
         return self.provider.vpc_conn.delete_route_table(router_id)
+
+
+class AWSGatewayService(BaseGatewayService):
+
+    def __init__(self, provider):
+        super(AWSGatewayService, self).__init__(provider)
+
+    def get_or_create_inet_gateway(self, name):
+        gateway = self.provider.vpc_conn.create_internet_gateway()
+        cb_gateway = AWSInternetGateway(self.provider, gateway)
+        cb_gateway.wait_till_ready()
+        cb_gateway.name = name
+        return cb_gateway
+
+    def delete(self, gateway):
+        gateway.delete()

+ 54 - 0
cloudbridge/cloud/providers/openstack/resources.py

@@ -12,6 +12,7 @@ from cloudbridge.cloud.base.resources import BaseBucketObject
 from cloudbridge.cloud.base.resources import BaseFloatingIP
 from cloudbridge.cloud.base.resources import BaseInstance
 from cloudbridge.cloud.base.resources import BaseInstanceType
+from cloudbridge.cloud.base.resources import BaseInternetGateway
 from cloudbridge.cloud.base.resources import BaseKeyPair
 from cloudbridge.cloud.base.resources import BaseMachineImage
 from cloudbridge.cloud.base.resources import BaseNetwork
@@ -25,6 +26,7 @@ from cloudbridge.cloud.base.resources import BaseSubnet
 from cloudbridge.cloud.base.resources import BaseVolume
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.interfaces.exceptions import InvalidNameException
+from cloudbridge.cloud.interfaces.resources import GatewayState
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
 from cloudbridge.cloud.interfaces.resources import NetworkState
@@ -939,6 +941,58 @@ class OpenStackRouter(BaseRouter):
         return False
 
 
+class OpenStackInternetGateway(BaseInternetGateway):
+
+    GATEWAY_STATE_MAP = {
+        NetworkState.AVAILABLE: GatewayState.AVAILABLE,
+        NetworkState.DOWN: GatewayState.ERROR,
+        NetworkState.ERROR: GatewayState.ERROR,
+        NetworkState.PENDING: GatewayState.CONFIGURING,
+        NetworkState.UNKNOWN: GatewayState.UNKNOWN
+    }
+
+    def __init__(self, provider, gateway_net):
+        super(OpenStackInternetGateway, self).__init__(provider)
+        self._gateway_net = gateway_net
+
+    @property
+    def id(self):
+        return self._gateway_net.get('id', None)
+
+    @property
+    def name(self):
+        return self._gateway_net.get('name', None)
+
+    @name.setter
+    # pylint:disable=arguments-differ
+    def name(self, value):
+        if self.is_valid_resource_name(value):
+            self._provider.neutron.update_network(self.id,
+                                                  {'network': {'name': value}})
+            self.refresh()
+        else:
+            raise InvalidNameException(value)
+
+    def refresh(self):
+        """Refresh the state of this network by re-querying the provider."""
+        network = self._provider.networking.networks.get(self.id)
+        if network:
+            # pylint:disable=protected-access
+            self._gateway_net = network._network
+        else:
+            # subnet no longer exists
+            self._gateway_net.state = NetworkState.UNKNOWN
+
+    @property
+    def state(self):
+        return self.GATEWAY_STATE_MAP.get(
+            self._gateway_net.state, GatewayState.UNKNOWN)
+
+    def delete(self):
+        """Do nothing on openstack"""
+        pass
+
+
 class OpenStackKeyPair(BaseKeyPair):
 
     def __init__(self, provider, key_pair):

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

@@ -11,6 +11,7 @@ from cloudbridge.cloud.base.resources import BaseLaunchConfig
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.services import BaseBlockStoreService
 from cloudbridge.cloud.base.services import BaseComputeService
+from cloudbridge.cloud.base.services import BaseGatewayService
 from cloudbridge.cloud.base.services import BaseImageService
 from cloudbridge.cloud.base.services import BaseInstanceService
 from cloudbridge.cloud.base.services import BaseInstanceTypesService
@@ -43,6 +44,7 @@ from .resources import OpenStackBucket
 from .resources import OpenStackFloatingIP
 from .resources import OpenStackInstance
 from .resources import OpenStackInstanceType
+from .resources import OpenStackInternetGateway
 from .resources import OpenStackKeyPair
 from .resources import OpenStackMachineImage
 from .resources import OpenStackNetwork
@@ -719,6 +721,7 @@ class OpenStackNetworkingService(BaseNetworkingService):
         self._network_service = OpenStackNetworkService(self.provider)
         self._subnet_service = OpenStackSubnetService(self.provider)
         self._router_service = OpenStackRouterService(self.provider)
+        self._gateway_service = OpenStackGatewayService(self.provider)
 
     @property
     def networks(self):
@@ -732,6 +735,10 @@ class OpenStackNetworkingService(BaseNetworkingService):
     def routers(self):
         return self._router_service
 
+    @property
+    def gateways(self):
+        return self._gateway_service
+
 
 class OpenStackNetworkService(BaseNetworkService):
 
@@ -895,3 +902,18 @@ class OpenStackRouterService(BaseRouterService):
         if router_id not in self.list():
             return True
         return False
+
+
+class OpenStackGatewayService(BaseGatewayService):
+
+    def __init__(self, provider):
+        super(OpenStackGatewayService, self).__init__(provider)
+
+    def get_or_create_inet_gateway(self, name):
+        for n in self.provider.networking.networks:
+            if n.external:
+                return OpenStackInternetGateway(self.provider, n)
+        return None
+
+    def delete(self, gateway):
+        gateway.delete()