Sfoglia il codice sorgente

Resolving merge conflicts

almahmoud 7 anni fa
parent
commit
e2b81c9f24

+ 6 - 0
CHANGELOG.rst

@@ -1,3 +1,9 @@
+1.0.2 - September 25, 2018 (sha 621aeed1a8d7c5ad270649f8ee960e9682e57dae)
+-------
+* Added AWS instance types caching for better performance
+* Added ``router.subnets`` property
+* Ensure the default network for CloudBridge on AWS has subnets
+
 1.0.1 - September 7, 2018. (sha 3130492008c5e0e115b8dfec880d32a4ac90b761)
 1.0.1 - September 7, 2018. (sha 3130492008c5e0e115b8dfec880d32a4ac90b761)
 -------
 -------
 * Fixed minor bug when retrieving buckets with only limited access.
 * Fixed minor bug when retrieving buckets with only limited access.

+ 1 - 1
cloudbridge/__init__.py

@@ -2,7 +2,7 @@
 import logging
 import logging
 
 
 # Current version of the library
 # Current version of the library
-__version__ = '1.0.1'
+__version__ = '1.0.2'
 
 
 
 
 def get_version():
 def get_version():

+ 34 - 0
cloudbridge/cloud/base/helpers.py

@@ -1,4 +1,5 @@
 import fnmatch
 import fnmatch
+import functools
 import os
 import os
 import re
 import re
 import sys
 import sys
@@ -9,8 +10,12 @@ from cryptography.hazmat.backends import default_backend
 from cryptography.hazmat.primitives import serialization as crypt_serialization
 from cryptography.hazmat.primitives import serialization as crypt_serialization
 from cryptography.hazmat.primitives.asymmetric import rsa
 from cryptography.hazmat.primitives.asymmetric import rsa
 
 
+from deprecation import deprecated
+
 import six
 import six
 
 
+import cloudbridge
+
 
 
 def generate_key_pair():
 def generate_key_pair():
     """
     """
@@ -129,3 +134,32 @@ def get_env(varname, default_value=None):
             value, six.text_type):
             value, six.text_type):
         return six.u(value)
         return six.u(value)
     return value
     return value
+
+
+# Alias deprication decorator, following:
+# https://stackoverflow.com/questions/49802412/
+# how-to-implement-deprecation-in-python-with-argument-alias
+def deprecated_alias(**aliases):
+    def deco(f):
+        @functools.wraps(f)
+        def wrapper(*args, **kwargs):
+            rename_kwargs(f.__name__, kwargs, aliases)
+            return f(*args, **kwargs)
+        return wrapper
+    return deco
+
+
+def rename_kwargs(func_name, kwargs, aliases):
+    for alias, new in aliases.items():
+        if alias in kwargs:
+            if new in kwargs:
+                raise TypeError('{} received both {} and {}'.format(
+                    func_name, alias, new))
+            # Manually invoke the deprecated decorator with an empty lambda
+            # to signal deprecation
+            deprecated(deprecated_in='1.1',
+                       removed_in='2.0',
+                       current_version=cloudbridge.__version__,
+                       details='{} is deprecated, use {} instead'.format(
+                           alias, new))(lambda: None)()
+            kwargs[new] = kwargs.pop(alias)

+ 2 - 2
cloudbridge/cloud/base/resources.py

@@ -86,8 +86,8 @@ class BaseCloudResource(CloudResource):
             raise InvalidLabelException(
             raise InvalidLabelException(
                 u"Invalid label: %s. Label must be at least 3 characters long"
                 u"Invalid label: %s. Label must be at least 3 characters long"
                 " and at most 63 characters. It must consist of lowercase"
                 " and at most 63 characters. It must consist of lowercase"
-                " letters, numbers, or dashes. The label must not start or"
-                " end with a dash." % name)
+                " letters, numbers, or dashes. The label must start with a "
+                "letter and not end with a dash." % name)
 
 
     @staticmethod
     @staticmethod
     def assert_valid_resource_name(name):
     def assert_valid_resource_name(name):

+ 2 - 1
cloudbridge/cloud/factory.py

@@ -120,7 +120,8 @@ class CloudProviderFactory(object):
         cloud provider.
         cloud provider.
 
 
         :type name: str
         :type name: str
-        :param name: Cloud provider name: one of ``aws``, ``openstack``.
+        :param name: Cloud provider name: one of ``aws``, ``openstack``,
+        ``azure``.
 
 
         :type config: an object with required fields
         :type config: an object with required fields
         :param config: This can be a Bunch or any other object whose fields can
         :param config: This can be a Bunch or any other object whose fields can

+ 1 - 1
cloudbridge/cloud/interfaces/resources.py

