瀏覽代碼

Add NetworkService and Network classes

Enis Afgan 10 年之前
父節點
當前提交
602b1ab547

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

@@ -18,6 +18,7 @@ from cloudbridge.cloud.interfaces.resources import KeyPair
 from cloudbridge.cloud.interfaces.resources import LaunchConfig
 from cloudbridge.cloud.interfaces.resources import LaunchConfig
 from cloudbridge.cloud.interfaces.resources import MachineImage
 from cloudbridge.cloud.interfaces.resources import MachineImage
 from cloudbridge.cloud.interfaces.resources import MachineImageState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
+from cloudbridge.cloud.interfaces.resources import Network
 from cloudbridge.cloud.interfaces.resources import ObjectLifeCycleMixin
 from cloudbridge.cloud.interfaces.resources import ObjectLifeCycleMixin
 from cloudbridge.cloud.interfaces.resources import PageableObjectMixin
 from cloudbridge.cloud.interfaces.resources import PageableObjectMixin
 from cloudbridge.cloud.interfaces.resources import PlacementZone
 from cloudbridge.cloud.interfaces.resources import PlacementZone
@@ -586,3 +587,19 @@ class BaseBucket(BasePageableObjectMixin, Bucket, BaseCloudResource):
     def __repr__(self):
     def __repr__(self):
         return "<CB-{0}: {1}>".format(self.__class__.__name__,
         return "<CB-{0}: {1}>".format(self.__class__.__name__,
                                       self.name)
                                       self.name)
+
+
+class BaseNetwork(Network, BaseCloudResource):
+
+    def __init__(self, provider):
+        super(BaseNetwork, 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, Network) and
+                # pylint:disable=protected-access
+                self._provider == other._provider and
+                self.id == other.id)

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

@@ -8,6 +8,7 @@ from cloudbridge.cloud.interfaces.services import ImageService
 from cloudbridge.cloud.interfaces.services import InstanceService
 from cloudbridge.cloud.interfaces.services import InstanceService
 from cloudbridge.cloud.interfaces.services import InstanceTypesService
 from cloudbridge.cloud.interfaces.services import InstanceTypesService
 from cloudbridge.cloud.interfaces.services import KeyPairService
 from cloudbridge.cloud.interfaces.services import KeyPairService
+from cloudbridge.cloud.interfaces.services import NetworkService
 from cloudbridge.cloud.interfaces.services import ObjectStoreService
 from cloudbridge.cloud.interfaces.services import ObjectStoreService
 from cloudbridge.cloud.interfaces.services import RegionService
 from cloudbridge.cloud.interfaces.services import RegionService
 from cloudbridge.cloud.interfaces.services import SecurityGroupService
 from cloudbridge.cloud.interfaces.services import SecurityGroupService
