Explorar el Código

Added ObjectLifeCycle interface to networks, subnets

Fixes: https://github.com/gvlproject/cloudbridge/issues/57
Nuwan Goonasekera hace 8 años
padre
commit
ded29485c7

+ 20 - 12
cloudbridge/cloud/base/resources.py

@@ -37,6 +37,7 @@ from cloudbridge.cloud.interfaces.resources import SecurityGroupRule
 from cloudbridge.cloud.interfaces.resources import Snapshot
 from cloudbridge.cloud.interfaces.resources import SnapshotState
 from cloudbridge.cloud.interfaces.resources import Subnet
+from cloudbridge.cloud.interfaces.resources import SubnetState
 from cloudbridge.cloud.interfaces.resources import Volume
 from cloudbridge.cloud.interfaces.resources import VolumeState
 
@@ -349,7 +350,7 @@ class BasePageableObjectMixin(PageableObjectMixin):
                 yield result
 
 
-class BaseInstanceType(InstanceType, BaseCloudResource):
+class BaseInstanceType(BaseCloudResource, InstanceType):
 
     def __init__(self, provider):
         super(BaseInstanceType, self).__init__(provider)
@@ -560,7 +561,7 @@ class BaseSnapshot(BaseCloudResource, BaseObjectLifeCycleMixin, Snapshot):
                                             self.name, self.id)
 
 
-class BaseKeyPair(KeyPair, BaseCloudResource):
+class BaseKeyPair(BaseCloudResource, KeyPair):
 
     def __init__(self, provider, key_pair):
         super(BaseKeyPair, self).__init__(provider)
@@ -601,7 +602,7 @@ class BaseKeyPair(KeyPair, BaseCloudResource):
         return "<CBKeyPair: {0}>".format(self.name)
 
 
-class BaseSecurityGroup(SecurityGroup, BaseCloudResource):
+class BaseSecurityGroup(BaseCloudResource, SecurityGroup):
 
     def __init__(self, provider, security_group):
         super(BaseSecurityGroup, self).__init__(provider)
@@ -655,7 +656,7 @@ class BaseSecurityGroup(SecurityGroup, BaseCloudResource):
                                             self.id, self.name)
 
 
-class BaseSecurityGroupRule(SecurityGroupRule, BaseCloudResource):
+class BaseSecurityGroupRule(BaseCloudResource, SecurityGroupRule):
 
     def __init__(self, provider, rule, parent):
         super(BaseSecurityGroupRule, self).__init__(provider)
@@ -688,7 +689,7 @@ class BaseSecurityGroupRule(SecurityGroupRule, BaseCloudResource):
                                              self.group))
 
 
-class BasePlacementZone(PlacementZone, BaseCloudResource):
+class BasePlacementZone(BaseCloudResource, PlacementZone):
 
     def __init__(self, provider):
         super(BasePlacementZone, self).__init__(provider)
@@ -704,7 +705,7 @@ class BasePlacementZone(PlacementZone, BaseCloudResource):
                 self.id == other.id)
 
 
-class BaseRegion(Region, BaseCloudResource):
+class BaseRegion(BaseCloudResource, Region):
 
     def __init__(self, provider):
         super(BaseRegion, self).__init__(provider)
@@ -726,7 +727,7 @@ class BaseRegion(Region, BaseCloudResource):
         return js
 
 
-class BaseBucketObject(BucketObject, BaseCloudResource):
+class BaseBucketObject(BaseCloudResource, BucketObject):
 
     def __init__(self, provider):
         super(BaseBucketObject, self).__init__(provider)
@@ -751,7 +752,7 @@ class BaseBucketObject(BucketObject, BaseCloudResource):
                                       self.name)
 
 
-class BaseBucket(BasePageableObjectMixin, Bucket, BaseCloudResource):
+class BaseBucket(BaseCloudResource, BasePageableObjectMixin, Bucket):
 
     def __init__(self, provider):
         super(BaseBucket, self).__init__(provider)
@@ -769,7 +770,7 @@ class BaseBucket(BasePageableObjectMixin, Bucket, BaseCloudResource):
                                       self.name)
 
 
-class BaseNetwork(BaseCloudResource, Network, BaseObjectLifeCycleMixin):
+class BaseNetwork(BaseCloudResource, BaseObjectLifeCycleMixin, Network):
 
     CB_DEFAULT_NETWORK_NAME = os.environ.get('CB_DEFAULT_NETWORK_NAME',
                                              'CloudBridgeNet')