@@ -731,7 +731,7 @@ class LaunchConfig(object):
         lc.add_block_device(...)
         lc.add_block_device(...)
 
 
         inst = provider.compute.instances.create(
         inst = provider.compute.instances.create(
-            'MyVM', image, vm_type, subnet, launch_config=lc)
+            'MyVM', image, vm_type, subnet, zone, launch_config=lc)
     """
     """
 
 
     @abstractmethod
     @abstractmethod

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

@@ -96,7 +96,7 @@ class ComputeService(CloudService):
 
 
             # launch a new instance
             # launch a new instance
             image = provider.compute.images.find(name='Ubuntu 16.04')[0]
             image = provider.compute.images.find(name='Ubuntu 16.04')[0]
-            size = provider.compute.vm_types.find(name='m1.small')
+            size = provider.compute.vm_types.find(name='m1.small')[0]
             instance = provider.compute.instances.create('Hello', image, size)
             instance = provider.compute.instances.create('Hello', image, size)
             print(instance.id, instance.label)
             print(instance.id, instance.label)
 
 
@@ -730,7 +730,7 @@ class SubnetService(PageableObjectMixin, CloudService):
         pass
         pass
 
 
     @abstractmethod
     @abstractmethod
-    def create(self, label, network_id, cidr_block, zone):
+    def create(self, label, network, cidr_block, zone):
         """
         """
         Create a new subnet within the supplied network.
         Create a new subnet within the supplied network.
 
 
@@ -1119,15 +1119,15 @@ class VMFirewallService(PageableObjectMixin, CloudService):
         pass
         pass
 
 
     @abstractmethod
     @abstractmethod
-    def create(self, label, network_id, description=None):
+    def create(self, label, network, description=None):
         """
         """
         Create a new VMFirewall.
         Create a new VMFirewall.
 
 
         :type label: str
         :type label: str
         :param label: The label for the new VM firewall.
         :param label: The label for the new VM firewall.
 
 
-        :type  network_id: ``str``
-        :param network_id: Network ID under which to create the VM firewall.
+        :type  network: ``str``
+        :param network: Network ID under which to create the VM firewall.
 
 
         :type description: str
         :type description: str
         :param description: The description of the new VM firewall.
         :param description: The description of the new VM firewall.

+ 3 - 2
cloudbridge/cloud/providers/aws/provider.py

@@ -2,6 +2,7 @@
 import logging as log
 import logging as log
 
 
 import boto3
 import boto3
+
 try:
 try:
     # These are installed only for the case of a dev instance
     # These are installed only for the case of a dev instance
     import responses
     import responses
@@ -105,9 +106,9 @@ class AWSCloudProvider(BaseCloudProvider):
         """
         """
         Get a boto ec2 connection object.
         Get a boto ec2 connection object.
         """
         """
-        return self._conect_ec2_region(region_name=self.region_name)
+        return self._connect_ec2_region(region_name=self.region_name)
 
 
-    def _conect_ec2_region(self, region_name=None):
+    def _connect_ec2_region(self, region_name=None):
         '''Get an EC2 resource object'''
         '''Get an EC2 resource object'''
         return self.session.resource(
         return self.session.resource(
             'ec2', region_name=region_name, **self.ec2_cfg)
             'ec2', region_name=region_name, **self.ec2_cfg)

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

@@ -952,7 +952,7 @@ class AWSRegion(BaseRegion):
             conn = self._provider.ec2_conn
             conn = self._provider.ec2_conn
         else:
         else:
             # pylint:disable=protected-access
             # pylint:disable=protected-access
-            conn = self._provider._conect_ec2_region(region_name=self.id)
+            conn = self._provider._connect_ec2_region(region_name=self.id)
 
 
         zones = (conn.meta.client.describe_availability_zones()
         zones = (conn.meta.client.describe_availability_zones()
                  .get('AvailabilityZones', []))
                  .get('AvailabilityZones', []))
@@ -1155,7 +1155,7 @@ class AWSFloatingIP(BaseFloatingIP):
 
 
     @property
     @property
     def in_use(self):
     def in_use(self):
-        return True if self._ip.instance_id else False
+        return True if self._ip.association_id else False
 
 
     def delete(self):
     def delete(self):
         self._ip.release()
         self._ip.release()

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

@@ -30,6 +30,7 @@ from cloudbridge.cloud.interfaces.exceptions \
     import DuplicateResourceException, InvalidConfigurationException
     import DuplicateResourceException, InvalidConfigurationException
 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 Snapshot
 from cloudbridge.cloud.interfaces.resources import Snapshot
 from cloudbridge.cloud.interfaces.resources import VMFirewall
 from cloudbridge.cloud.interfaces.resources import VMFirewall
@@ -134,12 +135,14 @@ class AWSVMFirewallService(BaseVMFirewallService):
     def list(self, limit=None, marker=None):
     def list(self, limit=None, marker=None):
         return self.svc.list(limit=limit, marker=marker)
         return self.svc.list(limit=limit, marker=marker)
 
 