@@ -131,3 +132,16 @@ class BaseRegionService(
 
 
     def __init__(self, provider):
     def __init__(self, provider):
         super(BaseRegionService, self).__init__(provider)
         super(BaseRegionService, self).__init__(provider)
+
+
+class BaseNetworkService(
+        BasePageableObjectMixin, NetworkService, BaseCloudService):
+
+    def __init__(self, provider):
+        super(BaseNetworkService, self).__init__(provider)
+
+    def delete(self, network_id):
+        network = self.get(network_id)
+        if network:
+            network.delete()
+        return True

+ 1 - 0
cloudbridge/cloud/interfaces/__init__.py

@@ -8,6 +8,7 @@ from .resources import InstanceState  # noqa
 from .resources import InvalidConfigurationException  # noqa
 from .resources import InvalidConfigurationException  # noqa
 from .resources import LaunchConfig  # noqa
 from .resources import LaunchConfig  # noqa
 from .resources import MachineImageState  # noqa
 from .resources import MachineImageState  # noqa
+from .resources import NetworkState  # noqa
 from .resources import Region  # noqa
 from .resources import Region  # noqa
 from .resources import SnapshotState  # noqa
 from .resources import SnapshotState  # noqa
 from .resources import VolumeState  # noqa
 from .resources import VolumeState  # noqa

+ 16 - 0
cloudbridge/cloud/interfaces/provider.py

@@ -110,6 +110,22 @@ class CloudProvider(object):
         """
         """
         pass
         pass
 
 
+    @abstractproperty
+    def network(self):
+        """
+        Provide access to all network related services in this provider.
+
+        Example:
+
+        .. code-block:: python
+
+            networks = provider.network.list()
+            network = provider.network.create(name="DevNet")
+
+        :rtype: :class:`.NetworkService`
+        :return:  a NetworkService object
+        """
+
     @abstractproperty
     @abstractproperty
     def security(self):
     def security(self):
         """
         """

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

@@ -781,6 +781,83 @@ class MachineImage(ObjectLifeCycleMixin, CloudResource):
         pass
         pass
 
 
 
 
+class NetworkState(object):
+
+    """
+    Standard states for a network.
+
+    :cvar UNKNOWN: Network state unknown.
+    :cvar PENDING: Network is being created.
+    :cvar AVAILABLE: Network is being available.
+    :cvar DOWN = Network is not operational.
+    :cvar ERROR = Network errored.
+    """
+    UNKNOWN = "unknown"
+    PENDING = "pending"
+    AVAILABLE = "available"
+    DOWN = "down"
+    ERROR = "error"
+
+
+class Network(CloudResource):
+    """
+    Represents a software-defined network, like the Virtual Private Cloud.
+    """
+    __metaclass__ = ABCMeta
+
+    @abstractproperty
+    def id(self):
+        """
+        Get the network identifier.
+
+        :rtype: ``str``
+        :return: ID for this network. Will generally correspond to the cloud
+                 middleware's ID, but should be treated as an opaque value.
+        """
+        pass
+
+    @abstractproperty
+    def name(self):
+        """
+        Get the network name.
+
+        :rtype: ``str``
+        :return: Name for this network as returned by the cloud middleware.
+        """
+        pass
+
+    @abstractproperty
+    def state(self):
+        """
+        The state of the network.
+
+        :rtype: ``str``
+        :return: One of ``unknown``, ``pending``, ``available``, ``down`` or
+                 ``error``.
+        """
+        pass
+
+    @abstractmethod
+    def delete(self):
+        """
+        Delete this network.
+
+        :rtype: ``bool``
+        :return: ``True`` is successful.
+        """
+        pass
+
+    @abstractmethod
+    def subnets(self):
+        """
+        The associated subnets.
+
+        :rtype: ``list`` of :class:`.Subnet`
+        :return: List of subnets associated with this network.
+        """
+        pass
+
+
 class VolumeState(object):
 class VolumeState(object):
 
 
     """
     """

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

@@ -473,6 +473,66 @@ class ImageService(PageableObjectMixin, CloudService):
         pass
         pass
 
 
 
 
+class NetworkService(PageableObjectMixin, CloudService):
+
+    """
+    Base interface for a Network Service.
+    """
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
+    def get(self, network_id):
+        """
+        Returns a Network given its ID or ``None`` if not found.
+
+        :type network_id: ``str``
+        :param network_id: The ID of the network to retrieve.
+
+        :rtype: ``object`` of :class:`.Network`
+        return: a Network object
+        """
+        pass
+
+    @abstractmethod
+    def list(self, limit=None, marker=None):
+        """
+        List all networks.
+
+        :rtype: ``list`` of :class:`.Network`
+        :return: list of Network objects
+        """
+        pass
+
+    @abstractmethod
+    def create(self, name=None):
+        """
+        Create a new network.
+
+        :type name: ``str``
+        :param name: An optional network name. The name will be set if the
+                     provider supports it.
+
+        :rtype: ``object`` of :class:`.Network`
+        :return:  A Network object
+        """
+        pass
+
+    @abstractmethod
+    def delete(self, network_id):
+        """
+        Delete an existing Network.
+
+        :type network_id: ``str``
+        :param network_id: The ID of the network to be deleted.
+
+        :rtype: ``bool``
+        :return:  ``True`` if the network does not exist, ``False`` otherwise.
+                  Note that this implies that the network may not have been
+                  deleted by this method but instead has not existed at all.
+        """
+        pass
+
+
 class ObjectStoreService(PageableObjectMixin, CloudService):
 class ObjectStoreService(PageableObjectMixin, CloudService):
 
 
     """
     """

+ 32 - 0
cloudbridge/cloud/providers/aws/provider.py

@@ -15,6 +15,7 @@ from cloudbridge.cloud.interfaces import TestMockHelperMixin
 
 
 from .services import AWSBlockStoreService
 from .services import AWSBlockStoreService
 from .services import AWSComputeService
 from .services import AWSComputeService
+from .services import AWSNetworkService
 from .services import AWSObjectStoreService
 from .services import AWSObjectStoreService
 from .services import AWSSecurityService
 from .services import AWSSecurityService
 
 
@@ -42,10 +43,12 @@ class AWSCloudProvider(BaseCloudProvider):
 
 
         # service connections, lazily initialized
         # service connections, lazily initialized
         self._ec2_conn = None
         self._ec2_conn = None
+        self._vpc_conn = None
         self._s3_conn = None
         self._s3_conn = None
 
 
         # Initialize provider services
         # Initialize provider services
         self._compute = AWSComputeService(self)
         self._compute = AWSComputeService(self)
+        self._network = AWSNetworkService(self)
         self._security = AWSSecurityService(self)
         self._security = AWSSecurityService(self)
         self._block_store = AWSBlockStoreService(self)
         self._block_store = AWSBlockStoreService(self)
         self._object_store = AWSObjectStoreService(self)
         self._object_store = AWSObjectStoreService(self)
@@ -56,6 +59,12 @@ class AWSCloudProvider(BaseCloudProvider):
             self._ec2_conn = self._connect_ec2()
             self._ec2_conn = self._connect_ec2()
         return self._ec2_conn
         return self._ec2_conn
 
 
+    @property
+    def vpc_conn(self):
+        if not self._vpc_conn:
+            self._vpc_conn = self._connect_vpc()
+        return self._vpc_conn
+
     @property
     @property
     def s3_conn(self):
     def s3_conn(self):
         if not self._s3_conn:
         if not self._s3_conn:
@@ -66,6 +75,10 @@ class AWSCloudProvider(BaseCloudProvider):
     def compute(self):
     def compute(self):
         return self._compute
         return self._compute
 
 
+    @property
+    def network(self):
+        return self._network
+
     @property
     @property
     def security(self):
     def security(self):
         return self._security
         return self._security
@@ -97,6 +110,25 @@ class AWSCloudProvider(BaseCloudProvider):
             debug=2 if self.config.debug_mode else 0)
             debug=2 if self.config.debug_mode else 0)
         return ec2_conn
         return ec2_conn
 
 