@@ -795,7 +796,7 @@ class BaseNetwork(BaseCloudResource, Network, BaseObjectLifeCycleMixin):
                 self.id == other.id)
 
 
-class BaseSubnet(Subnet, BaseCloudResource):
+class BaseSubnet(BaseCloudResource, BaseObjectLifeCycleMixin, Subnet):
 
     CB_DEFAULT_SUBNET_NAME = os.environ.get('CB_DEFAULT_SUBNET_NAME',
                                             'CloudBridgeSubnet')
@@ -813,8 +814,15 @@ class BaseSubnet(Subnet, BaseCloudResource):
                 self._provider == other._provider and
                 self.id == other.id)
 
+    def wait_till_ready(self, timeout=None, interval=None):
+        self.wait_for(
+            [SubnetState.AVAILABLE],
+            terminal_states=[SubnetState.ERROR],
+            timeout=timeout,
+            interval=interval)
+
 
-class BaseFloatingIP(FloatingIP, BaseCloudResource):
+class BaseFloatingIP(BaseCloudResource, FloatingIP):
 
     def __init__(self, provider):
         super(BaseFloatingIP, self).__init__(provider)
@@ -830,7 +838,7 @@ class BaseFloatingIP(FloatingIP, BaseCloudResource):
                 self.id == other.id)
 
 
-class BaseRouter(Router, BaseCloudResource):
+class BaseRouter(BaseCloudResource, Router):
 
     CB_DEFAULT_ROUTER_NAME = os.environ.get('CB_DEFAULT_ROUTER_NAME',
                                             'CloudBridgeRouter')

+ 22 - 11
cloudbridge/cloud/interfaces/resources.py

@@ -784,7 +784,7 @@ class NetworkState(object):
 
     :cvar UNKNOWN: Network state unknown.
     :cvar PENDING: Network is being created.
-    :cvar AVAILABLE: Network is being available.
+    :cvar AVAILABLE: Network is available.
     :cvar DOWN = Network is not operational.
     :cvar ERROR = Network errored.
     """
@@ -795,7 +795,7 @@ class NetworkState(object):
     ERROR = "error"
 
 
-class Network(CloudResource):
+class Network(ObjectLifeCycleMixin, CloudResource):
     """
     Represents a software-defined network, like the Virtual Private Cloud.
     """
@@ -878,7 +878,25 @@ class Network(CloudResource):
         pass
 
 
-class Subnet(CloudResource):
+class SubnetState(object):
+
+    """
+    Standard states for a subnet.
+
+    :cvar UNKNOWN: Subnet state unknown.
+    :cvar PENDING: Subnet is being created.
+    :cvar AVAILABLE: Subnet is available.
+    :cvar DOWN = Subnet is not operational.
+    :cvar ERROR = Subnet errored.
+    """
+    UNKNOWN = "unknown"
+    PENDING = "pending"
+    AVAILABLE = "available"
+    DOWN = "down"
+    ERROR = "error"
+
+
+class Subnet(ObjectLifeCycleMixin, CloudResource):
     """
     Represents a subnet, as part of a Network.
     """
@@ -989,19 +1007,12 @@ class RouterState(object):
     DETACHED = "detached"
 
 
-class Router(CloudResource):
+class Router(ObjectLifeCycleMixin, CloudResource):
     """
     Represents a private network router.
     """
     __metaclass__ = ABCMeta
 
-    @abstractmethod
-    def refresh(self):
-        """
-        Update this object.
-        """
-        pass
-
     @abstractproperty
     def state(self):
         """

+ 28 - 2
cloudbridge/cloud/providers/aws/resources.py

@@ -35,6 +35,7 @@ from cloudbridge.cloud.interfaces.resources import NetworkState
 from cloudbridge.cloud.interfaces.resources import RouterState
 from cloudbridge.cloud.interfaces.resources import SecurityGroup
 from cloudbridge.cloud.interfaces.resources import SnapshotState
+from cloudbridge.cloud.interfaces.resources import SubnetState
 from cloudbridge.cloud.interfaces.resources import VolumeState
 
 from retrying import retry
@@ -1015,7 +1016,7 @@ class AWSNetwork(BaseNetwork):
     @property
     def state(self):
         return AWSNetwork._NETWORK_STATE_MAP.get(
-            self._vpc.update(), NetworkState.UNKNOWN)
+            self._vpc.state, NetworkState.UNKNOWN)
 
     @property
     def cidr_block(self):