-    def create(self, label, network_id, description=None):
+    @cb_helpers.deprecated_alias(network_id='network')
+    def create(self, label, network=None, description=None):
         log.debug("Creating Firewall Service with the parameters "
         log.debug("Creating Firewall Service with the parameters "
-                  "[label: %s id: %s description: %s]", label, network_id,
+                  "[label: %s id: %s description: %s]", label, network,
                   description)
                   description)
         AWSVMFirewall.assert_valid_resource_label(label)
         AWSVMFirewall.assert_valid_resource_label(label)
         name = AWSVMFirewall._generate_name_from_label(label, 'cb-fw')
         name = AWSVMFirewall._generate_name_from_label(label, 'cb-fw')
+        network_id = network.id if isinstance(network, Network) else network
         obj = self.svc.create('create_security_group', GroupName=name,
         obj = self.svc.create('create_security_group', GroupName=name,
                               Description=description or name,
                               Description=description or name,
                               VpcId=network_id)
                               VpcId=network_id)

+ 5 - 2
cloudbridge/cloud/providers/azure/azure_client.py

@@ -408,8 +408,11 @@ class AzureClient(object):
         return self.network_management_client.security_rules. \
         return self.network_management_client.security_rules. \
             delete(self.resource_group, vm_firewall, name).result()
             delete(self.resource_group, vm_firewall, name).result()
 
 
-    def list_containers(self, prefix=None):
-        return self.blob_service.list_containers(prefix=prefix)
+    def list_containers(self, prefix=None, limit=None, marker=None):
+        results = self.blob_service.list_containers(prefix=prefix,
+                                                    num_results=limit,
+                                                    marker=marker)
+        return (results.items, results.next_marker)
 
 
     def create_container(self, container_name):
     def create_container(self, container_name):
         try:
         try:

+ 23 - 2
cloudbridge/cloud/providers/azure/provider.py

@@ -1,10 +1,13 @@
 import logging
 import logging
 import uuid
 import uuid
 
 
+from deprecation import deprecated
+
 from msrestazure.azure_exceptions import CloudError
 from msrestazure.azure_exceptions import CloudError
 
 
 import tenacity
 import tenacity
 
 
+import cloudbridge
 from cloudbridge.cloud.base import BaseCloudProvider
 from cloudbridge.cloud.base import BaseCloudProvider
 from cloudbridge.cloud.base.helpers import get_env
 from cloudbridge.cloud.base.helpers import get_env
 from cloudbridge.cloud.interfaces.exceptions import ProviderConnectionException
 from cloudbridge.cloud.interfaces.exceptions import ProviderConnectionException