+    def _connect_vpc(self):
+        """
+        Get a boto VPC connection object.
+        """
+        r = RegionInfo(name=self.region_name, endpoint=self.region_endpoint)
+        vpc_conn = boto.connect_vpc(
+            aws_access_key_id=self.a_key,
+            aws_secret_access_key=self.s_key,
+            # api_version is needed for availability
+            # zone support for EC2
+            api_version='2012-06-01' if self.cloud_type == 'aws' else None,
+            is_secure=self.is_secure,
+            region=r,
+            port=self.ec2_port,
+            path=self.ec2_conn_path,
+            validate_certs=False,
+            debug=2 if self.config.debug_mode else 0)
+        return vpc_conn
+
     def _connect_s3(self):
     def _connect_s3(self):
         """
         """
         Get a boto S3 connection object.
         Get a boto S3 connection object.

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

@@ -13,6 +13,7 @@ from cloudbridge.cloud.base.resources import BaseInstance
 from cloudbridge.cloud.base.resources import BaseInstanceType
 from cloudbridge.cloud.base.resources import BaseInstanceType
 from cloudbridge.cloud.base.resources import BaseKeyPair
 from cloudbridge.cloud.base.resources import BaseKeyPair
 from cloudbridge.cloud.base.resources import BaseMachineImage
 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 BasePlacementZone
 from cloudbridge.cloud.base.resources import BaseRegion
 from cloudbridge.cloud.base.resources import BaseRegion
 from cloudbridge.cloud.base.resources import BaseSecurityGroup
 from cloudbridge.cloud.base.resources import BaseSecurityGroup