@@ -1042,11 +1043,22 @@ class AWSNetwork(BaseNetwork):
         Refreshes the state of this instance by re-querying the cloud provider
         for its latest state.
         """
-        return self.state
+        try:
+            self._vpc.update(validate=True)
+        except (EC2ResponseError, ValueError):
+            # The network no longer exists and cannot be refreshed.
+            # set the status to unknown
+            self._vpc.state = 'unknown'
 
 
 class AWSSubnet(BaseSubnet):
 
+    # http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html
+    _SUBNET_STATE_MAP = {
+        'pending': SubnetState.PENDING,
+        'available': SubnetState.AVAILABLE,
+    }
+
     def __init__(self, provider, subnet):
         super(AWSSubnet, self).__init__(provider)
         self._subnet = subnet
@@ -1091,6 +1103,20 @@ class AWSSubnet(BaseSubnet):
     def delete(self):
         return self._provider.vpc_conn.delete_subnet(subnet_id=self.id)
 
+    @property
+    def state(self):
+        return self._SUBNET_STATE_MAP.get(
+            self._subnet.state, NetworkState.UNKNOWN)
+
+    def refresh(self):
+        subnet = self._provider.network.subnets.get(self.id)
+        if subnet:
+            # pylint:disable=protected-access
+            self._subnet = subnet._subnet
+        else:
+            # subnet no longer exists
+            self._subnet.state = "unknown"
+
 
 class AWSFloatingIP(BaseFloatingIP):
 

+ 24 - 2
cloudbridge/cloud/providers/openstack/resources.py

@@ -31,6 +31,7 @@ from cloudbridge.cloud.interfaces.resources import NetworkState
 from cloudbridge.cloud.interfaces.resources import RouterState
 from cloudbridge.cloud.interfaces.resources import SecurityGroup
 from cloudbridge.cloud.interfaces.resources import SnapshotState
+from cloudbridge.cloud.interfaces.resources import SubnetState
 from cloudbridge.cloud.interfaces.resources import VolumeState
 from cloudbridge.cloud.providers.openstack import helpers as oshelpers
 
@@ -755,8 +756,13 @@ class OpenStackNetwork(BaseNetwork):
 
     def refresh(self):
         """Refresh the state of this network by re-querying the provider."""
-        net = self._provider.neutron.list_networks(id=self.id).get('networks')
-        self._network = net[0] if net else {}
+        network = self._provider.network.get(self.id)
+        if network:
+            # pylint:disable=protected-access
+            self._network = network._network
+        else:
+            # subnet no longer exists
+            self._network.state = NetworkState.UNKNOWN
 
 
 class OpenStackSubnet(BaseSubnet):
@@ -764,6 +770,7 @@ class OpenStackSubnet(BaseSubnet):
     def __init__(self, provider, subnet):
         super(OpenStackSubnet, self).__init__(provider)
         self._subnet = subnet
+        self._state = None
 
     @property
     def id(self):
@@ -808,6 +815,21 @@ class OpenStackSubnet(BaseSubnet):
         if self.id not in str(self._provider.neutron.list_subnets()):
             return True
 
+    @property
+    def state(self):
+        return SubnetState.UNKNOWN if self._state == SubnetState.UNKNOWN \
+             else self.SubnetState.AVAILABLE
+
+    def refresh(self):
+        subnet = self._provider.network.subnets.get(self.id)
+        if subnet:
+            # pylint:disable=protected-access
+            self._subnet = subnet._subnet
+            self._state = SubnetState.AVAILABLE
+        else:
+            # subnet no longer exists
+            self._state = SubnetState.UNKNOWN
+
 
 class OpenStackFloatingIP(BaseFloatingIP):
 

+ 2 - 2
cloudbridge/cloud/providers/openstack/services.py

@@ -705,7 +705,7 @@ class OpenStackNetworkService(BaseNetworkService):
     def list(self, limit=None, marker=None):
         networks = [OpenStackNetwork(self.provider, network)
                     for network in self.provider.neutron.list_networks()
-                    .get('networks', [])]
+                    .get('networks') if network]
         return ClientPagedResultList(self.provider, networks,
                                      limit=limit, marker=marker)
 
@@ -713,7 +713,7 @@ class OpenStackNetworkService(BaseNetworkService):
         networks = [OpenStackNetwork(self.provider, network)
                     for network in self.provider.neutron.list_networks(
                         name=name)
-                    .get('networks', [])]
+                    .get('networks') if network]
         return ClientPagedResultList(self.provider, networks,
                                      limit=limit, marker=marker)