@@ -54,8 +57,9 @@ class AzureCloudProvider(BaseCloudProvider):
                                str(self.resource_group)))[-6:]))
                                str(self.resource_group)))[-6:]))
 
 
         self.vm_default_user_name = self._get_config_value(
         self.vm_default_user_name = self._get_config_value(
-            'azure_vm_default_user_name', get_env(
-                'AZURE_VM_DEFAULT_USER_NAME', 'cbuser'))
+                'azure_vm_default_username', get_env(
+                    'AZURE_VM_DEFAULT_USERNAME', None)) \
+            or self.__get_deprecated_username('cbuser')
 
 
         self.public_key_storage_table_name = self._get_config_value(
         self.public_key_storage_table_name = self._get_config_value(
             'azure_public_key_storage_table_name', get_env(
             'azure_public_key_storage_table_name', get_env(
@@ -68,6 +72,23 @@ class AzureCloudProvider(BaseCloudProvider):
         self._compute = AzureComputeService(self)
         self._compute = AzureComputeService(self)
         self._networking = AzureNetworkingService(self)
         self._networking = AzureNetworkingService(self)
 
 
+    def __get_deprecated_username(self, default):
+        username = self._get_config_value(
+            'azure_vm_default_user_name', get_env(
+                'AZURE_VM_DEFAULT_USER_NAME', None))
+        if username:
+            return self.__wrap_deprecated_username(username)
+        else:
+            return default
+
+    @deprecated(deprecated_in='1.1',
+                removed_in='2.0',
+                current_version=cloudbridge.__version__,
+                details='AZURE_VM_DEFAULT_USER_NAME was deprecated in favor '
+                        'of AZURE_VM_DEFAULT_USERNAME')
+    def __wrap_deprecated_username(self, username):
+        return username
+
     @property
     @property
     def compute(self):
     def compute(self):
         return self._compute
         return self._compute

+ 3 - 7
cloudbridge/cloud/providers/azure/resources.py

@@ -1124,7 +1124,7 @@ class AzureSubnet(BaseSubnet):
     def label(self):
     def label(self):
         # Although Subnet doesn't support labels, we use the parent Network's
         # Although Subnet doesn't support labels, we use the parent Network's
         # tags to track the subnet's labels
         # tags to track the subnet's labels
-        network = self._network
+        network = self.network
         az_network = network._network
         az_network = network._network
         return az_network.tags.get(self.tag_name, None)
         return az_network.tags.get(self.tag_name, None)
 
 
@@ -1132,7 +1132,7 @@ class AzureSubnet(BaseSubnet):
     # pylint:disable=arguments-differ
     # pylint:disable=arguments-differ
     def label(self, value):
     def label(self, value):
         self.assert_valid_resource_label(value)
         self.assert_valid_resource_label(value)
-        network = self._network
+        network = self.network
         az_network = network._network
         az_network = network._network
         kwargs = {self.tag_name: value or ""}
         kwargs = {self.tag_name: value or ""}
         az_network.tags.update(**kwargs)
         az_network.tags.update(**kwargs)
@@ -1153,7 +1153,7 @@ class AzureSubnet(BaseSubnet):
     def zone(self):
     def zone(self):
         # pylint:disable=protected-access
         # pylint:disable=protected-access
         region = self._provider.compute.regions.get(
         region = self._provider.compute.regions.get(
-            self._network._network.location)
+            self.network._network.location)
         return region.zones[0]
         return region.zones[0]
 
 
     @property
     @property
@@ -1164,10 +1164,6 @@ class AzureSubnet(BaseSubnet):
     def network_id(self):
     def network_id(self):
         return self._provider.azure_client.get_network_id_for_subnet(self.id)
         return self._provider.azure_client.get_network_id_for_subnet(self.id)
 
 
-    @property
-    def _network(self):
-        return self._provider.networking.networks.get(self.network_id)
-
     def delete(self):
     def delete(self):
         self._provider.azure_client.delete_subnet(self.id)
         self._provider.azure_client.delete_subnet(self.id)
 
 

+ 80 - 71
cloudbridge/cloud/providers/azure/services.py

@@ -48,76 +48,6 @@ class AzureSecurityService(BaseSecurityService):
         return self._vm_firewalls
         return self._vm_firewalls
 
 
 
 
-class AzureKeyPairService(BaseKeyPairService):
-    PARTITION_KEY = '00000000-0000-0000-0000-000000000000'
-
-    def __init__(self, provider):
-        super(AzureKeyPairService, self).__init__(provider)
-
-    def get(self, key_pair_id):
-        try:
-            key_pair = self.provider.azure_client.\
-                get_public_key(key_pair_id)
-
-            if key_pair:
-                return AzureKeyPair(self.provider, key_pair)
-            return None
-        except AzureException as error:
-            log.debug("KeyPair %s was not found.", key_pair_id)
-            log.debug(error)
-            return None
-
-    def list(self, limit=None, marker=None):
-        key_pairs, resume_marker = self.provider.azure_client.list_public_keys(
-            AzureKeyPairService.PARTITION_KEY, marker=marker,
-            limit=limit or self.provider.config.default_result_limit)
-        results = [AzureKeyPair(self.provider, key_pair)
-                   for key_pair in key_pairs]
-        return ServerPagedResultList(is_truncated=resume_marker,
-                                     marker=resume_marker,
-                                     supports_total=False,
-                                     data=results)
-
-    def find(self, **kwargs):
-        obj_list = self
-        filters = ['name']
-        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
-
-        # All kwargs should have been popped at this time.
-        if len(kwargs) > 0:
-            raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs,
-                                                           ", ".join(filters)))
-
-        return ClientPagedResultList(self.provider,
-                                     matches if matches else [])
-
-    def create(self, name, public_key_material=None):
-        AzureKeyPair.assert_valid_resource_name(name)
-
-        key_pair = self.get(name)
-
-        if key_pair:
-            raise DuplicateResourceException(
-                'Keypair already exists with name {0}'.format(name))
-
-        private_key = None
-        if not public_key_material:
-            public_key_material, private_key = cb_helpers.generate_key_pair()
-
-        entity = {
-            'PartitionKey': AzureKeyPairService.PARTITION_KEY,
-            'RowKey': str(uuid.uuid4()),
-            'Name': name,
-            'Key': public_key_material
-        }
-
-        self.provider.azure_client.create_public_key(entity)
-        key_pair = self.get(name)
-        key_pair.material = private_key
-        return key_pair
-
-
 class AzureVMFirewallService(BaseVMFirewallService):
 class AzureVMFirewallService(BaseVMFirewallService):
     def __init__(self, provider):
     def __init__(self, provider):
         super(AzureVMFirewallService, self).__init__(provider)
         super(AzureVMFirewallService, self).__init__(provider)
@@ -136,7 +66,8 @@ class AzureVMFirewallService(BaseVMFirewallService):
                for fw in self.provider.azure_client.list_vm_firewall()]
                for fw in self.provider.azure_client.list_vm_firewall()]
         return ClientPagedResultList(self.provider, fws, limit, marker)
         return ClientPagedResultList(self.provider, fws, limit, marker)
 
 