@@ -22,6 +23,7 @@ from cloudbridge.cloud.base.resources import BaseVolume
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
+from cloudbridge.cloud.interfaces.resources import NetworkState
 from cloudbridge.cloud.interfaces.resources import SnapshotState
 from cloudbridge.cloud.interfaces.resources import SnapshotState
 from cloudbridge.cloud.interfaces.resources import VolumeState
 from cloudbridge.cloud.interfaces.resources import VolumeState
 
 
@@ -703,3 +705,50 @@ class AWSRegion(BaseRegion):
         Accesss information about placement zones within this region.
         Accesss information about placement zones within this region.
         """
         """
         pass
         pass
+
+
+class AWSNetwork(BaseNetwork):
+
+    # Ref:
+    # docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html
+    _NETWORK_STATE_MAP = {
+        'pending': NetworkState.PENDING,
+        'available': VolumeState.AVAILABLE,
+    }
+
+    def __init__(self, provider, network):
+        super(AWSNetwork, self).__init__(provider)
+        self._vpc = network
+
+    @property
+    def id(self):
+        return self._vpc.id
+
+    @property
+    def name(self):
+        """
+        Get the network name.
+
+        .. note:: the network must have a (case sensitive) tag ``Name``
+        """
+        return self._vpc.tags.get('Name')
+
+    @name.setter
+    # pylint:disable=arguments-differ
+    def name(self, value):
+        """
+        Set the network name.
+        """
+        self._vpc.add_tag('Name', value)
+
+    @property
+    def state(self):
+        return AWSNetwork._NETWORK_STATE_MAP.get(
+            self._vpc.update(), NetworkState.UNKNOWN)
+
+    def delete(self):
+        return self._vpc.delete()
+
+    def subnets(self):
+        raise NotImplementedError(
+            'subnets not implemented by this provider.')

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

@@ -17,6 +17,7 @@ from cloudbridge.cloud.base.services import BaseImageService
 from cloudbridge.cloud.base.services import BaseInstanceService
 from cloudbridge.cloud.base.services import BaseInstanceService
 from cloudbridge.cloud.base.services import BaseInstanceTypesService
 from cloudbridge.cloud.base.services import BaseInstanceTypesService
 from cloudbridge.cloud.base.services import BaseKeyPairService
 from cloudbridge.cloud.base.services import BaseKeyPairService
+from cloudbridge.cloud.base.services import BaseNetworkService
 from cloudbridge.cloud.base.services import BaseObjectStoreService
 from cloudbridge.cloud.base.services import BaseObjectStoreService
 from cloudbridge.cloud.base.services import BaseRegionService
 from cloudbridge.cloud.base.services import BaseRegionService
 from cloudbridge.cloud.base.services import BaseSecurityGroupService
 from cloudbridge.cloud.base.services import BaseSecurityGroupService
@@ -28,6 +29,7 @@ from cloudbridge.cloud.interfaces.resources \
 from cloudbridge.cloud.interfaces.resources import InstanceType
 from cloudbridge.cloud.interfaces.resources import InstanceType
 from cloudbridge.cloud.interfaces.resources import KeyPair
 from cloudbridge.cloud.interfaces.resources import KeyPair
 from cloudbridge.cloud.interfaces.resources import MachineImage
 from cloudbridge.cloud.interfaces.resources import MachineImage