-    def create(self, label, description=None, network_id=None):
+    @cb_helpers.deprecated_alias(network_id='network')
+    def create(self, label, network=None, description=None):
         AzureVMFirewall.assert_valid_resource_label(label)
         AzureVMFirewall.assert_valid_resource_label(label)
         name = AzureVMFirewall._generate_name_from_label(label, "cb-fw")
         name = AzureVMFirewall._generate_name_from_label(label, "cb-fw")
         parameters = {"location": self.provider.region_name,
         parameters = {"location": self.provider.region_name,
@@ -194,6 +125,76 @@ class AzureVMFirewallService(BaseVMFirewallService):
         self.provider.azure_client.delete_vm_firewall(group_id)
         self.provider.azure_client.delete_vm_firewall(group_id)
 
 
 
 
+class AzureKeyPairService(BaseKeyPairService):
+    PARTITION_KEY = '00000000-0000-0000-0000-000000000000'
+
+    def __init__(self, provider):
+        super(AzureKeyPairService, self).__init__(provider)
+
+    def get(self, key_pair_id):
+        try:
+            key_pair = self.provider.azure_client.\
+                get_public_key(key_pair_id)
+
+            if key_pair:
+                return AzureKeyPair(self.provider, key_pair)
+            return None
+        except AzureException as error:
+            log.debug("KeyPair %s was not found.", key_pair_id)
+            log.debug(error)
+            return None
+
+    def list(self, limit=None, marker=None):
+        key_pairs, resume_marker = self.provider.azure_client.list_public_keys(
+            AzureKeyPairService.PARTITION_KEY, marker=marker,
+            limit=limit or self.provider.config.default_result_limit)
+        results = [AzureKeyPair(self.provider, key_pair)
+                   for key_pair in key_pairs]
+        return ServerPagedResultList(is_truncated=resume_marker,
+                                     marker=resume_marker,
+                                     supports_total=False,
+                                     data=results)
+
+    def find(self, **kwargs):
+        obj_list = self
+        filters = ['name']
+        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
+
+        # All kwargs should have been popped at this time.
+        if len(kwargs) > 0:
+            raise TypeError("Unrecognised parameters for search: %s."
+                            " Supported attributes: %s" % (kwargs,
+                                                           ", ".join(filters)))
+
+        return ClientPagedResultList(self.provider,
+                                     matches if matches else [])
+
+    def create(self, name, public_key_material=None):
+        AzureKeyPair.assert_valid_resource_name(name)
+
+        key_pair = self.get(name)
+
+        if key_pair:
+            raise DuplicateResourceException(
+                'Keypair already exists with name {0}'.format(name))
+
+        private_key = None
+        if not public_key_material:
+            public_key_material, private_key = cb_helpers.generate_key_pair()
+
+        entity = {
+            'PartitionKey': AzureKeyPairService.PARTITION_KEY,
+            'RowKey': str(uuid.uuid4()),
+            'Name': name,
+            'Key': public_key_material
+        }
+
+        self.provider.azure_client.create_public_key(entity)
+        key_pair = self.get(name)
+        key_pair.material = private_key
+        return key_pair
+
+
 class AzureStorageService(BaseStorageService):
 class AzureStorageService(BaseStorageService):
     def __init__(self, provider):
     def __init__(self, provider):
         super(AzureStorageService, self).__init__(provider)
         super(AzureStorageService, self).__init__(provider)
@@ -1016,6 +1017,14 @@ class AzureSubnetService(BaseSubnetService):
     def delete(self, subnet):
     def delete(self, subnet):
         subnet_id = subnet.id if isinstance(subnet, Subnet) else subnet
         subnet_id = subnet.id if isinstance(subnet, Subnet) else subnet
         self.provider.azure_client.delete_subnet(subnet_id)
         self.provider.azure_client.delete_subnet(subnet_id)
+        # Although Subnet doesn't support labels, we use the parent Network's
+        # tags to track the subnet's labels, thus that network-level tag must
+        # be deleted with the subnet
+        network = subnet.network
+        az_network = network._network
+        az_network.tags.pop(subnet.tag_name)
+        self._provider.azure_client.update_network_tags(
+            az_network.id, az_network)
 
 
 
 
 class AzureRouterService(BaseRouterService):
 class AzureRouterService(BaseRouterService):

+ 3 - 6
cloudbridge/cloud/providers/openstack/resources.py

@@ -453,12 +453,10 @@ class OpenStackInstance(BaseInstance):
         """
         """
         log.debug("Creating OpenStack Image with the label %s", label)
         log.debug("Creating OpenStack Image with the label %s", label)
         self.assert_valid_resource_label(label)
         self.assert_valid_resource_label(label)
-        name = self._generate_name_from_label(label, 'cb-img')
 
 
-        image_id = self._os_instance.create_image(name)
+        image_id = self._os_instance.create_image(label)
         img = OpenStackMachineImage(
         img = OpenStackMachineImage(
             self._provider, self._provider.compute.images.get(image_id))
             self._provider, self._provider.compute.images.get(image_id))
-        img.label = label
         return img
         return img
 
 
     def _get_fip(self, floating_ip):
     def _get_fip(self, floating_ip):
@@ -785,13 +783,12 @@ class OpenStackSnapshot(BaseSnapshot):
         Create a new Volume from this Snapshot.
         Create a new Volume from this Snapshot.
         """
         """
         vol_label = "from-snap-{0}".format(self.id or self.label)
         vol_label = "from-snap-{0}".format(self.id or self.label)
-        name = self._generate_name_from_label(vol_label, 'cb-vol')
+        self.assert_valid_resource_label(vol_label)
         size = size if size else self._snapshot.size
         size = size if size else self._snapshot.size
         os_vol = self._provider.cinder.volumes.create(
         os_vol = self._provider.cinder.volumes.create(
-            size, name=name, availability_zone=placement,
+            size, name=vol_label, availability_zone=placement,
             snapshot_id=self._snapshot.id)
             snapshot_id=self._snapshot.id)
         cb_vol = OpenStackVolume(self._provider, os_vol)
         cb_vol = OpenStackVolume(self._provider, os_vol)
-        cb_vol.label = vol_label
         return cb_vol
         return cb_vol
 
 
 
 

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

@@ -210,14 +210,14 @@ class OpenStackVMFirewallService(BaseVMFirewallService):
         return ClientPagedResultList(self.provider, firewalls,
         return ClientPagedResultList(self.provider, firewalls,
                                      limit=limit, marker=marker)
                                      limit=limit, marker=marker)
 
 
-    def create(self, label, description, network_id):
+    @cb_helpers.deprecated_alias(network_id='network')
+    def create(self, label, network, description=None):
         OpenStackVMFirewall.assert_valid_resource_label(label)
         OpenStackVMFirewall.assert_valid_resource_label(label)
-        name = OpenStackVMFirewall._generate_name_from_label(label, 'cb-fw')
         log.debug("Creating OpenStack VM Firewall with the params: "
         log.debug("Creating OpenStack VM Firewall with the params: "
                   "[label: %s network id: %s description: %s]", label,
                   "[label: %s network id: %s description: %s]", label,
-                  network_id, description)
+                  network, description)
         sg = self.provider.os_conn.network.create_security_group(
         sg = self.provider.os_conn.network.create_security_group(
-            name=name, description=description or name)
+            name=label, description=description or label)
         if sg:
         if sg:
             return OpenStackVMFirewall(self.provider, sg)
             return OpenStackVMFirewall(self.provider, sg)
         return None
         return None
@@ -857,15 +857,13 @@ class OpenStackSubnetService(BaseSubnetService):
                   "[Label: %s Network: %s Cinder Block: %s Zone: -ignored-]",
                   "[Label: %s Network: %s Cinder Block: %s Zone: -ignored-]",
                   label, network, cidr_block)
                   label, network, cidr_block)
         OpenStackSubnet.assert_valid_resource_label(label)
         OpenStackSubnet.assert_valid_resource_label(label)
-        name = OpenStackSubnet._generate_name_from_label(label, 'cb-subnet')
         network_id = (network.id if isinstance(network, OpenStackNetwork)
         network_id = (network.id if isinstance(network, OpenStackNetwork)
                       else network)
                       else network)
-        subnet_info = {'name': name, 'network_id': network_id,
+        subnet_info = {'name': label, 'network_id': network_id,
                        'cidr': cidr_block, 'ip_version': 4}
                        'cidr': cidr_block, 'ip_version': 4}
         subnet = (self.provider.neutron.create_subnet({'subnet': subnet_info})
         subnet = (self.provider.neutron.create_subnet({'subnet': subnet_info})
                   .get('subnet'))
                   .get('subnet'))
         cb_subnet = OpenStackSubnet(self.provider, subnet)
         cb_subnet = OpenStackSubnet(self.provider, subnet)
-        cb_subnet.label = label
         return cb_subnet
         return cb_subnet
 
 
     def get_or_create_default(self, zone):
     def get_or_create_default(self, zone):

+ 4 - 3
docs/getting_started.rst

@@ -103,7 +103,7 @@ on disk as a read-only file.
 .. code-block:: python
 .. code-block:: python
 
 
     import os
     import os
-    kp = provider.security.key_pairs.create('cloudbridge_intro')
+    kp = provider.security.key_pairs.create('cloudbridge-intro')
     with open('cloudbridge_intro.pem', 'w') as f:
     with open('cloudbridge_intro.pem', 'w') as f:
         f.write(kp.material)
         f.write(kp.material)
     os.chmod('cloudbridge_intro.pem', 0o400)
     os.chmod('cloudbridge_intro.pem', 0o400)