+# from cloudbridge.cloud.interfaces.resources import Network
 from cloudbridge.cloud.interfaces.resources import PlacementZone
 from cloudbridge.cloud.interfaces.resources import PlacementZone
 from cloudbridge.cloud.interfaces.resources import SecurityGroup
 from cloudbridge.cloud.interfaces.resources import SecurityGroup
 from cloudbridge.cloud.interfaces.resources import Snapshot
 from cloudbridge.cloud.interfaces.resources import Snapshot
@@ -38,6 +40,7 @@ from .resources import AWSInstance
 from .resources import AWSInstanceType
 from .resources import AWSInstanceType
 from .resources import AWSKeyPair
 from .resources import AWSKeyPair
 from .resources import AWSMachineImage
 from .resources import AWSMachineImage
+from .resources import AWSNetwork
 from .resources import AWSRegion
 from .resources import AWSRegion
 from .resources import AWSSecurityGroup
 from .resources import AWSSecurityGroup
 from .resources import AWSSnapshot
 from .resources import AWSSnapshot
@@ -627,3 +630,31 @@ class AWSRegionService(BaseRegionService):
                    for region in self.provider.ec2_conn.get_all_regions()]
                    for region in self.provider.ec2_conn.get_all_regions()]
         return ClientPagedResultList(self.provider, regions,
         return ClientPagedResultList(self.provider, regions,
                                      limit=limit, marker=marker)
                                      limit=limit, marker=marker)
+
+
+class AWSNetworkService(BaseNetworkService):
+
+    def __init__(self, provider):
+        super(AWSNetworkService, self).__init__(provider)
+
+    def get(self, network_id):
+        network = self.provider.vpc_conn.get_all_vpcs(vpc_ids=[network_id])
+        if network:
+            return AWSNetwork(self.provider, network[0])
+        return None
+
+    def list(self, limit=None, marker=None):
+        networks = [AWSNetwork(self.provider, network)
+                    for network in self.provider.vpc_conn.get_all_vpcs()]
+        return ClientPagedResultList(self.provider, networks,
+                                     limit=limit, marker=marker)
+
+    def create(self, name=None):
+        # AWS requried CIDR block to be specified when creating a network
+        # so set a default one and use the largest possible netmask.
+        default_cidr = '10.0.0.0/16'
+        network = self.provider.vpc_conn.create_vpc(cidr_block=default_cidr)
+        cb_network = AWSNetwork(self.provider, network)
+        if name:
+            cb_network.name = name
+        return cb_network

+ 6 - 0
cloudbridge/cloud/providers/openstack/provider.py

@@ -18,6 +18,7 @@ from cloudbridge.cloud.base import BaseCloudProvider
 
 
 from .services import OpenStackBlockStoreService
 from .services import OpenStackBlockStoreService
 from .services import OpenStackComputeService
 from .services import OpenStackComputeService
+from .services import OpenStackNetworkService
 from .services import OpenStackObjectStoreService
 from .services import OpenStackObjectStoreService
 from .services import OpenStackSecurityService
 from .services import OpenStackSecurityService
 
 
@@ -51,6 +52,7 @@ class OpenStackCloudProvider(BaseCloudProvider):
 
 
         # Initialize provider services
         # Initialize provider services
         self._compute = OpenStackComputeService(self)
         self._compute = OpenStackComputeService(self)
+        self._network = OpenStackNetworkService(self)
         self._security = OpenStackSecurityService(self)
         self._security = OpenStackSecurityService(self)
         self._block_store = OpenStackBlockStoreService(self)
         self._block_store = OpenStackBlockStoreService(self)
         self._object_store = OpenStackObjectStoreService(self)
         self._object_store = OpenStackObjectStoreService(self)
@@ -95,6 +97,10 @@ class OpenStackCloudProvider(BaseCloudProvider):
     def compute(self):
     def compute(self):
         return self._compute
         return self._compute
 
 