@@ -148,12 +148,13 @@ also add the network interface as a launch argument.
 .. code-block:: python
 .. code-block:: python
 
 
     img = provider.compute.images.get(image_id)
     img = provider.compute.images.get(image_id)
+    zone = provider.compute.regions.get(provider.region_name).zones[0]
     vm_type = sorted([t for t in provider.compute.vm_types
     vm_type = sorted([t for t in provider.compute.vm_types
                       if t.vcpus >= 2 and t.ram >= 4],
                       if t.vcpus >= 2 and t.ram >= 4],
                       key=lambda x: x.vcpus*x.ram)[0]
                       key=lambda x: x.vcpus*x.ram)[0]
     inst = provider.compute.instances.create(
     inst = provider.compute.instances.create(
         image=img, vm_type=vm_type, label='cloudbridge-intro',
         image=img, vm_type=vm_type, label='cloudbridge-intro',
-        subnet=sn, key_pair=kp, vm_firewalls=[fw])
+        subnet=sn, zone=zone, key_pair=kp, vm_firewalls=[fw])
     # Wait until ready
     # Wait until ready
     inst.wait_till_ready()  # This is a blocking call
     inst.wait_till_ready()  # This is a blocking call
     # Show instance state
     # Show instance state
@@ -205,7 +206,7 @@ their provider mappings, see :doc:`topics/resource_types_and_mappings`.
 
 
     # Key Pair
     # Key Pair
     kp = provider.security.key_pairs.get('keypair ID')
     kp = provider.security.key_pairs.get('keypair ID')
-    kp_list = provider.security.key_pairs.find(name='cloudbridge_intro')
+    kp_list = provider.security.key_pairs.find(name='cloudbridge-intro')
     kp = kp_list[0]
     kp = kp_list[0]
 
 
     # Floating IPs
     # Floating IPs

+ 1 - 1
docs/topics/design_decisions.rst

@@ -15,7 +15,7 @@ Require zone parameter when creating a default subnet
   time. Another factor influencing the decision was the example of creating a
   time. Another factor influencing the decision was the example of creating a
   volume followed by creating an instance with presumably the two needing to be
   volume followed by creating an instance with presumably the two needing to be
   in the same zone. By requiring the zone across the board, it is less likely to
   in the same zone. By requiring the zone across the board, it is less likely to
-  lead to a miss match. (Related to 63_.)
+  lead to a mismatch. (Related to 63_.)
 
 
 Resource identification, naming, and labeling
 Resource identification, naming, and labeling
 ---------------------------------------------
 ---------------------------------------------

+ 5 - 4
docs/topics/launch.rst

@@ -39,7 +39,7 @@ if you don't have those resources under your account, take a look at the
 
 
 .. code-block:: python
 .. code-block:: python
 
 
-    kp = provider.security.key_pairs.find(name='cloudbridge_intro')[0]
+    kp = provider.security.key_pairs.find(name='cloudbridge-intro')[0]
     fw = provider.security.vm_firewalls.list()[0]
     fw = provider.security.vm_firewalls.list()[0]
 
 
 Launch an instance
 Launch an instance
@@ -50,7 +50,7 @@ Once we have all the desired pieces, we'll use them to launch an instance:
 
 
     inst = provider.compute.instances.create(
     inst = provider.compute.instances.create(
         name='cloudbridge-vpc', image=img, vm_type=vm_type,
         name='cloudbridge-vpc', image=img, vm_type=vm_type,
-        subnet=subnet, key_pair=kp, vm_firewalls=[fw])
+        subnet=subnet, zone=zone, key_pair=kp, vm_firewalls=[fw])
 
 
 Private networking
 Private networking
 ~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~
@@ -73,7 +73,7 @@ that subnet.
 
 
     inst = provider.compute.instances.create(
     inst = provider.compute.instances.create(
         name='cloudbridge-vpc', image=img, vm_type=vm_type,
         name='cloudbridge-vpc', image=img, vm_type=vm_type,
-        subnet=sn, key_pair=kp, vm_firewalls=[fw])
+        subnet=sn, zone=zone, key_pair=kp, vm_firewalls=[fw])
 
 
 For more information on how to create and setup a private network, take a look
 For more information on how to create and setup a private network, take a look
 at `Networking <./networking.html>`_.
 at `Networking <./networking.html>`_.