+    @property
+    def network(self):
+        return self._network
+
     @property
     @property
     def security(self):
     def security(self):
         return self._security
         return self._security

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

@@ -12,6 +12,7 @@ from cloudbridge.cloud.base.resources import BaseInstance
 from cloudbridge.cloud.base.resources import BaseInstanceType
 from cloudbridge.cloud.base.resources import BaseInstanceType
 from cloudbridge.cloud.base.resources import BaseKeyPair
 from cloudbridge.cloud.base.resources import BaseKeyPair
 from cloudbridge.cloud.base.resources import BaseMachineImage
 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 BasePlacementZone
 from cloudbridge.cloud.base.resources import BaseRegion
 from cloudbridge.cloud.base.resources import BaseRegion
 from cloudbridge.cloud.base.resources import BaseSecurityGroup
 from cloudbridge.cloud.base.resources import BaseSecurityGroup
@@ -20,6 +21,7 @@ from cloudbridge.cloud.base.resources import BaseSnapshot
 from cloudbridge.cloud.base.resources import BaseVolume
 from cloudbridge.cloud.base.resources import BaseVolume
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
+from cloudbridge.cloud.interfaces.resources import NetworkState
 from cloudbridge.cloud.interfaces.resources import SnapshotState
 from cloudbridge.cloud.interfaces.resources import SnapshotState
 from cloudbridge.cloud.interfaces.resources import VolumeState
 from cloudbridge.cloud.interfaces.resources import VolumeState
 from cloudbridge.cloud.providers.openstack import helpers as oshelpers
 from cloudbridge.cloud.providers.openstack import helpers as oshelpers
@@ -532,6 +534,51 @@ class OpenStackSnapshot(BaseSnapshot):
         return cb_vol
         return cb_vol
 
 
 
 
+class OpenStackNetwork(BaseNetwork):
+
+    # Ref: https://github.com/openstack/neutron/blob/master/neutron/plugins/
+    #      common/constants.py
+    _NETWORK_STATE_MAP = {
+        'PENDING_CREATE': NetworkState.PENDING,
+        'PENDING_UPDATE': NetworkState.PENDING,
+        'PENDING_DELETE': NetworkState.PENDING,
+        'CREATED': NetworkState.PENDING,
+        'INACTIVE': NetworkState.PENDING,
+        'DOWN': NetworkState.DOWN,
+        'ERROR': NetworkState.ERROR,
+        'ACTIVE': NetworkState.AVAILABLE
+    }
+
+    def __init__(self, provider, network):
+        super(OpenStackNetwork, self).__init__(provider)
+        self._network = network
+
+    @property
+    def id(self):
+        return self._network.get('id', None)
+
+    @property
+    def name(self):
+        return self._network.get('name', None)
+
+    @property
+    def state(self):
+        return OpenStackNetwork._NETWORK_STATE_MAP.get(
+            self._network.get('status', None),
+            NetworkState.UNKNOWN)
+
+    def delete(self):
+        self._provider.neutron.delete_network(self.id)
+        # Adhear to the interface docs
+        if self.id not in self._provider.neutron.list_networks():
+            return True
+        return False
+
+    def subnets(self):
+        raise NotImplementedError(
+            'subnets not implemented by this provider.')
+
+
 class OpenStackKeyPair(BaseKeyPair):
 class OpenStackKeyPair(BaseKeyPair):
 
 
     def __init__(self, provider, key_pair):
     def __init__(self, provider, key_pair):

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

@@ -15,6 +15,7 @@ from cloudbridge.cloud.base.services import BaseImageService
 from cloudbridge.cloud.base.services import BaseInstanceService
 from cloudbridge.cloud.base.services import BaseInstanceService
 from cloudbridge.cloud.base.services import BaseInstanceTypesService
 from cloudbridge.cloud.base.services import BaseInstanceTypesService
 from cloudbridge.cloud.base.services import BaseKeyPairService
 from cloudbridge.cloud.base.services import BaseKeyPairService
+from cloudbridge.cloud.base.services import BaseNetworkService
 from cloudbridge.cloud.base.services import BaseObjectStoreService
 from cloudbridge.cloud.base.services import BaseObjectStoreService
 from cloudbridge.cloud.base.services import BaseRegionService
 from cloudbridge.cloud.base.services import BaseRegionService
 from cloudbridge.cloud.base.services import BaseSecurityGroupService
 from cloudbridge.cloud.base.services import BaseSecurityGroupService
@@ -35,6 +36,7 @@ from .resources import OpenStackInstance
 from .resources import OpenStackInstanceType
 from .resources import OpenStackInstanceType
 from .resources import OpenStackKeyPair
 from .resources import OpenStackKeyPair
 from .resources import OpenStackMachineImage
 from .resources import OpenStackMachineImage
+from .resources import OpenStackNetwork
 from .resources import OpenStackRegion
 from .resources import OpenStackRegion
 from .resources import OpenStackSecurityGroup
 from .resources import OpenStackSecurityGroup
 from .resources import OpenStackSnapshot
 from .resources import OpenStackSnapshot
@@ -633,3 +635,25 @@ class OpenStackInstanceService(BaseInstanceService):
             return OpenStackInstance(self.provider, os_instance)
             return OpenStackInstance(self.provider, os_instance)
         except NovaNotFound:
         except NovaNotFound:
             return None
             return None
+
+
+class OpenStackNetworkService(BaseNetworkService):
+
+    def __init__(self, provider):
+        super(OpenStackNetworkService, self).__init__(provider)
+
+    def get(self, network_id):
+        network = (n for n in self.list() if n.id == network_id)
+        return next(network, None)
+
+    def list(self, limit=None, marker=None):
+        networks = [OpenStackNetwork(self.provider, network)
+                    for network in self.provider.neutron.list_networks()
+                    .get('networks', [])]
+        return ClientPagedResultList(self.provider, networks,
+                                     limit=limit, marker=marker)
+
+    def create(self, name=''):
+        net_info = {'name': name}
+        network = self.provider.neutron.create_network({'network': net_info})
+        return OpenStackNetwork(self.provider, network.get('network'))

+ 14 - 1
setup.py

@@ -1,5 +1,18 @@
+import ast
+import os
+import re
 from setuptools import setup, find_packages
 from setuptools import setup, find_packages
 
 
+# Cannot use "from cloudbridge import get_version" because that would try to
+# import the six package which may not be installed yet.
+reg = re.compile(r'__version__\s*=\s*(.+)')
+with open(os.path.join('cloudbridge', '__init__.py')) as f:
+    for line in f:
+        m = reg.match(line)
+        if m:
+            version = ast.literal_eval(m.group(1))
+            break
+
 base_reqs = ['bunch>=1.00', 'six>=1.9.0', 'retrying']
 base_reqs = ['bunch>=1.00', 'six>=1.9.0', 'retrying']
 openstack_reqs = ['python-novaclient', 'python-glanceclient',
 openstack_reqs = ['python-novaclient', 'python-glanceclient',
                   'python-cinderclient', 'python-swiftclient',
                   'python-cinderclient', 'python-swiftclient',
@@ -9,7 +22,7 @@ full_reqs = base_reqs + aws_reqs + openstack_reqs
 dev_reqs = ['tox', 'moto>=0.4.18', 'sphinx'] + full_reqs
 dev_reqs = ['tox', 'moto>=0.4.18', 'sphinx'] + full_reqs
 
 
 setup(name='cloudbridge',
 setup(name='cloudbridge',
-      version=0.1,
+      version=version,
       description='A simple layer of abstraction over multiple cloud'
       description='A simple layer of abstraction over multiple cloud'
       'providers.',
       'providers.',
       author='Galaxy and GVL Projects',
       author='Galaxy and GVL Projects',