@@ -95,7 +95,8 @@ refer to :class:`.LaunchConfig`.
     lc.add_volume_device(source=img, size=11, is_root=True)
     lc.add_volume_device(source=img, size=11, is_root=True)
     inst = provider.compute.instances.create(
     inst = provider.compute.instances.create(
         name='cloudbridge-bdm', image=img,  vm_type=vm_type,
         name='cloudbridge-bdm', image=img,  vm_type=vm_type,
-        launch_config=lc, key_pair=kp, vm_firewalls=[fw])
+        launch_config=lc, key_pair=kp, vm_firewalls=[fw],
+        subnet=subnet, zone=zone)
 
 
 where ``img`` is the :class:`.Image` object to use for the root volume.
 where ``img`` is the :class:`.Image` object to use for the root volume.
 
 

+ 4 - 4
docs/topics/networking.rst

@@ -51,9 +51,9 @@ several common scenarios.
      receive incoming traffic from Tier 2, but must not be able to make
      receive incoming traffic from Tier 2, but must not be able to make
      outgoing traffic outside of its subnet.
      outgoing traffic outside of its subnet.
 
 
-    At present, CloudBridge does not provide support for this scenario,
-    primarily because OpenStack's FwaaS (Firewall-as-a-Service) is not widely
-    available.
+   At present, CloudBridge does not provide support for this scenario,
+   primarily because OpenStack's FwaaS (Firewall-as-a-Service) is not widely
+   available.
 
 
 1. Allowing internet access from a launched VM
 1. Allowing internet access from a launched VM
 ----------------------------------------------
 ----------------------------------------------
@@ -90,7 +90,7 @@ The additional step that's required here is to assign a floating IP to the VM:
         name='my-network', cidr_block='10.0.0.0/16')
         name='my-network', cidr_block='10.0.0.0/16')
     sn = net.create_subnet(name='my-subnet', cidr_block='10.0.0.0/28', zone=zone)
     sn = net.create_subnet(name='my-subnet', cidr_block='10.0.0.0/28', zone=zone)
 
 
-    vm = provider.compute.instances.create('my-inst', subnet=sn, ...)
+    vm = provider.compute.instances.create('my-inst', subnet=sn, zone=zone, ...)
 
 
     router = provider.networking.routers.create(network=net, name='my-router')
     router = provider.networking.routers.create(network=net, name='my-router')
     router.attach_subnet(sn)
     router.attach_subnet(sn)

+ 10 - 10
docs/topics/release_process.rst

@@ -1,21 +1,21 @@
 Release Process
 Release Process
 ~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~
 
 
-1. Increment version number in ``cloudbridge/__init__.py`` as per
-   `semver rules <https://semver.org/>_.
+1. Make sure `all tests pass <https://travis-ci.org/CloudVE/cloudbridge>`_.
 
 
-2. Freeze all library dependencies in ``setup.py``. The version numbers can be
-   a range with the upper limit being the latest known working version, and the
-   lowest being the last known working version.
+2. Increment version number in ``cloudbridge/__init__.py`` as per
+   `semver rules <https://semver.org/>`_.
+
+3. Freeze all library dependencies in ``setup.py`` and commit.
+   The version numbers can be a range with the upper limit being the latest
+   known working version, and the lowest being the last known working version.
 
 
    In general, our strategy is to make provider sdk libraries fixed within
    In general, our strategy is to make provider sdk libraries fixed within
    relatively known compatibility ranges, so that we reduce the chances of
    relatively known compatibility ranges, so that we reduce the chances of
-   breakage. If someone uses cloudbridge, presumably, they do not use the sdks
+   breakage. If someone uses CloudBridge, presumably, they do not use the SDKs
    directly. For all other libraries, especially, general purpose libraries
    directly. For all other libraries, especially, general purpose libraries
-   (e.g. six), our strategy is to make compatibility as broad and unrestricted
-   as possible.
-
-3. Run all ``tox`` tests.
+   (e.g. ``six``), our strategy is to make compatibility as broad and
+   unrestricted as possible.
 
 
 4. Add release notes to ``CHANGELOG.rst``. Also add last commit hash to
 4. Add release notes to ``CHANGELOG.rst``. Also add last commit hash to
    changelog. List of commits can be obtained using
    changelog. List of commits can be obtained using

+ 2 - 1
setup.py

@@ -22,7 +22,8 @@ REQS_BASE = [
     'bunch>=1.0.1',
     'bunch>=1.0.1',
     'six>=1.11',
     'six>=1.11',
     'tenacity>=4.12.0,<=5.0',
     'tenacity>=4.12.0,<=5.0',
-    'cachetools'
+    'cachetools>=2.1.0',
+    'deprecated>=1.2.3'
 ]
 ]
 REQS_AWS = ['boto3<1.8.0']
 REQS_AWS = ['boto3<1.8.0']
 # Install azure>=3.0.0 package to find which of the azure libraries listed
 # Install azure>=3.0.0 package to find which of the azure libraries listed