Forráskód Böngészése

Support for labels and names added with aws passing

Nuwan Goonasekera 7 éve
szülő
commit
695296600d

+ 66 - 58
cloudbridge/cloud/base/resources.py

@@ -8,13 +8,14 @@ import os
 import re
 import shutil
 import time
+import uuid
 
 import six
 
 import cloudbridge.cloud.base.helpers as cb_helpers
 from cloudbridge.cloud.interfaces.exceptions \
     import InvalidConfigurationException
-from cloudbridge.cloud.interfaces.exceptions import InvalidNameException
+from cloudbridge.cloud.interfaces.exceptions import InvalidLabelException
 from cloudbridge.cloud.interfaces.exceptions import WaitStateException
 from cloudbridge.cloud.interfaces.resources import AttachmentInfo
 from cloudbridge.cloud.interfaces.resources import Bucket
@@ -60,13 +61,13 @@ class BaseCloudResource(CloudResource):
     Base implementation of a CloudBridge Resource.
     """
 
-    # Regular expression for valid cloudbridge resource names.
+    # Regular expression for valid cloudbridge resource labels.
     # They, must match the same criteria as GCE labels.
     # as discussed here: https://github.com/CloudVE/cloudbridge/issues/55
     #
     # NOTE: The following regex is based on GCEs internal validation logic,
     # and is significantly complex to allow for international characters.
-    CB_NAME_PATTERN = re.compile(six.u(
+    CB_LABEL_PATTERN = re.compile(six.u(
         r"^[\u0061-\u007A\u00B5\u00DF-\u00F6\u00F8-\u00FF\u0101\u0103\u0105"
         "\u0107\u0109\u010B\u010D\u010F\u0111\u0113\u0115\u0117\u0119\u011B"
         "\u011D\u011F\u0121\u0123\u0125\u0127\u0129\u012B\u012D\u012F\u0131"
@@ -196,17 +197,22 @@ class BaseCloudResource(CloudResource):
         self.__provider = provider
 
     @staticmethod
-    def is_valid_resource_name(name):
-        return True if BaseCloudResource.CB_NAME_PATTERN.match(name) else False
+    def is_valid_resource_label(label):
+        return (True if BaseCloudResource.CB_LABEL_PATTERN.match(label)
+                else False)
 
     @staticmethod
-    def assert_valid_resource_name(name):
-        if not BaseCloudResource.is_valid_resource_name(name):
-            log.debug("InvalidNameException raised on %s", name)
-            raise InvalidNameException(
-                u"Invalid name: %s. Name must be at most 63 characters "
+    def assert_valid_resource_label(label):
+        if not BaseCloudResource.is_valid_resource_label(label):
+            log.debug("InvalidLabelException raised on %s", label)
+            raise InvalidLabelException(
+                u"Invalid label: %s. Label must be at most 63 characters "
                 "long and consist of lowercase letters, numbers, "
-                "underscores, dashes or international characters" % name)
+                "underscores, dashes or international characters" % label)
+
+    @staticmethod
+    def _generate_name_from_label(label):
+        return (label[:57] if label else 'cb') + '_' + uuid.uuid4().hex[:6]
 
     @property
     def _provider(self):
@@ -401,7 +407,7 @@ class BaseInstance(BaseCloudResource, BaseObjectLifeCycleMixin, Instance):
                 self.id == other.id and
                 # check from most to least likely mutables
                 self.state == other.state and
-                self.name == other.name and
+                self.label == other.label and
                 self.vm_firewalls == other.vm_firewalls and
                 self.public_ips == other.public_ips and
                 self.private_ips == other.private_ips and
@@ -416,7 +422,7 @@ class BaseInstance(BaseCloudResource, BaseObjectLifeCycleMixin, Instance):
 
     def __repr__(self):
         return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
-                                            self.name, self.id)
+                                            self.label, self.name)
 
 
 class BaseLaunchConfig(LaunchConfig):
@@ -505,7 +511,7 @@ class BaseMachineImage(
                 self.id == other.id and
                 # check from most to least likely mutables
                 self.state == other.state and
-                self.name == other.name and
+                self.label == other.label and
                 self.description == other.description)
 
     def wait_till_ready(self, timeout=None, interval=None):
@@ -552,7 +558,7 @@ class BaseVolume(BaseCloudResource, BaseObjectLifeCycleMixin, Volume):
                 self.id == other.id and
                 # check from most to least likely mutables
                 self.state == other.state and
-                self.name == other.name)
+                self.label == other.label)
 
     def wait_till_ready(self, timeout=None, interval=None):
         self.wait_for(
@@ -563,7 +569,7 @@ class BaseVolume(BaseCloudResource, BaseObjectLifeCycleMixin, Volume):
 
     def __repr__(self):
         return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
-                                            self.name, self.id)
+                                            self.label, self.name)
 
 
 class BaseSnapshot(BaseCloudResource, BaseObjectLifeCycleMixin, Snapshot):
@@ -578,7 +584,7 @@ class BaseSnapshot(BaseCloudResource, BaseObjectLifeCycleMixin, Snapshot):
                 self.id == other.id and
                 # check from most to least likely mutables
                 self.state == other.state and
-                self.name == other.name)
+                self.label == other.label)
 
     def wait_till_ready(self, timeout=None, interval=None):
         self.wait_for(
@@ -589,7 +595,7 @@ class BaseSnapshot(BaseCloudResource, BaseObjectLifeCycleMixin, Snapshot):
 
     def __repr__(self):
         return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
-                                            self.name, self.id)
+                                            self.label, self.name)
 
 
 class BaseKeyPair(BaseCloudResource, KeyPair):
@@ -617,7 +623,7 @@ class BaseKeyPair(BaseCloudResource, KeyPair):
         """
         Return the name of this key pair.
         """
-        return self._key_pair.name
+        return self.id
 
     @property
     def material(self):
@@ -640,7 +646,7 @@ class BaseKeyPair(BaseCloudResource, KeyPair):
         self._key_pair.delete()
 
     def __repr__(self):
-        return "<CBKeyPair: {0}>".format(self.name)
+        return "<CBKeyPair: {0}>".format(self.id)
 
 
 class BaseVMFirewall(BaseCloudResource, VMFirewall):
@@ -676,7 +682,7 @@ class BaseVMFirewall(BaseCloudResource, VMFirewall):
         """
         Return the name of this VM firewall.
         """
-        return self._vm_firewall.name
+        return self.id
 
     @property
     def description(self):
@@ -693,7 +699,7 @@ class BaseVMFirewall(BaseCloudResource, VMFirewall):
 
     def __repr__(self):
         return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
-                                            self.id, self.name)
+                                            self.label or self.name, self.id)
 
 
 class BaseVMFirewallRuleContainer(BasePageableObjectMixin,
@@ -817,7 +823,7 @@ class BaseRegion(BaseCloudResource, Region):
     def to_json(self):
         attr = inspect.getmembers(self, lambda a: not(inspect.isroutine(a)))
         js = {k: v for(k, v) in attr if not k.startswith('_')}
-        js['zones'] = [z.name for z in self.zones]
+        js['zones'] = [z.id for z in self.zones]
         return js
 
 
@@ -829,23 +835,25 @@ class BaseBucketObject(BaseCloudResource, BucketObject):
     #
     # Note: The following regex is based on: https://stackoverflow.com/question
     # s/537772/what-is-the-most-correct-regular-expression-for-a-unix-file-path
-    CB_NAME_PATTERN = re.compile(r"[^\0]+")
+    CB_LABEL_PATTERN = re.compile(r"[^\0]+")
 
     def __init__(self, provider):
         super(BaseBucketObject, self).__init__(provider)
 
     @staticmethod
-    def is_valid_resource_name(name):
-        return True if BaseBucketObject.CB_NAME_PATTERN.match(name) else False
+    def is_valid_resource_label(label):
+        return (True if BaseBucketObject.CB_LABEL_PATTERN.match(label)
+                else False)
 
     @staticmethod
-    def assert_valid_resource_name(name):
-        if not BaseBucketObject.is_valid_resource_name(name):
-            log.debug("InvalidNameException raised on %s", name, exc_info=True)
-            raise InvalidNameException(
-                u"Invalid object name: %s. Name must match criteria defined "
+    def assert_valid_resource_label(label):
+        if not BaseBucketObject.is_valid_resource_label(label):
+            log.debug("InvalidLabelException raised on %s", label,
+                      exc_info=True)
+            raise InvalidLabelException(
+                u"Invalid object label: %s. Label must match criteria defined "
                 "in: http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMeta"
-                "data.html#object-key-guidelines" % name)
+                "data.html#object-key-guidelines" % label)
 
     def save_content(self, target_stream):
         shutil.copyfileobj(self.iter_content(), target_stream)
@@ -860,7 +868,7 @@ class BaseBucketObject(BaseCloudResource, BucketObject):
 
     def __repr__(self):
         return "<CB-{0}: {1}>".format(self.__class__.__name__,
-                                      self.name)
+                                      self.id)
 
 
 class BaseBucket(BaseCloudResource, Bucket):
@@ -871,23 +879,23 @@ class BaseBucket(BaseCloudResource, Bucket):
     #
     # NOTE: The following regex is based on: https://stackoverflow.com/questio
     # ns/2063213/regular-expression-for-validating-dns-label-host-name
-    CB_NAME_PATTERN = re.compile(r"^(?![0-9]+$)(?!-)[a-z0-9-]{3,63}(?<!-)$")
+    CB_LABEL_PATTERN = re.compile(r"^(?![0-9]+$)(?!-)[a-z0-9-]{3,63}(?<!-)$")
 
     def __init__(self, provider):
         super(BaseBucket, self).__init__(provider)
 
     @staticmethod
-    def is_valid_resource_name(name):
-        return True if BaseBucket.CB_NAME_PATTERN.match(name) else False
+    def is_valid_resource_label(label):
+        return True if BaseBucket.CB_LABEL_PATTERN.match(label) else False
 
     @staticmethod
-    def assert_valid_resource_name(name):
-        if not BaseBucket.is_valid_resource_name(name):
-            log.debug("Invalid resource name %s", name, exc_info=True)
-            raise InvalidNameException(
+    def assert_valid_resource_label(label):
+        if not BaseBucket.is_valid_resource_label(label):
+            log.debug("Invalid bucket name %s", label, exc_info=True)
+            raise InvalidLabelException(
                 u"Invalid bucket name: %s. Name must match criteria defined "
                 "in: http://docs.aws.amazon.com/awscloudtrail/latest/userguide"
-                "/cloudtrail-s3-bucket-naming-requirements.html" % name)
+                "/cloudtrail-s3-bucket-naming-requirements.html" % label)
 
     def __eq__(self, other):
         return (isinstance(other, Bucket) and
@@ -899,7 +907,7 @@ class BaseBucket(BaseCloudResource, Bucket):
 
     def __repr__(self):
         return "<CB-{0}: {1}>".format(self.__class__.__name__,
-                                      self.name)
+                                      self.id)
 
 
 class BaseBucketContainer(BasePageableObjectMixin, BucketContainer):
@@ -922,15 +930,15 @@ class BaseGatewayContainer(GatewayContainer, BasePageableObjectMixin):
 
 class BaseNetwork(BaseCloudResource, BaseObjectLifeCycleMixin, Network):
 
-    CB_DEFAULT_NETWORK_NAME = os.environ.get('CB_DEFAULT_NETWORK_NAME',
-                                             'cloudbridge-net')
+    CB_DEFAULT_NETWORK_LABEL = os.environ.get('CB_DEFAULT_NETWORK_LABEL',
+                                              'cloudbridge-net')
 
     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)
+                                            self.id, self.label)
 
     def wait_till_ready(self, timeout=None, interval=None):
         self.wait_for(
@@ -939,9 +947,9 @@ class BaseNetwork(BaseCloudResource, BaseObjectLifeCycleMixin, Network):
             timeout=timeout,
             interval=interval)
 
-    def create_subnet(self, name, cidr_block, zone=None):
+    def create_subnet(self, cidr_block, label=None, zone=None):
         return self._provider.networking.subnets.create(
-            name=name, network=self, cidr_block=cidr_block, zone=zone)
+            label=label, network=self, cidr_block=cidr_block, zone=zone)
 
     def __eq__(self, other):
         return (isinstance(other, Network) and
@@ -952,15 +960,15 @@ class BaseNetwork(BaseCloudResource, BaseObjectLifeCycleMixin, Network):
 
 class BaseSubnet(BaseCloudResource, BaseObjectLifeCycleMixin, Subnet):
 
-    CB_DEFAULT_SUBNET_NAME = os.environ.get('CB_DEFAULT_SUBNET_NAME',
-                                            'cloudbridge-subnet')
+    CB_DEFAULT_SUBNET_LABEL = os.environ.get('CB_DEFAULT_SUBNET_LABEL',
+                                             'cloudbridge-subnet')
 
     def __init__(self, provider):
         super(BaseSubnet, self).__init__(provider)
 
     def __repr__(self):
         return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
-                                            self.id, self.name)
+                                            self.id, self.label)
 
     def __eq__(self, other):
         return (isinstance(other, Subnet) and
@@ -1005,7 +1013,7 @@ class BaseFloatingIP(BaseCloudResource, BaseObjectLifeCycleMixin, FloatingIP):
 
     @property
     def name(self):
-        # VM firewall rules don't support names, so pass
+        # VM firewall rules don't support labels
         return self.public_ip
 
     @property
@@ -1022,7 +1030,7 @@ class BaseFloatingIP(BaseCloudResource, BaseObjectLifeCycleMixin, FloatingIP):
 
     def __repr__(self):
         return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
-                                            self.id, self.public_ip)
+                                            self.id, self.name)
 
     def __eq__(self, other):
         return (isinstance(other, FloatingIP) and
@@ -1033,15 +1041,15 @@ class BaseFloatingIP(BaseCloudResource, BaseObjectLifeCycleMixin, FloatingIP):
 
 class BaseRouter(BaseCloudResource, Router):
 
-    CB_DEFAULT_ROUTER_NAME = os.environ.get('CB_DEFAULT_ROUTER_NAME',
-                                            'cloudbridge-router')
+    CB_DEFAULT_ROUTER_LABEL = os.environ.get('CB_DEFAULT_ROUTER_LABEL',
+                                             'cloudbridge-router')
 
     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)
+                                            self.label)
 
     def __eq__(self, other):
         return (isinstance(other, Router) and
@@ -1053,8 +1061,8 @@ class BaseRouter(BaseCloudResource, Router):
 class BaseInternetGateway(BaseCloudResource, BaseObjectLifeCycleMixin,
                           InternetGateway):
 
-    CB_DEFAULT_INET_GATEWAY_NAME = os.environ.get(
-        'CB_DEFAULT_INET_GATEWAY_NAME', 'cloudbridge-inetgateway')
+    CB_DEFAULT_INET_GATEWAY_LABEL = os.environ.get(
+        'CB_DEFAULT_INET_GATEWAY_LABEL', 'cloudbridge-inetgateway')
 
     def __init__(self, provider):
         super(BaseInternetGateway, self).__init__(provider)
@@ -1062,7 +1070,7 @@ class BaseInternetGateway(BaseCloudResource, BaseObjectLifeCycleMixin,
 
     def __repr__(self):
         return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__, self.id,
-                                            self.name)
+                                            self.label)
 
     def __eq__(self, other):
         return (isinstance(other, InternetGateway) and

+ 1 - 1
cloudbridge/cloud/base/services.py

@@ -186,7 +186,7 @@ class BaseSubnetService(
 
     def find(self, **kwargs):
         obj_list = self
-        filters = ['name']
+        filters = ['label']
         matches = cb_helpers.generic_find(filters, kwargs, obj_list)
         return ClientPagedResultList(self._provider, list(matches))
 

+ 4 - 4
cloudbridge/cloud/interfaces/exceptions.py

@@ -47,15 +47,15 @@ class ProviderConnectionException(CloudBridgeBaseException):
     pass
 
 
-class InvalidNameException(CloudBridgeBaseException):
+class InvalidLabelException(CloudBridgeBaseException):
     """
-    Marker interface for any attempt to set an invalid name on
+    Marker interface for any attempt to set an invalid label on
     a CloudBridge resource.An example would be setting uppercase
-    letters, which are not allowed in a resource name.
+    letters, which are not allowed in a resource label.
     """
 
     def __init__(self, msg):
-        super(InvalidNameException, self).__init__(msg)
+        super(InvalidLabelException, self).__init__(msg)
 
 
 class InvalidValueException(CloudBridgeBaseException):

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

@@ -147,7 +147,7 @@ class CloudProvider(object):
 
             networks = provider.networking.networks.list()
             network = provider.networking.networks.create(
-                           name="DevNet", cidr_block='10.0.0.0/16')
+                           label="DevNet", cidr_block='10.0.0.0/16')
 
         :rtype: :class:`.NetworkingService`
         :return:  a NetworkingService object

+ 117 - 94
cloudbridge/cloud/interfaces/resources.py

@@ -31,9 +31,12 @@ class CloudResource(object):
 
     This interface has a  _provider property that can be used to access the
     provider associated with the resource, which is only intended for use by
-    subclasses. Every cloudbridge resource also has an id and name property.
-    The id property is a unique identifier for the resource. The name property
-    is a display value.
+    subclasses. Every cloudbridge resource also has an id, a name and a
+    label property. The id property is a unique identifier for the resource.
+    The name is a more user-friendly version of an id, suitable for
+    display to an end-user. However, it cannot be used in place of id. See
+    @name documentation. The label property is a user-assignable
+    identifier for the resource.
     """
     __metaclass__ = ABCMeta
 
@@ -64,42 +67,52 @@ class CloudResource(object):
         pass
 
     @abstractproperty
-    def display_id(self):
+    def name(self):
         """
-        Get the displayable id for the resource.
+        Get the name id for the resource.
 
-        The display_id property is typically a user-friendly id value for the
-        resource. The display_id is different from the id property in the
+        The name property is typically a user-friendly id value for the
+        resource. The name is different from the id property in the
         following ways:
-        1. The display_id property is often a more user-friendly value to
+        1. The name property is often a more user-friendly value to
            display to the user than the id property.
-        2. The display_id may sometimes be the same as the id, but should never
+        2. The name may sometimes be the same as the id, but should never
            be used in place of the id.
         3. The id is what will uniquely identify a resource, and will be used
            internally by cloudbridge for all get operations etc.
-        4. All resources have a display_id.
-        5. The display_id is read-only.
-        6. However, the display_id may not necessarily be unique, which is the
+        4. All resources have a name.
+        5. The name is read-only.
+        6. However, the name may not necessarily be unique, which is the
            reason why it should not be used for uniquely identifying a
            resource.
         Example:
-        The AWS machine image name is a display_id. It is not editable and is
-        a user friendly name such as 'Ubuntu 14.04' and corresponds to the
-        ami-name. It is distinct from the ami-id, which corresponds to
-        cloudbridge's id property. The ami-name cannot be edited, and is set
-        at creation time. It is not necessarily unique.
-        In Azure, the machine image's display_id corresponds to the name
+        The AWS machine image name maps to a cloudbridge name. It is not
+        editable and is a user friendly name such as 'Ubuntu 14.04' and
+        corresponds to the ami-name. It is distinct from the ami-id, which
+        maps to cloudbridge's id property. The ami-name cannot be edited, and
+        is set at creation time. It is not necessarily unique.
+        In Azure, the machine image's name corresponds to cloudbridge's name
         property. In Azure, it also happens to be the same as the id property.
         """
         pass
 
+    @abstractmethod
+    def to_json(self):
+        """
+        Returns a JSON representation of the CloudResource object.
+        """
+        pass
+
+
+class LabeledCloudResource(CloudResource):
+
     @abstractproperty
     def label(self):
         """
         Get the resource label.
 
         The label property is a user-defined, editable identifier for a
-        resource. It will often correspond to a user editable resource name
+        resource. It will often correspond to a user editable resource label
         in the underlying cloud provider, or be simulated through tags/labels.
 
         The label property adheres to the following restrictions:
@@ -116,13 +129,6 @@ class CloudResource(object):
         """
         pass
 
-    @abstractmethod
-    def to_json(self):
-        """
-        Returns a JSON representation of the CloudResource object.
-        """
-        pass
-
 
 class Configuration(dict):
     """
@@ -484,19 +490,15 @@ class InstanceState(object):
     ERROR = "error"
 
 
-class Instance(ObjectLifeCycleMixin, CloudResource):
+class Instance(ObjectLifeCycleMixin, LabeledCloudResource):
 
     __metaclass__ = ABCMeta
 
-    @CloudResource.name.setter
+    @LabeledCloudResource.label.setter
     @abstractmethod
-    def name(self, value):
+    def label(self, value):
         """
-        Set the instance name.
-
-        Note that the changing the name of an existing resource may result in
-        cloud-dependent code. See the following page for more details:
-        http://cloudbridge.cloudve.org/en/latest/topics/design-decisions.html
+        Set the instance label.
         """
         pass
 
@@ -530,7 +532,7 @@ class Instance(ObjectLifeCycleMixin, CloudResource):
         object, you can use the ``instance.vm_type`` property instead.
 
         :rtype: ``str``
-        :return: VM type name for this instance (e.g., ``m1.large``)
+        :return: VM type id for this instance (e.g., ``m1.large``)
         """
         pass
 
@@ -624,15 +626,15 @@ class Instance(ObjectLifeCycleMixin, CloudResource):
     @abstractproperty
     def key_pair_name(self):
         """
-        Get the name of the key pair associated with this instance.
+        Get the id of the key pair associated with this instance.
 
         :rtype: ``str``
-        :return: Name of the ssh key pair associated with this instance.
+        :return: Id of the ssh key pair associated with this instance.
         """
         pass
 
     @abstractmethod
-    def create_image(self, name):
+    def create_image(self, label=None):
         """
         Create a new image based on this instance.
 
@@ -722,8 +724,8 @@ class LaunchConfig(object):
         lc = provider.compute.instances.create_launch_config()
         lc.add_block_device(...)
 
-        inst = provider.compute.instances.create(name, image, vm_type,
-                                                 network, launch_config=lc)
+        inst = provider.compute.instances.create(
+            image, vm_type, network, label='MyVM', launch_config=lc)
     """
 
     @abstractmethod
@@ -874,12 +876,20 @@ class NetworkState(object):
     ERROR = "error"
 
 
-class Network(ObjectLifeCycleMixin, CloudResource):
+class Network(ObjectLifeCycleMixin, LabeledCloudResource):
     """
     Represents a software-defined network, like the Virtual Private Cloud.
     """
     __metaclass__ = ABCMeta
 
+    @LabeledCloudResource.label.setter
+    @abstractmethod
+    def label(self, value):
+        """
+        Set the resource label.
+        """
+        pass
+
     @abstractproperty
     def external(self):
         """
@@ -934,18 +944,18 @@ class Network(ObjectLifeCycleMixin, CloudResource):
         pass
 
     @abstractmethod
-    def create_subnet(self, name, cidr_block, zone=None):
+    def create_subnet(self, cidr_block, label=None, zone=None):
         """
         Create a new network subnet and associate it with this Network.
 
-        :type name: ``str``
-        :param name: The subnet name. The name will be set if the
-                     provider supports it.
-
         :type cidr_block: ``str``
         :param cidr_block: CIDR block within this Network to assign to the
                            subnet.
 
+        :type label: ``str``
+        :param label: The subnet label. The subnet name will be derived from
+                      this label.
+
         :type zone: ``str``
         :param zone: Placement zone where to create the subnet. Some providers
                      may not support subnet zones, in which case the value is
@@ -984,12 +994,20 @@ class SubnetState(object):
     ERROR = "error"
 
 
-class Subnet(ObjectLifeCycleMixin, CloudResource):
+class Subnet(ObjectLifeCycleMixin, LabeledCloudResource):
     """
     Represents a subnet, as part of a Network.
     """
     __metaclass__ = ABCMeta
 
+    @LabeledCloudResource.label.setter
+    @abstractmethod
+    def label(self, value):
+        """
+        Set the resource label.
+        """
+        pass
+
     @abstractproperty
     def cidr_block(self):
         """
@@ -1067,7 +1085,7 @@ class FloatingIPContainer(PageableObjectMixin):
         """
         Searches for a FloatingIP by a given list of attributes.
 
-        Supported attributes: name, public_ip
+        Supported attributes: label, public_ip
 
         Example:
 
@@ -1180,7 +1198,7 @@ class RouterState(object):
     DETACHED = "detached"
 
 
-class Router(CloudResource):
+class Router(LabeledCloudResource):
     """
     Represents a private network router.
 
@@ -1191,6 +1209,14 @@ class Router(CloudResource):
     """
     __metaclass__ = ABCMeta
 
+    @LabeledCloudResource.label.setter
+    @abstractmethod
+    def label(self, value):
+        """
+        Set the resource label.
+        """
+        pass
+
     @abstractproperty
     def state(self):
         """
@@ -1294,16 +1320,15 @@ class GatewayContainer(PageableObjectMixin):
     __metaclass__ = ABCMeta
 
     @abstractmethod
-    def get_or_create_inet_gateway(self, name=None):
+    def get_or_create_inet_gateway(self, label=None):
         """
         Creates new or returns an existing internet gateway for a network.
 
         The returned gateway object can subsequently be attached to a router to
         provide internet routing to a network.
 
-        :type  name: ``str``
-        :param name: The gateway name. This applies only if creating a gateway
-                     and if the provider supports it.
+        :type  label: ``str``
+        :param label: The gateway label.
 
         :rtype: ``object``  of :class:`.InternetGateway` or ``None``
         :return: an InternetGateway object of ``None`` if not found.
@@ -1331,12 +1356,20 @@ class GatewayContainer(PageableObjectMixin):
         pass
 
 
-class Gateway(CloudResource):
+class Gateway(LabeledCloudResource):
     """
     Represents a gateway resource.
     """
     __metaclass__ = ABCMeta
 
+    @LabeledCloudResource.label.setter
+    @abstractmethod
+    def label(self, value):
+        """
+        Set the resource label.
+        """
+        pass
+
     @abstractproperty
     def network_id(self):
         """
@@ -1431,22 +1464,18 @@ class VolumeState(object):
     ERROR = "error"
 
 
-class Volume(ObjectLifeCycleMixin, CloudResource):
+class Volume(ObjectLifeCycleMixin, LabeledCloudResource):
     """
     Represents a block storage device (aka volume).
     """
 
     __metaclass__ = ABCMeta
 
-    @CloudResource.name.setter
+    @LabeledCloudResource.label.setter
     @abstractmethod
-    def name(self, value):
+    def label(self, value):
         """
-        Set the volume name.
-
-        Note that the changing the name of an existing resource may result in
-        cloud-dependent code. See the following page for more details:
-        http://cloudbridge.cloudve.org/en/latest/topics/design-decisions.html
+        Set the volume label.
         """
         pass
 
@@ -1456,7 +1485,7 @@ class Volume(ObjectLifeCycleMixin, CloudResource):
         Get the volume description.
 
         Some cloud providers may not support this property, and will return the
-        volume name instead.
+        volume label instead.
 
         :rtype: ``str``
         :return: Description for this volume as returned by the cloud
@@ -1472,7 +1501,7 @@ class Volume(ObjectLifeCycleMixin, CloudResource):
 
         Some cloud providers may not support this property, and setting the
         description may have no effect (providers that do not support this
-        property will always return the volume name as the description).
+        property will always return the volume label as the description).
         """
         pass
 
@@ -1571,12 +1600,12 @@ class Volume(ObjectLifeCycleMixin, CloudResource):
         pass
 
     @abstractmethod
-    def create_snapshot(self, name, description=None):
+    def create_snapshot(self, label=None, description=None):
         """
         Create a snapshot of this Volume.
 
-        :type name: ``str``
-        :param name: The name of this snapshot.
+        :type label: ``str``
+        :param label: The label for this snapshot.
 
         :type description: ``str``
         :param description: A description of the snapshot.
@@ -1616,22 +1645,18 @@ class SnapshotState(object):
     ERROR = "error"
 
 
-class Snapshot(ObjectLifeCycleMixin, CloudResource):
+class Snapshot(ObjectLifeCycleMixin, LabeledCloudResource):
     """
     Represents a snapshot of a block storage device.
     """
 
     __metaclass__ = ABCMeta
 
-    @CloudResource.name.setter
+    @LabeledCloudResource.label.setter
     @abstractmethod
-    def name(self, value):
+    def label(self, value):
         """
-        Set the snapshot name.
-
-        Note that the changing the name of an existing resource may result in
-        cloud-dependent code. See the following page for more details:
-        http://cloudbridge.cloudve.org/en/latest/topics/design-decisions.html
+        Set the snapshot label.
         """
         pass
 
@@ -1641,7 +1666,7 @@ class Snapshot(ObjectLifeCycleMixin, CloudResource):
         Get the snapshot description.
 
         Some cloud providers may not support this property, and will return the
-        snapshot name instead.
+        snapshot label instead.
 
         :rtype: ``str``
         :return: Description for this snapshot as returned by the cloud
@@ -1657,7 +1682,7 @@ class Snapshot(ObjectLifeCycleMixin, CloudResource):
 
         Some cloud providers may not support this property, and setting the
         description may have no effect (providers that do not support this
-        property will always return the snapshot name as the description).
+        property will always return the snapshot label as the description).
 
         :type value: ``str``
         :param value: The value for the snapshot description.
@@ -1824,7 +1849,7 @@ class PlacementZone(CloudResource):
         A region this placement zone is associated with.
 
         :rtype: ``str``
-        :return: The name of the region the zone is associated with.
+        :return: The id of the region the zone is associated with.
         """
         pass
 
@@ -1921,7 +1946,7 @@ class VMType(CloudResource):
         pass
 
 
-class VMFirewall(CloudResource):
+class VMFirewall(LabeledCloudResource):
     """
     Represents a firewall resource applied to virtual machines.
 
@@ -1930,6 +1955,14 @@ class VMFirewall(CloudResource):
 
     __metaclass__ = ABCMeta
 
+    @LabeledCloudResource.label.setter
+    @abstractmethod
+    def label(self, value):
+        """
+        Set the resource label.
+        """
+        pass
+
     @abstractproperty
     def description(self):
         """
@@ -1983,7 +2016,7 @@ class VMFirewallRuleContainer(PageableObjectMixin):
 
             fw = provider.security.vm_firewalls.get('my_fw_id')
             rule = fw.rules.get('rule_id')
-            print(rule.id, rule.name)
+            print(rule.id, rule.label)
 
         :rtype: :class:`.FirewallRule`
         :return:  a FirewallRule instance
@@ -2055,8 +2088,8 @@ class VMFirewallRuleContainer(PageableObjectMixin):
         """
         Find a firewall rule filtered by the given parameters.
 
-        :type name: str
-        :param name: The name of the VM firewall to retrieve.
+        :type label: str
+        :param label: The label of the VM firewall to retrieve.
 
         :type protocol: ``str``
         :param protocol: Either ``tcp`` | ``udp`` | ``icmp``.
@@ -2202,11 +2235,6 @@ class BucketObject(CloudResource):
         """
         Retrieve the name of the current object.
 
-        The bucket object name adheres to a naming requirement that is more
-        relaxed than the naming requirement enforced across CloudBridge. More
-        details are available here: http://docs.aws.amazon.com/AmazonS3/latest/
-        dev/UsingMetadata.html#object-key-guidelines
-
         :rtype: ``str``
         :return: Name for this object as returned by the cloud middleware.
         """
@@ -2318,11 +2346,6 @@ class Bucket(CloudResource):
         """
         Retrieve the name of the current bucket.
 
-        The bucket name adheres to a naming requirement that is more
-        relaxed than the naming requirement enforced across CloudBridge. More
-        details are available here: http://docs.aws.amazon.com/awscloudtrail/
-        latest/userguide/cloudtrail-s3-bucket-naming-requirements.html
-
         :rtype: ``str``
         :return: Name for this instance as returned by the cloud middleware.
         """
@@ -2342,7 +2365,7 @@ class Bucket(CloudResource):
             # Show all objects in bucket
             print(list(bucket.objects))
 
-            # Find an object by name
+            # Find an object by label
             print(bucket.objects.find(name='my_obj.txt'))
 
             # Get first page of bucket list
@@ -2413,7 +2436,7 @@ class BucketContainer(PageableObjectMixin):
     @abstractmethod
     def find(self, **kwargs):
         """
-        Searche for an object by a given list of attributes.
+        Search for an object by a given list of attributes.
 
         Supported attributes: ``name``
 

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

@@ -48,15 +48,15 @@ class ComputeService(CloudService):
 
             # print all images
             for image in provider.compute.images:
-                print(image.id, image.name)
+                print(image.id, image.name, image.label)
 
             # print only first 50 images
             for image in provider.compute.images.list(limit=50):
-                print(image.id, image.name)
+                print(image.id, image.name, image.label)
 
             # find image by name
-            image = provider.compute.images.find(name='Ubuntu 16.04')
-            print(image.id, image.name)
+            image = provider.compute.images.find(name='Ubuntu 16.04')[0]
+            print(image.id, image.name, image.label)
 
         :rtype: :class:`.ImageService`
         :return: an ImageService object
@@ -77,7 +77,7 @@ class ComputeService(CloudService):
                 print(vm_type.id, vm_type.name)
 
             # find a specific size by name
-            vm_type = provider.compute.vm_types.find(name='m1.small')
+            vm_type = provider.compute.vm_types.find(name='m1.small')[0]
             print(vm_type.vcpus)
 
         :rtype: :class:`.VMTypeService`
@@ -164,11 +164,14 @@ class InstanceService(PageableObjectMixin, CloudService):
         """
         Searches for an instance by a given list of attributes.
 
-        Supported attributes: name
+        Supported attributes: name, label
 
         :type  name: ``str``
         :param name: The name to search for
 
+        :type  label: ``str``
+        :param label: The label to search for
+
         :rtype: List of ``object`` of :class:`.Instance`
         :return: A list of Instance objects matching the supplied attributes.
         """
@@ -210,16 +213,13 @@ class InstanceService(PageableObjectMixin, CloudService):
         pass
 
     @abstractmethod
-    def create(self, name, image, vm_type, subnet, zone=None,
+    def create(self, image, vm_type, subnet, label=None, zone=None,
                key_pair=None, vm_firewalls=None, user_data=None,
                launch_config=None,
                **kwargs):
         """
         Creates a new virtual machine instance.
 
-        :type  name: ``str``
-        :param name: The name of the virtual machine instance
-
         :type  image: ``MachineImage`` or ``str``
         :param image: The MachineImage object or id to boot the virtual machine
                       with
@@ -239,8 +239,12 @@ class InstanceService(PageableObjectMixin, CloudService):
                        work. Some providers (e.g. OpenStack) support a null
                        value but the behaviour is implementation specific.
 
+        :type  label: ``str``
+        :param label: The label of the virtual machine instance. The instance
+                      name will be derived from this label.
+
         :type  zone: ``Zone`` or ``str``
-        :param zone: The Zone or its name, where the instance should be placed.
+        :param zone: The Zone or its id, where the instance should be placed.
                      This parameter is provided for legacy compatibility (with
                      classic networks).
 
@@ -248,7 +252,7 @@ class InstanceService(PageableObjectMixin, CloudService):
                      parameter, but in its absence, this value will be used.
 
         :type  key_pair: ``KeyPair`` or ``str``
-        :param key_pair: The KeyPair object or its name, to set for the
+        :param key_pair: The KeyPair object or its id, to set for the
                          instance.
 
         :type  vm_firewalls: A ``list`` of ``VMFirewall`` objects or a
@@ -311,7 +315,7 @@ class VolumeService(PageableObjectMixin, CloudService):
         """
         Searches for a volume by a given list of attributes.
 
-        Supported attributes: name
+        Supported attributes: label
 
         :rtype: ``object`` of :class:`.Volume`
         :return: a Volume object or ``None`` if not found.
@@ -329,19 +333,19 @@ class VolumeService(PageableObjectMixin, CloudService):
         pass
 
     @abstractmethod
-    def create(self, name, size, zone, snapshot=None, description=None):
+    def create(self, size, zone, label=None, snapshot=None, description=None):
         """
         Creates a new volume.
 
-        :type  name: ``str``
-        :param name: The name of the volume.
-
         :type  size: ``int``
         :param size: The size of the volume (in GB).
 
         :type  zone: ``str`` or :class:`.PlacementZone` object
         :param zone: The availability zone in which the Volume will be created.
 
+        :type  label: ``str``
+        :param label: The label for the volume.
+
         :type  snapshot: ``str`` or :class:`.Snapshot` object
         :param snapshot: An optional reference to a snapshot from which this
                          volume should be created.
@@ -378,7 +382,7 @@ class SnapshotService(PageableObjectMixin, CloudService):
         """
         Searches for a snapshot by a given list of attributes.
 
-        Supported attributes: name
+        Supported attributes: label
 
         :rtype: list of :class:`.Snapshot`
         :return: a Snapshot object or an empty list if none found.
@@ -396,16 +400,16 @@ class SnapshotService(PageableObjectMixin, CloudService):
         pass
 
     @abstractmethod
-    def create(self, name, volume, description=None):
+    def create(self, volume, label=None, description=None):
         """
         Creates a new snapshot off a volume.
 
-        :type  name: ``str``
-        :param name: The name of the snapshot
-
         :type  volume: ``str`` or ``Volume``
         :param volume: The volume to create a snapshot of.
 
+        :type  label: ``str``
+        :param label: The label for the snapshot.
+
         :type  description: ``str``
         :param description: An optional description that may be supported by
                             some providers. Providers that do not support this
@@ -437,11 +441,11 @@ class StorageService(CloudService):
 
             # print all volumes
             for vol in provider.storage.volumes:
-                print(vol.id, vol.name)
+                print(vol.id, vol.name, vol.label)
 
-            # find volume by name
-            vol = provider.storage.volumes.find(name='my_vol')[0]
-            print(vol.id, vol.name)
+            # find volume by label
+            vol = provider.storage.volumes.find(label='my_vol')[0]
+            print(vol.id, vol.name, vol.label)
 
         :rtype: :class:`.VolumeService`
         :return: a VolumeService object
@@ -459,11 +463,11 @@ class StorageService(CloudService):
 
             # print all snapshots
             for snap in provider.storage.snapshots:
-                print(snap.id, snap.name)
+                print(snap.id, snap.name, snap.label)
 
-            # find snapshot by name
-            snap = provider.storage.snapshots.find(name='my_snap')[0]
-            print(snap.id, snap.name)
+            # find snapshot by label
+            snap = provider.storage.snapshots.find(label='my_snap')[0]
+            print(snap.id, snap.name, snap.label)
 
         :rtype: :class:`.SnapshotService`
         :return: a SnapshotService object
@@ -516,7 +520,7 @@ class ImageService(PageableObjectMixin, CloudService):
         """
         Searches for an image by a given list of attributes
 
-        Supported attributes: name
+        Supported attributes: name, label
 
         :rtype: ``object`` of :class:`.Image`
         :return:  an Image instance
@@ -616,7 +620,7 @@ class NetworkService(PageableObjectMixin, CloudService):
         """
         Searches for a network by a given list of attributes.
 
-        Supported attributes: name
+        Supported attributes: name, label
 
         :rtype: List of ``object`` of :class:`.Network`
         :return: A list of Network objects matching the supplied attributes.
@@ -624,14 +628,10 @@ class NetworkService(PageableObjectMixin, CloudService):
         pass
 
     @abstractmethod
-    def create(self, name, cidr_block):
+    def create(self, cidr_block, label=None):
         """
         Create a new network.
 
-        :type name: ``str``
-        :param name: A network name. The name will be set if the
-                     provider supports it.
-
         :type cidr_block: ``str``
         :param cidr_block: The cidr block for this network. Some providers
                            will respect this at the network level, while others
@@ -642,6 +642,9 @@ class NetworkService(PageableObjectMixin, CloudService):
                            between a /16 netmask (65,536 IP addresses) and /28
                            netmask (16 IP addresses). e.g. 10.0.0.0/16
 
+        :type label: ``str``
+        :param label: A label for the network.
+
         :rtype: ``object`` of :class:`.Network`
         :return:  A Network object
         """
@@ -668,11 +671,11 @@ class NetworkService(PageableObjectMixin, CloudService):
 
             # Print all subnets
             for s in provider.networking.subnets:
-                print(s.id, s.name)
+                print(s.id, s.name, s.label)
 
             # Get subnet by ID
             s = provider.networking.subnets.get('subnet-id')
-            print(s.id, s.name)
+            print(s.id, s.name, s.label)
 
         :rtype: :class:`.SubnetService`
         :return: a SubnetService object
@@ -719,7 +722,7 @@ class SubnetService(PageableObjectMixin, CloudService):
         """
         Searches for a subnet by a given list of attributes.
 
-        Supported attributes: name
+        Supported attributes: name, label
 
         :rtype: List of ``object`` of :class:`.Subnet`
         :return: A list of Subnet objects matching the supplied attributes.
@@ -727,14 +730,10 @@ class SubnetService(PageableObjectMixin, CloudService):
         pass
 
     @abstractmethod
-    def create(self, name, network_id, cidr_block, zone):
+    def create(self, network_id, cidr_block, zone, label=None):
         """
         Create a new subnet within the supplied network.
 
-        :type name: ``str``
-        :param name: The subnet name. The name will be set if the
-                     provider supports it.
-
         :type network: :class:`.Network` object or ``str``
         :param network: Network object or ID under which to create the subnet.
 
@@ -746,6 +745,9 @@ class SubnetService(PageableObjectMixin, CloudService):
         :param zone: A placement zone for the subnet. Some providers
                      may not support this, in which case the value is ignored.
 
+        :type label: ``str``
+        :param label: The subnet label.
+
         :rtype: ``object`` of :class:`.Subnet`
         :return:  A Subnet object
         """
@@ -759,7 +761,7 @@ class SubnetService(PageableObjectMixin, CloudService):
         are not particularly concerned with how the network is structured.
 
         A default network is one marked as such by the provider or matches the
-        default name used by this library (e.g., cloudbridge-net).
+        default label used by this library (e.g., cloudbridge-net).
 
         :type zone: :class:`.PlacementZone` object ``str``
         :param zone: Placement zone where to look for the subnet.
@@ -814,7 +816,7 @@ class RouterService(PageableObjectMixin, CloudService):
         """
         Searches for a router by a given list of attributes.
 
-        Supported attributes: name
+        Supported attributes: name, label
 
         :rtype: List of ``object`` of :class:`.Router`
         :return: A list of Router objects matching the supplied attributes.
@@ -822,17 +824,16 @@ class RouterService(PageableObjectMixin, CloudService):
         pass
 
     @abstractmethod
-    def create(self, name, network):
+    def create(self, network, label=None):
         """
         Create a new router.
 
-        :type name: ``str``
-        :param name: A router name. The name will be set if the provider
-                     supports it.
-
         :type network: :class:`.Network` object or ``str``
         :param network: Network object or ID under which to create the router.
 
+        :type label: ``str``
+        :param label: A router label.
+
         :rtype: ``object`` of :class:`.Router`
         :return:  A Router object
         """

+ 85 - 50
cloudbridge/cloud/providers/aws/resources.py

@@ -141,7 +141,7 @@ class AWSPlacementZone(BasePlacementZone):
 
     @property
     def name(self):
-        return self._aws_zone
+        return self.id
 
     @property
     def region_name(self):
@@ -160,7 +160,7 @@ class AWSVMType(BaseVMType):
 
     @property
     def name(self):
-        return self._inst_dict['instance_type']
+        return self.id
 
     @property
     def family(self):
@@ -222,17 +222,21 @@ class AWSInstance(BaseInstance):
         return self._ec2_instance.id
 
     @property
-    # pylint:disable=arguments-differ
     def name(self):
+        return self.id
+
+    @property
+    # pylint:disable=arguments-differ
+    def label(self):
         """
         .. note:: an instance must have a (case sensitive) tag ``Name``
         """
         return find_tag_value(self._ec2_instance.tags, 'Name')
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
-        self.assert_valid_resource_name(value)
+    def label(self, value):
+        self.assert_valid_resource_label(value)
         self._ec2_instance.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
 
     @property
@@ -290,14 +294,17 @@ class AWSInstance(BaseInstance):
     def key_pair_name(self):
         return self._ec2_instance.key_name
 
-    def create_image(self, name):
-        self.assert_valid_resource_name(name)
+    def create_image(self, label=None):
+        self.assert_valid_resource_label(label)
+        name = self._generate_name_from_label(label)
 
         image = AWSMachineImage(self._provider,
                                 self._ec2_instance.create_image(Name=name))
         # Wait for the image to exist
         self._provider.ec2_conn.meta.client.get_waiter('image_exists').wait(
             ImageIds=[image.id])
+        # Add image label
+        image.label = label
         # Return the image
         image.refresh()
         return image
@@ -385,17 +392,21 @@ class AWSVolume(BaseVolume):
         return self._volume.id
 
     @property
-    # pylint:disable=arguments-differ
     def name(self):
+        return self.id
+
+    @property
+    # pylint:disable=arguments-differ
+    def label(self):
         try:
             return find_tag_value(self._volume.tags, 'Name')
         except ClientError as e:
-            log.warn("Cannot get name for volume {0}: {1}".format(self.id, e))
+            log.warn("Cannot get label for volume {0}: {1}".format(self.id, e))
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
-        self.assert_valid_resource_name(value)
+    def label(self, value):
+        self.assert_valid_resource_label(value)
         self._volume.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
 
     @property
@@ -449,12 +460,12 @@ class AWSVolume(BaseVolume):
                 Device=a.device,
                 Force=force)
 
-    def create_snapshot(self, name, description=None):
+    def create_snapshot(self, label=None, description=None):
         snap = AWSSnapshot(
             self._provider,
             self._volume.create_snapshot(
                 Description=description))
-        snap.name = name
+        snap.label = label
         return snap
 
     def delete(self):
@@ -498,17 +509,21 @@ class AWSSnapshot(BaseSnapshot):
         return self._snapshot.id
 
     @property
-    # pylint:disable=arguments-differ
     def name(self):
+        return self.id
+
+    @property
+    # pylint:disable=arguments-differ
+    def label(self):
         try:
             return find_tag_value(self._snapshot.tags, 'Name')
         except ClientError as e:
-            log.warn("Cannot get name for snap {0}: {1}".format(self.id, e))
+            log.warn("Cannot get label for snap {0}: {1}".format(self.id, e))
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
-        self.assert_valid_resource_name(value)
+    def label(self, value):
+        self.assert_valid_resource_label(value)
         self._snapshot.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
 
     @property
@@ -553,13 +568,13 @@ class AWSSnapshot(BaseSnapshot):
         self._snapshot.delete()
 
     def create_volume(self, placement, size=None, volume_type=None, iops=None):
+        label = "from_snap_{0}".format(self.label or self.id)
         cb_vol = self._provider.storage.volumes.create(
-            name=self.name,
+            label=label,
             size=size,
             zone=placement,
             snapshot=self.id)
         cb_vol.wait_till_ready()
-        cb_vol.name = "from_snap_{0}".format(self.name or self.id)
         return cb_vol
 
 
@@ -577,18 +592,22 @@ class AWSVMFirewall(BaseVMFirewall):
 
     @property
     def name(self):
+        """
+        Return the name of this VM firewall.
+        """
+        return self._vm_firewall.group_name
+
+    @property
+    def label(self):
         try:
-            name = find_tag_value(self._vm_firewall.tags, 'Name')
-            if not name:  # Return group_name (which cannot be changed)
-                name = self._vm_firewall.group_name
-            return name
+            return find_tag_value(self._vm_firewall.tags, 'Name')
         except ClientError:
-            return self._vm_firewall.group_name
+            return None
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
-        self.assert_valid_resource_name(value)
+    def label(self, value):
+        self.assert_valid_resource_label(value)
         self._vm_firewall.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
 
     @property
@@ -826,7 +845,7 @@ class AWSBucket(BaseBucket):
 
     @property
     def name(self):
-        return self._bucket.name
+        return self.id
 
     @property
     def objects(self):
@@ -924,12 +943,16 @@ class AWSNetwork(BaseNetwork):
 
     @property
     def name(self):
+        return self.id
+
+    @property
+    def label(self):
         return find_tag_value(self._vpc.tags, 'Name')
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
-        self.assert_valid_resource_name(value)
+    def label(self, value):
+        self.assert_valid_resource_label(value)
         self._vpc.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
 
     @property
@@ -996,12 +1019,16 @@ class AWSSubnet(BaseSubnet):
 
     @property
     def name(self):
+        return self.id
+
+    @property
+    def label(self):
         return find_tag_value(self._subnet.tags, 'Name')
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
-        self.assert_valid_resource_name(value)
+    def label(self, value):
+        self.assert_valid_resource_label(value)
         self._subnet.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
 
     @property
@@ -1105,12 +1132,16 @@ class AWSRouter(BaseRouter):
 
     @property
     def name(self):
+        return self.id
+
+    @property
+    def label(self):
         return find_tag_value(self._route_table.tags, 'Name')
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
-        self.assert_valid_resource_name(value)
+    def label(self, value):
+        self.assert_valid_resource_label(value)
         self._route_table.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
 
     def refresh(self):
@@ -1168,15 +1199,15 @@ class AWSGatewayContainer(BaseGatewayContainer):
                                   cb_resource=AWSInternetGateway,
                                   boto_collection_name='internet_gateways')
 
-    def get_or_create_inet_gateway(self, name=None):
-        log.debug("Get or create inet gateway %s on net %s", name,
+    def get_or_create_inet_gateway(self, label=None):
+        log.debug("Get or create inet gateway %s on net %s", label,
                   self._network)
-        if name:
-            AWSInternetGateway.assert_valid_resource_name(name)
+        if label:
+            AWSInternetGateway.assert_valid_resource_label(label)
 
         network_id = self._network.id if isinstance(
             self._network, AWSNetwork) else self._network
-        # Don't filter by name because it may conflict with at least the
+        # Don't filter by label because it may conflict with at least the
         # default VPC that most accounts have but that network is typically
         # without a name.
         gtw = self.svc.find(filter_name='attachment.vpc-id',
@@ -1185,8 +1216,8 @@ class AWSGatewayContainer(BaseGatewayContainer):
             return gtw[0]  # There can be only one gtw attached to a VPC
         # Gateway does not exist so create one and attach to the supplied net
         cb_gateway = self.svc.create('create_internet_gateway')
-        if name:
-            cb_gateway.name = name
+        if label:
+            cb_gateway.label = label
         cb_gateway._gateway.attach_to_vpc(VpcId=network_id)
         return cb_gateway
 
@@ -1219,12 +1250,16 @@ class AWSInternetGateway(BaseInternetGateway):
 
     @property
     def name(self):
+        return self.id
+
+    @property
+    def label(self):
         return find_tag_value(self._gateway.tags, 'Name')
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
-        self.assert_valid_resource_name(value)
+    def label(self, value):
+        self.assert_valid_resource_label(value)
         self._gateway.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
 
     def refresh(self):

+ 83 - 79
cloudbridge/cloud/providers/aws/services.py

@@ -100,7 +100,7 @@ class AWSKeyPairService(BaseKeyPairService):
 
     def create(self, name, public_key_material=None):
         log.debug("Creating Key Pair Service %s", name)
-        AWSKeyPair.assert_valid_resource_name(name)
+        AWSKeyPair.assert_valid_resource_label(name)
         private_key = None
         if not public_key_material:
             public_key_material, private_key = cb_helpers.generate_key_pair()
@@ -132,24 +132,27 @@ class AWSVMFirewallService(BaseVMFirewallService):
     def list(self, limit=None, marker=None):
         return self.svc.list(limit=limit, marker=marker)
 
-    def create(self, name, description, network_id):
+    def create(self, description, network_id, label=None):
         log.debug("Creating Firewall Service with the parameters "
-                  "[name: %s id: %s description: %s]", name, network_id,
+                  "[label: %s id: %s description: %s]", label, network_id,
                   description)
-        AWSVMFirewall.assert_valid_resource_name(name)
-        return self.svc.create('create_security_group', GroupName=name,
-                               Description=description, VpcId=network_id)
+        AWSVMFirewall.assert_valid_resource_label(label)
+        name = AWSVMFirewall._generate_name_from_label(label)
+        obj = self.svc.create('create_security_group', GroupName=name,
+                              Description=description, VpcId=network_id)
+        obj.label = label
+        return obj
 
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
-
+        # Filter by name or label
+        label = kwargs.pop('label', None)
+        log.debug("Searching for Firewall Service %s", label)
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
-
-        log.debug("Searching for Firewall Service %s", name)
-        return self.svc.find(filter_name='group-name', filter_value=name)
+                            " Supported attributes: %s" % (kwargs, 'label'))
+        return self.svc.find(filter_name='tag:Name',
+                             filter_value=label)
 
     def delete(self, firewall_id):
         log.info("Deleting Firewall Service with the id %s", firewall_id)
@@ -195,25 +198,25 @@ class AWSVolumeService(BaseVolumeService):
         return self.svc.get(volume_id)
 
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        label = kwargs.pop('label', None)
 
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs, 'label'))
 
-        log.debug("Searching for AWS Volume Service %s", name)
-        return self.svc.find(filter_name='tag:Name', filter_value=name)
+        log.debug("Searching for AWS Volume Service %s", label)
+        return self.svc.find(filter_name='tag:Name', filter_value=label)
 
     def list(self, limit=None, marker=None):
         return self.svc.list(limit=limit, marker=marker)
 
-    def create(self, name, size, zone, snapshot=None, description=None):
+    def create(self, size, zone, label=None, snapshot=None, description=None):
         log.debug("Creating AWS Volume Service with the parameters "
-                  "[name: %s size: %s zone: %s snapshot: %s "
-                  "description: %s]", name, size, zone, snapshot,
+                  "[label: %s size: %s zone: %s snapshot: %s "
+                  "description: %s]", label, size, zone, snapshot,
                   description)
-        AWSVolume.assert_valid_resource_name(name)
+        AWSVolume.assert_valid_resource_label(label)
 
         zone_id = zone.id if isinstance(zone, PlacementZone) else zone
         snapshot_id = snapshot.id if isinstance(
@@ -224,7 +227,7 @@ class AWSVolumeService(BaseVolumeService):
                                  SnapshotId=snapshot_id)
         # Wait until ready to tag instance
         cb_vol.wait_till_ready()
-        cb_vol.name = name
+        cb_vol.label = label
         if description:
             cb_vol.description = description
         return cb_vol
@@ -244,34 +247,34 @@ class AWSSnapshotService(BaseSnapshotService):
         return self.svc.get(snapshot_id)
 
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        label = kwargs.pop('label', None)
 
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs, 'label'))
 
-        log.debug("Searching for AWS Snapshot Service %s", name)
-        return self.svc.find(filter_name='tag:Name', filter_value=name)
+        log.debug("Searching for AWS Snapshot Service %s", label)
+        return self.svc.find(filter_name='tag:Name', filter_value=label)
 
     def list(self, limit=None, marker=None):
         return self.svc.list(limit=limit, marker=marker)
 
-    def create(self, name, volume, description=None):
+    def create(self, volume, label=None, description=None):
         """
         Creates a new snapshot of a given volume.
         """
         log.debug("Creating a new AWS snapshot Service with the "
-                  "parameters [name: %s volume: %s description: %s]",
-                  name, volume, description)
-        AWSSnapshot.assert_valid_resource_name(name)
+                  "parameters [label: %s volume: %s description: %s]",
+                  label, volume, description)
+        AWSSnapshot.assert_valid_resource_label(label)
 
         volume_id = volume.id if isinstance(volume, AWSVolume) else volume
 
         cb_snap = self.svc.create('create_snapshot', VolumeId=volume_id)
         # Wait until ready to tag instance
         cb_snap.wait_till_ready()
-        cb_snap.name = name
+        cb_snap.label = label
         if cb_snap.description:
             cb_snap.description = description
         return cb_snap
@@ -328,7 +331,7 @@ class AWSBucketService(BaseBucketService):
     def create(self, name, location=None):
         log.debug("Creating AWS Bucket with the params "
                   "[name: %s, location: %s]", name, location)
-        AWSBucket.assert_valid_resource_name(name)
+        AWSBucket.assert_valid_resource_label(name)
         loc_constraint = location or self.provider.region_name
         # Due to an API issue in S3, specifying us-east-1 as a
         # LocationConstraint results in an InvalidLocationConstraint.
@@ -356,15 +359,15 @@ class AWSImageService(BaseImageService):
         return self.svc.get(image_id)
 
     def find(self, **kwargs):
+        # Filter by name or label
         name = kwargs.pop('name', None)
-
-        # All kwargs should have been popped at this time.
-        if len(kwargs) > 0:
-            raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
-
-        log.debug("Searching for AWS Image Service %s", name)
-        return self.svc.find(filter_name='name', filter_value=name)
+        if name:
+            log.debug("Searching for AWS Image Service %s", name)
+            obj_list = self.svc.find(filter_name='name', filter_value=name)
+        else:
+            obj_list = self.svc
+        filters = ['label']
+        return cb_helpers.generic_find(filters, kwargs, obj_list)
 
     def list(self, filter_by_owner=True, limit=None, marker=None):
         return self.svc.list(Owners=['self'] if filter_by_owner else [],
@@ -405,15 +408,15 @@ class AWSInstanceService(BaseInstanceService):
                                   cb_resource=AWSInstance,
                                   boto_collection_name='instances')
 
-    def create(self, name, image, vm_type, subnet, zone,
+    def create(self, image, vm_type, subnet, zone, label=None,
                key_pair=None, vm_firewalls=None, user_data=None,
                launch_config=None, **kwargs):
         log.debug("Creating AWS Instance Service with the params "
-                  "[name: %s image: %s type: %s subnet: %s zone: %s "
+                  "[label: %s image: %s type: %s subnet: %s zone: %s "
                   "key pair: %s firewalls: %s user data: %s config %s "
-                  "others: %s]", name, image, vm_type, subnet, zone,
+                  "others: %s]", label, image, vm_type, subnet, zone,
                   key_pair, vm_firewalls, user_data, launch_config, kwargs)
-        AWSInstance.assert_valid_resource_name(name)
+        AWSInstance.assert_valid_resource_label(label)
 
         image_id = image.id if isinstance(image, MachineImage) else image
         vm_size = vm_type.id if \
@@ -450,7 +453,7 @@ class AWSInstanceService(BaseInstanceService):
             # pylint:disable=protected-access
             inst[0]._wait_till_exists()
             # Tag the instance w/ the name
-            inst[0].name = name
+            inst[0].label = label
             return inst[0]
         raise ValueError(
             'Expected a single object response, got a list: %s' % inst)
@@ -544,14 +547,14 @@ class AWSInstanceService(BaseInstanceService):
         return self.svc.get(instance_id)
 
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        label = kwargs.pop('label', None)
 
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs, 'label'))
 
-        return self.svc.find(filter_name='tag:Name', filter_value=name)
+        return self.svc.find(filter_name='tag:Name', filter_value=label)
 
     def list(self, limit=None, marker=None):
         return self.svc.list(limit=limit, marker=marker)
@@ -653,26 +656,26 @@ class AWSNetworkService(BaseNetworkService):
         return self.svc.list(limit=limit, marker=marker)
 
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        label = kwargs.pop('label', None)
 
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs, 'label'))
 
-        log.debug("Searching for AWS Network Service %s", name)
-        return self.svc.find(filter_name='tag:Name', filter_value=name)
+        log.debug("Searching for AWS Network Service %s", label)
+        return self.svc.find(filter_name='tag:Name', filter_value=label)
 
-    def create(self, name, cidr_block):
+    def create(self, cidr_block, label=None):
         log.debug("Creating AWS Network Service with the params "
-                  "[name: %s block: %s]", name, cidr_block)
-        AWSNetwork.assert_valid_resource_name(name)
+                  "[label: %s block: %s]", label, cidr_block)
+        AWSNetwork.assert_valid_resource_label(label)
 
         cb_net = self.svc.create('create_vpc', CidrBlock=cidr_block)
         # Wait until ready to tag instance
         cb_net.wait_till_ready()
-        if name:
-            cb_net.name = name
+        if label:
+            cb_net.label = label
         return cb_net
 
 
@@ -698,21 +701,21 @@ class AWSSubnetService(BaseSubnetService):
             return self.svc.list(limit=limit, marker=marker)
 
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        label = kwargs.pop('label', None)
 
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs, 'label'))
 
-        log.debug("Searching for AWS Subnet Service %s", name)
-        return self.svc.find(filter_name='tag:Name', filter_value=name)
+        log.debug("Searching for AWS Subnet Service %s", label)
+        return self.svc.find(filter_name='tag:Name', filter_value=label)
 
-    def create(self, name, network, cidr_block, zone):
+    def create(self, network, cidr_block, zone, label=None):
         log.debug("Creating AWS Subnet Service with the params "
-                  "[name: %s network: %s block: %s zone: %s]",
-                  name, network, cidr_block, zone)
-        AWSSubnet.assert_valid_resource_name(name)
+                  "[label: %s network: %s block: %s zone: %s]",
+                  label, network, cidr_block, zone)
+        AWSSubnet.assert_valid_resource_label(label)
 
         network_id = network.id if isinstance(network, AWSNetwork) else network
 
@@ -720,8 +723,8 @@ class AWSSubnetService(BaseSubnetService):
                                  VpcId=network_id,
                                  CidrBlock=cidr_block,
                                  AvailabilityZone=zone)
-        if name:
-            subnet.name = name
+        if label:
+            subnet.label = label
         return subnet
 
     def get_or_create_default(self, zone):
@@ -744,17 +747,18 @@ class AWSSubnetService(BaseSubnetService):
             # pylint:disable=protected-access
             for tag in sn._subnet.tags or {}:
                 if (tag.get('Key') == 'Name' and
-                        tag.get('Value') == AWSSubnet.CB_DEFAULT_SUBNET_NAME):
+                        tag.get('Value') == AWSSubnet.CB_DEFAULT_SUBNET_LABEL):
                     return sn
         # No provider-default Subnet exists, try to create it (net + subnets)
         default_net = self.provider.networking.networks.create(
-            name=AWSNetwork.CB_DEFAULT_NETWORK_NAME, cidr_block='10.0.0.0/16')
+            label=AWSNetwork.CB_DEFAULT_NETWORK_LABEL,
+            cidr_block='10.0.0.0/16')
         # Create a subnet in each of the region's zones
         region = self.provider.compute.regions.get(self.provider.region_name)
         default_sn = None
         for i, z in enumerate(region.zones):
-            sn = self.create(AWSSubnet.CB_DEFAULT_SUBNET_NAME, default_net,
-                             '10.0.{0}.0/24'.format(i), z.name)
+            sn = self.create(default_net, '10.0.{0}.0/24'.format(i), z.name,
+                             label=AWSSubnet.CB_DEFAULT_SUBNET_LABEL)
             if zone and zone == z.name:
                 default_sn = sn
         # No specific zone was supplied; return the last created subnet
@@ -782,27 +786,27 @@ class AWSRouterService(BaseRouterService):
         return self.svc.get(router_id)
 
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        label = kwargs.pop('label', None)
 
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs, 'label'))
 
-        log.debug("Searching for AWS Router Service %s", name)
-        return self.svc.find(filter_name='tag:Name', filter_value=name)
+        log.debug("Searching for AWS Router Service %s", label)
+        return self.svc.find(filter_name='tag:Name', filter_value=label)
 
     def list(self, limit=None, marker=None):
         return self.svc.list(limit=limit, marker=marker)
 
-    def create(self, name, network):
+    def create(self, network, label=None):
         log.debug("Creating AWS Router Service with the params "
-                  "[name: %s network: %s]", name, network)
-        AWSRouter.assert_valid_resource_name(name)
+                  "[label: %s network: %s]", label, network)
+        AWSRouter.assert_valid_resource_label(label)
 
         network_id = network.id if isinstance(network, AWSNetwork) else network
 
         cb_router = self.svc.create('create_route_table', VpcId=network_id)
-        if name:
-            cb_router.name = name
+        if label:
+            cb_router.label = label
         return cb_router

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

@@ -19,7 +19,7 @@ from msrestazure.azure_exceptions import CloudError
 import tenacity
 
 from cloudbridge.cloud.interfaces.exceptions import \
-    InvalidNameException, ProviderConnectionException, WaitStateException
+    InvalidLabelException, ProviderConnectionException, WaitStateException
 
 from . import helpers as azure_helpers
 
@@ -343,7 +343,7 @@ class AzureClient(object):
                                    'azure/azure-resource-manager/resource-' \
                                    'manager-storage-account-name-errors\n' \
                                    % cloud_error2
-                            raise InvalidNameException(mess)
+                            raise InvalidLabelException(mess)
                         else:
                             raise cloud_error2
                 else:

+ 115 - 56
cloudbridge/cloud/providers/openstack/resources.py

@@ -99,10 +99,27 @@ class OpenStackMachineImage(BaseMachineImage):
     @property
     def name(self):
         """
-        Get the image name.
+        Get the image identifier.
+        """
+        return self._os_image.id
+
+    @property
+    def label(self):
+        """
+        Get the image label.
         """
         return self._os_image.name
 
+    @label.setter
+    # pylint:disable=arguments-differ
+    def label(self, value):
+        """
+        Set the image label.
+        """
+        self.assert_valid_resource_label(value)
+        self._os_image.name = value
+        self._os_image.update(name=value)
+
     @property
     def description(self):
         """
@@ -280,20 +297,27 @@ class OpenStackInstance(BaseInstance):
         return self._os_instance.id
 
     @property
-    # pylint:disable=arguments-differ
     def name(self):
         """
-        Get the instance name.
+        Get the instance identifier.
+        """
+        return self.id
+
+    @property
+    # pylint:disable=arguments-differ
+    def label(self):
+        """
+        Get the instance label.
         """
         return self._os_instance.name
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
+    def label(self, value):
         """
-        Set the instance name.
+        Set the instance label.
         """
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
 
         self._os_instance.name = value
         self._os_instance.update(name=value)
@@ -417,22 +441,25 @@ class OpenStackInstance(BaseInstance):
         return [fw.id for fw in self.vm_firewalls]
 
     @property
-    def key_pair_name(self):
+    def key_pair_id(self):
         """
-        Get the name of the key pair associated with this instance.
+        Get the id of the key pair associated with this instance.
         """
         return self._os_instance.key_name
 
-    def create_image(self, name):
+    def create_image(self, label=None):
         """
         Create a new image based on this instance.
         """
-        log.debug("Creating OpenStack Image with the name %s", name)
-        self.assert_valid_resource_name(name)
+        log.debug("Creating OpenStack Image with the label %s", label)
+        self.assert_valid_resource_label(label)
+        name = self._generate_name_from_label(label)
 
         image_id = self._os_instance.create_image(name)
-        return OpenStackMachineImage(
+        img = OpenStackMachineImage(
             self._provider, self._provider.compute.images.get(image_id))
+        img.label = label
+        return img
 
     def _get_fip(self, floating_ip):
         """Get a floating IP object based on the supplied ID."""
@@ -508,8 +535,7 @@ class OpenStackRegion(BaseRegion):
 
     @property
     def name(self):
-        return (self._os_region.id if type(self._os_region) == Region else
-                self._os_region)
+        return self.id
 
     @property
     def zones(self):
@@ -557,20 +583,24 @@ class OpenStackVolume(BaseVolume):
         return self._volume.id
 
     @property
-    # pylint:disable=arguments-differ
     def name(self):
+        return self.id
+
+    @property
+    # pylint:disable=arguments-differ
+    def label(self):
         """
-        Get the volume name.
+        Get the volume label.
         """
         return self._volume.name
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
+    def label(self, value):
         """
-        Set the volume name.
+        Set the volume label.
         """
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         self._volume.name = value
         self._volume.update(name=value)
 
@@ -628,12 +658,13 @@ class OpenStackVolume(BaseVolume):
         """
         self._volume.detach()
 
-    def create_snapshot(self, name, description=None):
+    def create_snapshot(self, label=None, description=None):
         """
         Create a snapshot of this Volume.
         """
         log.debug("Creating snapchat of volume: %s with the "
-                  "description: %s", name, description)
+                  "description: %s", label, description)
+        name = self._generate_name_from_label(label)
         return self._provider.storage.snapshots.create(
             name, self, description=description)
 
@@ -683,20 +714,24 @@ class OpenStackSnapshot(BaseSnapshot):
         return self._snapshot.id
 
     @property
-    # pylint:disable=arguments-differ
     def name(self):
+        return self.id
+
+    @property
+    # pylint:disable=arguments-differ
+    def label(self):
         """
-        Get the snapshot name.
+        Get the snapshot label.
         """
         return self._snapshot.name
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
+    def label(self, value):
         """
-        Set the snapshot name.
+        Set the snapshot label.
         """
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         self._snapshot.name = value
         self._snapshot.update(name=value)
 
@@ -750,13 +785,14 @@ class OpenStackSnapshot(BaseSnapshot):
         """
         Create a new Volume from this Snapshot.
         """
-        vol_name = "from_snap_{0}".format(self.id or self.name)
+        vol_label = "from_snap_{0}".format(self.id or self.label)
+        name = self._generate_name_from_label(vol_label)
         size = size if size else self._snapshot.size
         os_vol = self._provider.cinder.volumes.create(
-            size, name=vol_name, availability_zone=placement,
+            size, name=name, availability_zone=placement,
             snapshot_id=self._snapshot.id)
         cb_vol = OpenStackVolume(self._provider, os_vol)
-        cb_vol.name = vol_name
+        cb_vol.label = vol_label
         return cb_vol
 
 
@@ -774,7 +810,7 @@ class OpenStackGatewayContainer(BaseGatewayContainer):
         # all available networks and perform an assignment test to infer valid
         # floating ip nets.
         dummy_router = self._provider.networking.routers.create(
-            network=self._network, name='cb_conn_test_router')
+            network=self._network, label='cb_conn_test_router')
         with cb_helpers.cleanup_action(lambda: dummy_router.delete()):
             try:
                 dummy_router.attach_gateway(external_net)
@@ -782,10 +818,10 @@ class OpenStackGatewayContainer(BaseGatewayContainer):
             except Exception:
                 return False
 
-    def get_or_create_inet_gateway(self, name=None):
+    def get_or_create_inet_gateway(self, label=None):
         """For OS, inet gtw is any net that has `external` property set."""
-        if name:
-            OpenStackInternetGateway.assert_valid_resource_name(name)
+        if label:
+            OpenStackInternetGateway.assert_valid_resource_label(label)
 
         external_nets = (n for n in self._provider.networking.networks
                          if n.external)
@@ -833,14 +869,18 @@ class OpenStackNetwork(BaseNetwork):
 
     @property
     def name(self):
+        return self.id
+
+    @property
+    def label(self):
         return self._network.get('name', None)
 
-    @name.setter
-    def name(self, value):  # pylint:disable=arguments-differ
+    @label.setter
+    def label(self, value):  # pylint:disable=arguments-differ
         """
-        Set the network name.
+        Set the network label.
         """
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         self._provider.neutron.update_network(self.id,
                                               {'network': {'name': value}})
         self.refresh()
@@ -910,14 +950,18 @@ class OpenStackSubnet(BaseSubnet):
 
     @property
     def name(self):
+        return self.id
+
+    @property
+    def label(self):
         return self._subnet.get('name', None)
 
-    @name.setter
-    def name(self, value):  # pylint:disable=arguments-differ
+    @label.setter
+    def label(self, value):  # pylint:disable=arguments-differ
         """
-        Set the subnet name.
+        Set the subnet label.
         """
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         self._provider.neutron.update_subnet(
             self.id, {'subnet': {'name': value}})
         self._subnet['name'] = value
@@ -1032,14 +1076,18 @@ class OpenStackRouter(BaseRouter):
 
     @property
     def name(self):
+        return self.id
+
+    @property
+    def label(self):
         return self._router.get('name', None)
 
-    @name.setter
-    def name(self, value):  # pylint:disable=arguments-differ
+    @label.setter
+    def label(self, value):  # pylint:disable=arguments-differ
         """
-        Set the router name.
+        Set the router label.
         """
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         self._provider.neutron.update_router(
             self.id, {'router': {'name': value}})
         self.refresh()
@@ -1112,12 +1160,16 @@ class OpenStackInternetGateway(BaseInternetGateway):
 
     @property
     def name(self):
+        return self.id
+
+    @property
+    def label(self):
         return self._gateway_net.get('name', None)
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
-        self.assert_valid_resource_name(value)
+    def label(self, value):
+        self.assert_valid_resource_label(value)
         self._provider.neutron.update_network(self.id,
                                               {'network': {'name': value}})
         self.refresh()
@@ -1173,12 +1225,19 @@ class OpenStackVMFirewall(BaseVMFirewall):
 
     @property
     def name(self):
+        """
+        Return the name of this VM firewall.
+        """
+        return self.id
+
+    @property
+    def label(self):
         return self._vm_firewall.name
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
-        self.assert_valid_resource_name(value)
+    def label(self, value):
+        self.assert_valid_resource_label(value)
         self._provider.os_conn.network.update_security_group(self.id,
                                                              name=value)
         self.refresh()
@@ -1319,7 +1378,7 @@ class OpenStackBucketObject(BaseBucketObject):
     @property
     def name(self):
         """Get this object's name."""
-        return self._obj.get("name")
+        return self.id
 
     @property
     def size(self):
@@ -1432,7 +1491,7 @@ class OpenStackBucket(BaseBucket):
 
     @property
     def name(self):
-        return self._bucket.get("name")
+        return self.id
 
     @property
     def objects(self):

+ 67 - 75
cloudbridge/cloud/providers/openstack/services.py

@@ -47,7 +47,6 @@ from cloudbridge.cloud.providers.openstack import helpers as oshelpers
 
 from .resources import OpenStackBucket
 from .resources import OpenStackInstance
-from .resources import OpenStackInternetGateway
 from .resources import OpenStackKeyPair
 from .resources import OpenStackMachineImage
 from .resources import OpenStackNetwork
@@ -170,7 +169,7 @@ class OpenStackKeyPairService(BaseKeyPairService):
 
     def create(self, name, public_key_material=None):
         log.debug("Creating a new key pair with the name: %s", name)
-        OpenStackKeyPair.assert_valid_resource_name(name)
+        OpenStackKeyPair.assert_valid_resource_label(name)
 
         existing_kp = self.find(name=name)
         if existing_kp:
@@ -211,27 +210,27 @@ class OpenStackVMFirewallService(BaseVMFirewallService):
         return ClientPagedResultList(self.provider, firewalls,
                                      limit=limit, marker=marker)
 
-    def create(self, name, description, network_id):
-        OpenStackVMFirewall.assert_valid_resource_name(name)
+    def create(self, description, network_id, label=None):
+        OpenStackVMFirewall.assert_valid_resource_label(label)
         log.debug("Creating OpenStack VM Firewall with the params: "
-                  "[name: %s network id: %s description: %s]", name,
+                  "[label: %s network id: %s description: %s]", label,
                   network_id, description)
         sg = self.provider.os_conn.network.create_security_group(
-            name=name, description=description)
+            name=label, description=description)
         if sg:
             return OpenStackVMFirewall(self.provider, sg)
         return None
 
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        label = kwargs.pop('label', None)
 
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs, 'label'))
 
-        log.debug("Searching for %s", name)
-        sgs = [self.provider.os_conn.network.find_security_group(name)]
+        log.debug("Searching for %s", label)
+        sgs = [self.provider.os_conn.network.find_security_group(label)]
         results = [OpenStackVMFirewall(self.provider, sg)
                    for sg in sgs if sg]
         return ClientPagedResultList(self.provider, results)
@@ -263,19 +262,14 @@ class OpenStackImageService(BaseImageService):
 
     def find(self, **kwargs):
         name = kwargs.pop('name', None)
-
-        # All kwargs should have been popped at this time.
-        if len(kwargs) > 0:
-            raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
-
-        log.debug("Searching for the OpenStack image with the name: %s", name)
-        regex = fnmatch.translate(name)
+        label = kwargs.pop('label', None)
+        log.debug("Searching for OpenStack image with label: %s", label)
+        regex = fnmatch.translate(label)
         cb_images = [
             img
             for img in self
-            if img.name and re.search(regex, img.name)]
-
+            if ((img.label and re.search(regex, img.label))
+                or img.name == name)]
         return oshelpers.to_server_paged_list(self.provider, cb_images)
 
     def list(self, filter_by_owner=True, limit=None, marker=None):
@@ -352,15 +346,15 @@ class OpenStackVolumeService(BaseVolumeService):
             return None
 
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        label = kwargs.pop('label', None)
 
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs, 'label'))
 
-        log.debug("Searching for an OpenStack Volume with the name %s", name)
-        search_opts = {'name': name}
+        log.debug("Searching for an OpenStack Volume with the label %s", label)
+        search_opts = {'name': label}
         cb_vols = [
             OpenStackVolume(self.provider, vol)
             for vol in self.provider.cinder.volumes.list(
@@ -382,21 +376,21 @@ class OpenStackVolumeService(BaseVolumeService):
 
         return oshelpers.to_server_paged_list(self.provider, cb_vols, limit)
 
-    def create(self, name, size, zone, snapshot=None, description=None):
+    def create(self, size, zone, label=None, snapshot=None, description=None):
         """
         Creates a new volume.
         """
         log.debug("Creating a new volume with the params: "
-                  "[name: %s size: %s zone: %s snapshot: %s description: %s]",
-                  name, size, zone, snapshot, description)
-        OpenStackVolume.assert_valid_resource_name(name)
+                  "[label: %s size: %s zone: %s snapshot: %s description: %s]",
+                  label, size, zone, snapshot, description)
+        OpenStackVolume.assert_valid_resource_label(label)
 
         zone_id = zone.id if isinstance(zone, PlacementZone) else zone
         snapshot_id = snapshot.id if isinstance(
             snapshot, OpenStackSnapshot) and snapshot else snapshot
 
         os_vol = self.provider.cinder.volumes.create(
-            size, name=name, description=description,
+            size, name=label, description=description,
             availability_zone=zone_id, snapshot_id=snapshot_id)
         return OpenStackVolume(self.provider, os_vol)
 
@@ -420,22 +414,21 @@ class OpenStackSnapshotService(BaseSnapshotService):
             return None
 
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        label = kwargs.pop('label', None)
 
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs, 'label'))
 
-        search_opts = {'name': name,  # TODO: Cinder is ignoring name
+        search_opts = {'name': label,  # TODO: Cinder is ignoring name
                        'limit': oshelpers.os_result_limit(self.provider),
                        'marker': None}
         log.debug("Searching for an OpenStack volume with the following "
                   "params: %s", search_opts)
         cb_snaps = [
             OpenStackSnapshot(self.provider, snap) for
-            snap in self.provider.cinder.volume_snapshots.list(search_opts)
-            if snap.name == name]
+            snap in self.provider.cinder.volume_snapshots.list(search_opts)]
 
         return oshelpers.to_server_paged_list(self.provider, cb_snaps)
 
@@ -451,18 +444,18 @@ class OpenStackSnapshotService(BaseSnapshotService):
                              'marker': marker})]
         return oshelpers.to_server_paged_list(self.provider, cb_snaps, limit)
 
-    def create(self, name, volume, description=None):
+    def create(self, volume, label=None, description=None):
         """
         Creates a new snapshot of a given volume.
         """
-        log.debug("Creating a new snapshot of the %s volume.", name)
-        OpenStackSnapshot.assert_valid_resource_name(name)
+        log.debug("Creating a new snapshot of the %s volume.", label)
+        OpenStackSnapshot.assert_valid_resource_label(label)
 
         volume_id = (volume.id if isinstance(volume, OpenStackVolume)
                      else volume)
 
         os_snap = self.provider.cinder.volume_snapshots.create(
-            volume_id, name=name,
+            volume_id, name=label,
             description=description)
         return OpenStackSnapshot(self.provider, os_snap)
 
@@ -521,7 +514,7 @@ class OpenStackBucketService(BaseBucketService):
         Create a new bucket.
         """
         log.debug("Creating a new OpenStack Bucket with the name: %s", name)
-        OpenStackBucket.assert_valid_resource_name(name)
+        OpenStackBucket.assert_valid_resource_label(name)
 
         self.provider.swift.put_container(name)
         return self.get(name)
@@ -596,12 +589,12 @@ class OpenStackInstanceService(BaseInstanceService):
     def __init__(self, provider):
         super(OpenStackInstanceService, self).__init__(provider)
 
-    def create(self, name, image, vm_type, subnet, zone,
+    def create(self, image, vm_type, subnet, zone, label=None,
                key_pair=None, vm_firewalls=None, user_data=None,
                launch_config=None,
                **kwargs):
         """Create a new virtual machine instance."""
-        OpenStackInstance.assert_valid_resource_name(name)
+        OpenStackInstance.assert_valid_resource_label(label)
 
         image_id = image.id if isinstance(image, MachineImage) else image
         vm_size = vm_type.id if \
@@ -632,7 +625,7 @@ class OpenStackInstanceService(BaseInstanceService):
         nics = None
         if subnet_id:
             log.debug("Creating network port for %s in subnet: %s",
-                      name, subnet_id)
+                      label, subnet_id)
             sg_list = []
             if vm_firewalls:
                 if isinstance(vm_firewalls, list) and \
@@ -640,13 +633,13 @@ class OpenStackInstanceService(BaseInstanceService):
                     sg_list = vm_firewalls
                 else:
                     sg_list = (self.provider.security.vm_firewalls
-                               .find(name=sg) for sg in vm_firewalls)
+                               .find(label=sg) for sg in vm_firewalls)
                     sg_list = (sg[0] for sg in sg_list if sg)
             sg_id_list = [sg.id for sg in sg_list]
             port_def = {
                 "port": {
                     "admin_state_up": True,
-                    "name": name,
+                    "name": label,
                     "network_id": net_id,
                     "fixed_ips": [{"subnet_id": subnet_id}],
                     "security_groups": sg_id_list
@@ -658,13 +651,13 @@ class OpenStackInstanceService(BaseInstanceService):
             if vm_firewalls:
                 if isinstance(vm_firewalls, list) and \
                         isinstance(vm_firewalls[0], VMFirewall):
-                    sg_name_list = [sg.name for sg in vm_firewalls]
+                    sg_name_list = [sg.label for sg in vm_firewalls]
                 else:
                     sg_name_list = vm_firewalls
 
         log.debug("Launching in subnet %s", subnet_id)
         os_instance = self.provider.nova.servers.create(
-            name,
+            label,
             None if self._has_root_device(launch_config) else image_id,
             vm_size,
             min_count=1,
@@ -731,14 +724,14 @@ class OpenStackInstanceService(BaseInstanceService):
         return BaseLaunchConfig(self.provider)
 
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        label = kwargs.pop('label', None)
 
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs, 'label'))
 
-        search_opts = {'name': name}
+        search_opts = {'name': label}
         cb_insts = [
             OpenStackInstance(self.provider, inst)
             for inst in self.provider.nova.servers.list(
@@ -809,26 +802,25 @@ class OpenStackNetworkService(BaseNetworkService):
                                      limit=limit, marker=marker)
 
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        label = kwargs.pop('label', None)
 
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs, 'label'))
 
-        log.debug("Searching for the OpenStack Network with the "
-                  "name: %s", name)
+        log.debug("Searching for OpenStack Network with label: %s", label)
         networks = [OpenStackNetwork(self.provider, network)
                     for network in self.provider.neutron.list_networks(
-                        name=name)
+                        name=label)
                     .get('networks') if network]
         return ClientPagedResultList(self.provider, networks)
 
-    def create(self, name, cidr_block):
+    def create(self, cidr_block, label=None):
         log.debug("Creating OpenStack Network with the params: "
-                  "[name: %s Cinder Block: %s]", name, cidr_block)
-        OpenStackNetwork.assert_valid_resource_name(name)
-
+                  "[label: %s Cinder Block: %s]", label, cidr_block)
+        OpenStackNetwork.assert_valid_resource_label(label)
+        name = OpenStackNetwork._generate_name_from_label(label)
         net_info = {'name': name}
         network = self.provider.neutron.create_network({'network': net_info})
         return OpenStackNetwork(self.provider, network.get('network'))
@@ -856,16 +848,16 @@ class OpenStackSubnetService(BaseSubnetService):
         return ClientPagedResultList(self.provider, subnets,
                                      limit=limit, marker=marker)
 
-    def create(self, name, network, cidr_block, zone):
+    def create(self, network, cidr_block, zone, label=None):
         """zone param is ignored."""
         log.debug("Creating OpenStack Subnet with the params: "
-                  "[Name: %s Network: %s Cinder Block: %s Zone: -ignored-]",
-                  name, network, cidr_block)
-        OpenStackSubnet.assert_valid_resource_name(name)
+                  "[Label: %s Network: %s Cinder Block: %s Zone: -ignored-]",
+                  label, network, cidr_block)
+        OpenStackSubnet.assert_valid_resource_label(label)
 
         network_id = (network.id if isinstance(network, OpenStackNetwork)
                       else network)
-        subnet_info = {'name': name, 'network_id': network_id,
+        subnet_info = {'name': label, 'network_id': network_id,
                        'cidr': cidr_block, 'ip_version': 4}
         subnet = (self.provider.neutron.create_subnet({'subnet': subnet_info})
                   .get('subnet'))
@@ -876,20 +868,20 @@ class OpenStackSubnetService(BaseSubnetService):
         Subnet zone is not supported by OpenStack and is thus ignored.
         """
         try:
-            sn = self.find(name=OpenStackSubnet.CB_DEFAULT_SUBNET_NAME)
+            sn = self.find(label=OpenStackSubnet.CB_DEFAULT_SUBNET_LABEL)
             if sn:
                 return sn[0]
             # No default; create one
             net = self.provider.networking.networks.create(
-                name=OpenStackNetwork.CB_DEFAULT_NETWORK_NAME,
+                label=OpenStackNetwork.CB_DEFAULT_NETWORK_LABEL,
                 cidr_block='10.0.0.0/16')
-            sn = net.create_subnet(name=OpenStackSubnet.CB_DEFAULT_SUBNET_NAME,
-                                   cidr_block='10.0.0.0/24')
+            sn = net.create_subnet(
+                label=OpenStackSubnet.CB_DEFAULT_SUBNET_LABEL,
+                cidr_block='10.0.0.0/24')
             router = self.provider.networking.routers.create(
-                network=net, name=OpenStackRouter.CB_DEFAULT_ROUTER_NAME)
+                network=net, label=OpenStackRouter.CB_DEFAULT_ROUTER_LABEL)
             router.attach_subnet(sn)
-            gteway = net.gateways.get_or_create_inet_gateway(
-                        OpenStackInternetGateway.CB_DEFAULT_INET_GATEWAY_NAME)
+            gteway = net.gateways.get_or_create_inet_gateway()
             router.attach_gateway(gteway)
             return sn
         except NeutronClientException:
@@ -924,11 +916,11 @@ class OpenStackRouterService(BaseRouterService):
 
     def find(self, **kwargs):
         obj_list = self
-        filters = ['name']
+        filters = ['label']
         matches = cb_helpers.generic_find(filters, kwargs, obj_list)
         return ClientPagedResultList(self._provider, list(matches))
 
-    def create(self, name, network):
+    def create(self, network, label=None):
         """
         ``network`` is not used by OpenStack.
 
@@ -936,9 +928,9 @@ class OpenStackRouterService(BaseRouterService):
         https://developer.openstack.org/api-ref/networking/v2/
             ?expanded=delete-router-detail,create-router-detail#create-router
         """
-        log.debug("Creating OpenStack Router with the name: %s", name)
-        OpenStackRouter.assert_valid_resource_name(name)
+        log.debug("Creating OpenStack Router with the label: %s", label)
+        OpenStackRouter.assert_valid_resource_label(label)
 
-        body = {'router': {'name': name}} if name else None
+        body = {'router': {'name': label}} if label else None
         router = self.provider.neutron.create_router(body)
         return OpenStackRouter(self.provider, router.get('router'))

+ 2 - 2
docs/api_docs/cloud/exceptions.rst

@@ -23,9 +23,9 @@ ProviderConnectionException
 .. autoclass:: cloudbridge.cloud.interfaces.exceptions.ProviderConnectionException
     :members:
 
-InvalidNameException
+InvalidLabelException
 -----------------------------
-.. autoclass:: cloudbridge.cloud.interfaces.exceptions.InvalidNameException
+.. autoclass:: cloudbridge.cloud.interfaces.exceptions.InvalidLabelException
     :members:
 
 InvalidValueException

+ 11 - 11
test/helpers/__init__.py

@@ -112,14 +112,14 @@ def get_provider_test_data(provider, key):
     return None
 
 
-def create_test_network(provider, name):
+def create_test_network(provider, label):
     """
     Create a network with one subnet, returning the network and subnet objects.
     """
-    net = provider.networking.networks.create(name=name,
+    net = provider.networking.networks.create(label=label,
                                               cidr_block='10.0.0.0/16')
     cidr_block = (net.cidr_block).split('/')[0] or '10.0.0.1'
-    sn = net.create_subnet(cidr_block='{0}/28'.format(cidr_block), name=name,
+    sn = net.create_subnet(cidr_block='{0}/28'.format(cidr_block), label=label,
                            zone=get_provider_test_data(provider, 'placement'))
     return net, sn
 
@@ -134,16 +134,16 @@ def delete_test_network(network):
                 pass
 
 
-def get_test_gateway(provider, name):
+def get_test_gateway(provider, label):
     """
     Get an internet gateway for testing.
 
     This includes creating a network for the gateway, which is also returned.
     """
-    net_name = 'cb_testgwnet-{0}'.format(get_uuid())
+    net_label = 'cb_testgwnet-{0}'.format(get_uuid())
     net = provider.networking.networks.create(
-        name=net_name, cidr_block='10.0.0.0/16')
-    return net, net.gateways.get_or_create_inet_gateway(name)
+        label=net_label, cidr_block='10.0.0.0/16')
+    return net, net.gateways.get_or_create_inet_gateway(label=label)
 
 
 def delete_test_gateway(network, gateway):
@@ -156,13 +156,13 @@ def delete_test_gateway(network, gateway):
 
 
 def create_test_instance(
-        provider, instance_name, subnet, launch_config=None,
+        provider, instance_label, subnet, launch_config=None,
         key_pair=None, vm_firewalls=None, user_data=None):
 
     instance = provider.compute.instances.create(
-        instance_name,
         get_provider_test_data(provider, 'image'),
         get_provider_test_data(provider, 'vm_type'),
+        label=instance_label,
         subnet=subnet,
         zone=get_provider_test_data(provider, 'placement'),
         key_pair=key_pair,
@@ -173,12 +173,12 @@ def create_test_instance(
     return instance
 
 
-def get_test_instance(provider, name, key_pair=None, vm_firewalls=None,
+def get_test_instance(provider, label, key_pair=None, vm_firewalls=None,
                       subnet=None, user_data=None):
     launch_config = None
     instance = create_test_instance(
         provider,
-        name,
+        label,
         subnet=subnet,
         key_pair=key_pair,
         vm_firewalls=vm_firewalls,

+ 72 - 45
test/helpers/standard_interface_tests.py

@@ -8,7 +8,8 @@ This includes:
 import uuid
 
 from cloudbridge.cloud.interfaces.exceptions \
-    import InvalidNameException
+    import InvalidLabelException
+from cloudbridge.cloud.interfaces.resources import LabeledCloudResource
 from cloudbridge.cloud.interfaces.resources import ObjectLifeCycleMixin
 from cloudbridge.cloud.interfaces.resources import ResultList
 
@@ -27,12 +28,15 @@ def check_json(test, obj):
     val = obj.to_json()
     test.assertEqual(val.get('id'), obj.id)
     test.assertEqual(val.get('name'), obj.name)
+    if isinstance(obj, LabeledCloudResource):
+        test.assertEqual(val.get('label'), obj.label)
 
 
 def check_obj_properties(test, obj):
     test.assertEqual(obj, obj, "Object should be equal to itself")
     test.assertFalse(obj != obj, "Object inequality should be false")
     check_obj_name(test, obj)
+    check_obj_label(test, obj)
 
 
 def check_list(test, service, obj):
@@ -66,18 +70,24 @@ def check_iter(test, service, obj):
 
 def check_find(test, service, obj):
     # check find
-    find_objs = service.find(name=obj.name)
+    if isinstance(obj, LabeledCloudResource):
+        find_objs = service.find(label=obj.label)
+    else:
+        find_objs = service.find(name=obj.name)
     test.assertTrue(
         len(find_objs) == 1,
         "Find objects for %s does not return the expected object: %s. Got %s"
-        % (type(obj).__name__, obj.name, find_objs))
+        % (type(obj).__name__, getattr(obj, 'label', obj.name), find_objs))
     test.assertEqual(find_objs[0].id, obj.id)
     return find_objs
 
 
-def check_find_non_existent(test, service):
+def check_find_non_existent(test, service, obj):
     # check find
-    find_objs = service.find(name="random_imagined_obj_name")
+    if isinstance(obj, LabeledCloudResource):
+        find_objs = service.find(label="random_imagined_obj_name")
+    else:
+        find_objs = service.find(name="random_imagined_obj_name")
     test.assertTrue(
         len(find_objs) == 0,
         "Find non-existent object for %s returned unexpected objects: %s"
@@ -113,6 +123,12 @@ def check_delete(test, service, obj, perform_delete=False):
 
 
 def check_obj_name(test, obj):
+    name_property = getattr(type(obj), 'name', None)
+    test.assertIsInstance(name_property, property)
+    test.assertIsNone(name_property.fset, "Name should not have a setter")
+
+
+def check_obj_label(test, obj):
     """
     Cloudbridge identifiers must be 1-63 characters long, and comply with
     RFC1035. In addition, identifiers should contain only lowercase letters,
@@ -120,31 +136,32 @@ def check_obj_name(test, obj):
     characters are allowed.
     """
 
-    # if name has a setter, make sure invalid values cannot be set
-    name_property = getattr(type(obj), 'name', None)
-    if isinstance(name_property, property) and name_property.fset:
+    # if label property exists, make sure invalid values cannot be set
+    label_property = getattr(type(obj), 'label', None)
+    if isinstance(label_property, property):
+        test.assertIsInstance(obj, LabeledCloudResource)
         # setting letters, numbers and international characters should succeed
         # TODO: Unicode characters trip up Moto. Add following: \u0D85\u0200
-        VALID_NAME = u"hello_world-123"
-        original_name = obj.name
-        obj.name = VALID_NAME
+        VALID_LABEL = u"hello_world-123"
+        original_label = obj.label
+        obj.label = VALID_LABEL
         # setting spaces should raise an exception
-        with test.assertRaises(InvalidNameException):
-            obj.name = "hello world"
+        with test.assertRaises(InvalidLabelException):
+            obj.label = "hello world"
         # setting upper case characters should raise an exception
-        with test.assertRaises(InvalidNameException):
-            obj.name = "helloWorld"
+        with test.assertRaises(InvalidLabelException):
+            obj.label = "helloWorld"
         # setting special characters should raise an exception
-        with test.assertRaises(InvalidNameException):
-            obj.name = "hello.world:how_goes_it"
+        with test.assertRaises(InvalidLabelException):
+            obj.label = "hello.world:how_goes_it"
         # setting a length > 63 should result in an exception
-        with test.assertRaises(InvalidNameException,
-                               msg="Name of length > 64 should be disallowed"):
-            obj.name = "a" * 64
-        # refreshing should yield the last successfully set name
+        with test.assertRaises(InvalidLabelException,
+                               msg="Label of length > 64 is not allowed"):
+            obj.label = "a" * 64
+        # refreshing should yield the last successfully set label
         obj.refresh()
-        test.assertEqual(obj.name, VALID_NAME)
-        obj.name = original_name
+        test.assertEqual(obj.label, VALID_LABEL)
+        obj.label = original_label
 
 
 def check_standard_behaviour(test, service, obj):
@@ -158,7 +175,7 @@ def check_standard_behaviour(test, service, obj):
     objs_list = check_list(test, service, obj)
     objs_iter = check_iter(test, service, obj)
     objs_find = check_find(test, service, obj)
-    check_find_non_existent(test, service)
+    check_find_non_existent(test, service, obj)
     obj_get = check_get(test, service, obj)
     check_get_non_existent(test, service)
 
@@ -178,29 +195,39 @@ def check_standard_behaviour(test, service, obj):
                                            objs_find[0].id, obj_get.id,
                                            obj.id))
 
+    if isinstance(obj, LabeledCloudResource):
+        test.assertTrue(
+            obj.label == objs_list[0].label == objs_iter[0].label ==
+            objs_find[0].label == obj_get.label,
+            "Labels returned by list: {0}, iter: {1}, find: {2} and get: {3} "
+            " are not as expected: {4}".format(objs_list[0].id,
+                                               objs_iter[0].id,
+                                               objs_find[0].id, obj_get.id,
+                                               obj.id))
+
 
 def check_create(test, service, iface, name_prefix,
                  create_func, cleanup_func):
-    # check create with invalid name
-    with test.assertRaises(InvalidNameException):
+    # check create with invalid label
+    with test.assertRaises(InvalidLabelException):
         # spaces should raise an exception
         create_func("hello world")
-    # check create with invalid name
-    with test.assertRaises(InvalidNameException):
+    # check create with invalid label
+    with test.assertRaises(InvalidLabelException):
         # uppercase characters should raise an exception
         create_func("helloWorld")
     # setting special characters should raise an exception
-    with test.assertRaises(InvalidNameException):
+    with test.assertRaises(InvalidLabelException):
         create_func("hello.world:how_goes_it")
     # setting a length > 63 should result in an exception
-    with test.assertRaises(InvalidNameException,
-                           msg="Name of length > 64 should be disallowed"):
+    with test.assertRaises(InvalidLabelException,
+                           msg="Label of length > 63 should be disallowed"):
         create_func("a" * 64)
 
 
-def check_crud(test, service, iface, name_prefix,
+def check_crud(test, service, iface, label_prefix,
                create_func, cleanup_func, extra_test_func=None,
-               custom_check_delete=None, skip_name_check=False):
+               custom_check_delete=None, skip_label_check=False):
     """
     Checks crud behaviour of a given cloudbridge service. The create_func will
     be used as a factory function to create a service object and the
@@ -219,14 +246,14 @@ def check_crud(test, service, iface, name_prefix,
     :param iface: The type to test behaviour against. This type must be a
                   subclass of ``CloudResource``.
 
-    :type  name_prefix: ``str``
-    :param name_prefix: The name to prefix all created objects with. This
-                        function will generated a new name with the
-                        specified name_prefix for each test object created
-                        and pass that name into the create_func
+    :type  label_prefix: ``str``
+    :param label_prefix: The label to prefix all created objects with. This
+                        function will generated a new label with the
+                        specified label_prefix for each test object created
+                        and pass that label into the create_func
 
     :type  create_func: ``func``
-    :param create_func: The create_func must accept the name of the object to
+    :param create_func: The create_func must accept the label of the object to
                         create as a parameter and return the constructed
                         object.
 
@@ -246,18 +273,18 @@ def check_crud(test, service, iface, name_prefix,
                                 instead of the standard check_delete function
                                 to make sure that the object has been deleted.
 
-    :type  skip_name_check: ``boolean``
-    :param skip_name_check:  If True, the invalid name checking will be
+    :type  skip_label_check: ``boolean``
+    :param skip_label_check:  If True, the invalid label checking will be
                              skipped.
     """
 
     obj = None
     with helpers.cleanup_action(lambda: cleanup_func(obj)):
-        if not skip_name_check:
-            check_create(test, service, iface, name_prefix,
+        if not skip_label_check:
+            check_create(test, service, iface, label_prefix,
                          create_func, cleanup_func)
-        name = "{0}-{1}".format(name_prefix, helpers.get_uuid())
-        obj = create_func(name)
+        label = "{0}-{1}".format(label_prefix, helpers.get_uuid())
+        obj = create_func(label)
         if issubclass(iface, ObjectLifeCycleMixin):
             obj.wait_till_ready()
         check_standard_behaviour(test, service, obj)

+ 31 - 31
test/test_block_store_service.py

@@ -26,11 +26,11 @@ class CloudBlockStoreServiceTestCase(ProviderTestBase):
         Create a new volume, check whether the expected values are set,
         and delete it
         """
-        def create_vol(name):
+        def create_vol(label):
             return self.provider.storage.volumes.create(
-                name,
                 1,
-                helpers.get_provider_test_data(self.provider, "placement"))
+                helpers.get_provider_test_data(self.provider, "placement"),
+                label=label)
 
         def cleanup_vol(vol):
             if vol:
@@ -46,7 +46,7 @@ class CloudBlockStoreServiceTestCase(ProviderTestBase):
         """
         Create a new volume, and attempt to attach it to an instance
         """
-        name = "cb_attachvol-{0}".format(helpers.get_uuid())
+        label = "cb_attachvol-{0}".format(helpers.get_uuid())
         # Declare these variables and late binding will allow
         # the cleanup method access to the most current values
         net = None
@@ -54,12 +54,12 @@ class CloudBlockStoreServiceTestCase(ProviderTestBase):
         with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
                 test_instance, net)):
             net, subnet = helpers.create_test_network(
-                self.provider, name)
+                self.provider, label)
             test_instance = helpers.get_test_instance(
-                self.provider, name, subnet=subnet)
+                self.provider, label, subnet=subnet)
 
             test_vol = self.provider.storage.volumes.create(
-                name, 1, test_instance.zone_id)
+                1, test_instance.zone_id, label=label)
             with helpers.cleanup_action(lambda: test_vol.delete()):
                 test_vol.wait_till_ready()
                 test_vol.attach(test_instance, '/dev/sda2')
@@ -76,7 +76,7 @@ class CloudBlockStoreServiceTestCase(ProviderTestBase):
         """
         Test volume properties
         """
-        name = "cb_volprops-{0}".format(helpers.get_uuid())
+        label = "cb_volprops-{0}".format(helpers.get_uuid())
         vol_desc = 'newvoldesc1'
         # Declare these variables and late binding will allow
         # the cleanup method access to the most current values
@@ -85,12 +85,12 @@ class CloudBlockStoreServiceTestCase(ProviderTestBase):
         with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
                 test_instance, net)):
             net, subnet = helpers.create_test_network(
-                self.provider, name)
+                self.provider, label)
             test_instance = helpers.get_test_instance(
-                self.provider, name, subnet=subnet)
+                self.provider, label, subnet=subnet)
 
             test_vol = self.provider.storage.volumes.create(
-                name, 1, test_instance.zone_id, description=vol_desc)
+                1, test_instance.zone_id, label=label, description=vol_desc)
             with helpers.cleanup_action(lambda: test_vol.delete()):
                 test_vol.wait_till_ready()
                 self.assertTrue(
@@ -121,11 +121,11 @@ class CloudBlockStoreServiceTestCase(ProviderTestBase):
                     self.assertEqual(test_vol.attachments.device,
                                      "/dev/sda2")
                 test_vol.detach()
-                test_vol.name = 'newvolname1'
+                test_vol.label = 'newvolname1'
                 test_vol.wait_for(
                     [VolumeState.AVAILABLE],
                     terminal_states=[VolumeState.ERROR, VolumeState.DELETED])
-                self.assertEqual(test_vol.name, 'newvolname1')
+                self.assertEqual(test_vol.label, 'newvolname1')
                 self.assertEqual(test_vol.description, vol_desc)
                 self.assertIsNone(test_vol.attachments)
                 test_vol.wait_for(
@@ -139,17 +139,17 @@ class CloudBlockStoreServiceTestCase(ProviderTestBase):
         whether list_snapshots properly detects the new snapshot.
         Delete everything afterwards.
         """
-        name = "cb_crudsnap-{0}".format(helpers.get_uuid())
+        label = "cb_crudsnap-{0}".format(helpers.get_uuid())
         test_vol = self.provider.storage.volumes.create(
-            name,
             1,
-            helpers.get_provider_test_data(self.provider, "placement"))
+            helpers.get_provider_test_data(self.provider, "placement"),
+            label=label)
         with helpers.cleanup_action(lambda: test_vol.delete()):
             test_vol.wait_till_ready()
 
-            def create_snap(name):
-                return test_vol.create_snapshot(name=name,
-                                                description=name)
+            def create_snap(label):
+                return test_vol.create_snapshot(label=label,
+                                                description=label)
 
             def cleanup_snap(snap):
                 if snap:
@@ -161,9 +161,9 @@ class CloudBlockStoreServiceTestCase(ProviderTestBase):
                            "cb_snap", create_snap, cleanup_snap)
 
             # Test creation of a snap via SnapshotService
-            def create_snap2(name):
+            def create_snap2(label):
                 return self.provider.storage.snapshots.create(
-                    name=name, volume=test_vol, description=name)
+                    label=label, volume=test_vol, description=label)
 
             if (self.provider.PROVIDER_ID == ProviderList.AWS and
                     not isinstance(self.provider, TestMockHelperMixin)):
@@ -176,16 +176,16 @@ class CloudBlockStoreServiceTestCase(ProviderTestBase):
         """
         Test snapshot properties
         """
-        name = "cb_snapprop-{0}".format(uuid.uuid4())
+        label = "cb_snapprop-{0}".format(uuid.uuid4())
         test_vol = self.provider.storage.volumes.create(
-            name,
             1,
-            helpers.get_provider_test_data(self.provider, "placement"))
+            helpers.get_provider_test_data(self.provider, "placement"),
+            label=label)
         with helpers.cleanup_action(lambda: test_vol.delete()):
             test_vol.wait_till_ready()
-            snap_name = "cb_snap-{0}".format(name)
-            test_snap = test_vol.create_snapshot(name=snap_name,
-                                                 description=snap_name)
+            snap_label = "cb_snap-{0}".format(label)
+            test_snap = test_vol.create_snapshot(label=snap_label,
+                                                 description=snap_label)
 
             def cleanup_snap(snap):
                 if snap:
@@ -207,18 +207,18 @@ class CloudBlockStoreServiceTestCase(ProviderTestBase):
                     % test_vol.description)
                 self.assertEqual(test_vol.id, test_snap.volume_id)
                 self.assertIsNotNone(test_vol.create_time)
-                test_snap.name = 'snapnewname1'
+                test_snap.label = 'snapnewname1'
                 test_snap.description = 'snapnewdescription1'
                 test_snap.refresh()
-                self.assertEqual(test_snap.name, 'snapnewname1')
+                self.assertEqual(test_snap.label, 'snapnewname1')
                 self.assertEqual(test_snap.description, 'snapnewdescription1')
 
                 # Test volume creation from a snapshot (via VolumeService)
-                sv_name = "cb_snapvol_{0}".format(test_snap.name)
+                sv_label = "cb_snapvol_{0}".format(test_snap.name)
                 snap_vol = self.provider.storage.volumes.create(
-                    sv_name,
                     1,
                     helpers.get_provider_test_data(self.provider, "placement"),
+                    label=sv_label,
                     snapshot=test_snap)
                 with helpers.cleanup_action(lambda: snap_vol.delete()):
                     snap_vol.wait_till_ready()

+ 26 - 26
test/test_compute_service.py

@@ -21,16 +21,16 @@ class CloudComputeServiceTestCase(ProviderTestBase):
 
     @helpers.skipIfNoService(['compute.instances', 'networking.networks'])
     def test_crud_instance(self):
-        name = "cb_instcrud-{0}".format(helpers.get_uuid())
+        label = "cb_instcrud-{0}".format(helpers.get_uuid())
         # Declare these variables and late binding will allow
         # the cleanup method access to the most current values
         net = None
         subnet = None
 
-        def create_inst(name):
+        def create_inst(label):
             # Also test whether sending in an empty_dict for user_data
             # results in an automatic conversion to string.
-            return helpers.get_test_instance(self.provider, name,
+            return helpers.get_test_instance(self.provider, label,
                                              subnet=subnet, user_data={})
 
         def cleanup_inst(inst):
@@ -46,11 +46,11 @@ class CloudComputeServiceTestCase(ProviderTestBase):
                     InstanceState.DELETED,
                     InstanceState.UNKNOWN),
                 "Instance %s should have been deleted but still exists." %
-                name)
+                label)
 
         with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
                                                network=net)):
-            net, subnet = helpers.create_test_network(self.provider, name)
+            net, subnet = helpers.create_test_network(self.provider, label)
 
             sit.check_crud(self, self.provider.compute.instances, Instance,
                            "cb_instcrud", create_inst, cleanup_inst,
@@ -67,7 +67,7 @@ class CloudComputeServiceTestCase(ProviderTestBase):
                               'security.vm_firewalls',
                               'security.key_pairs'])
     def test_instance_properties(self):
-        name = "cb_inst_props-{0}".format(helpers.get_uuid())
+        label = "cb_inst_props-{0}".format(helpers.get_uuid())
 
         # Declare these variables and late binding will allow
         # the cleanup method access to the most current values
@@ -77,18 +77,18 @@ class CloudComputeServiceTestCase(ProviderTestBase):
         kp = None
         with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
                 test_instance, net, fw, kp)):
-            net, subnet = helpers.create_test_network(self.provider, name)
-            kp = self.provider.security.key_pairs.create(name=name)
+            net, subnet = helpers.create_test_network(self.provider, label)
+            kp = self.provider.security.key_pairs.create(name=label)
             fw = self.provider.security.vm_firewalls.create(
-                name=name, description=name, network_id=net.id)
+                label=label, description=label, network_id=net.id)
             test_instance = helpers.get_test_instance(self.provider,
-                                                      name, key_pair=kp,
+                                                      label, key_pair=kp,
                                                       vm_firewalls=[fw],
                                                       subnet=subnet)
             self.assertEqual(
-                test_instance.name, name,
-                "Instance name {0} is not equal to the expected name"
-                " {1}".format(test_instance.name, name))
+                test_instance.label, label,
+                "Instance label {0} is not equal to the expected label"
+                " {1}".format(test_instance.label, label))
             image_id = helpers.get_provider_test_data(self.provider, "image")
             self.assertEqual(test_instance.image_id, image_id,
                              "Image id {0} is not equal to the expected id"
@@ -217,21 +217,21 @@ class CloudComputeServiceTestCase(ProviderTestBase):
     @helpers.skipIfNoService(['compute.instances', 'compute.images',
                               'compute.vm_types', 'storage.volumes'])
     def test_block_device_mapping_attachments(self):
-        name = "cb_blkattch-{0}".format(helpers.get_uuid())
+        label = "cb_blkattch-{0}".format(helpers.get_uuid())
 
         if self.provider.PROVIDER_ID == ProviderList.OPENSTACK:
             raise self.skipTest("Not running BDM tests because OpenStack is"
                                 " not stable enough yet")
 
         test_vol = self.provider.storage.volumes.create(
-           name,
            1,
            helpers.get_provider_test_data(self.provider,
-                                          "placement"))
+                                          "placement"),
+           label=label)
         with helpers.cleanup_action(lambda: test_vol.delete()):
             test_vol.wait_till_ready()
-            test_snap = test_vol.create_snapshot(name=name,
-                                                 description=name)
+            test_snap = test_vol.create_snapshot(label=label,
+                                                 description=label)
 
             def cleanup_snap(snap):
                 if snap:
@@ -277,14 +277,14 @@ class CloudComputeServiceTestCase(ProviderTestBase):
                 for _ in range(vm_type.num_ephemeral_disks):
                     lc.add_ephemeral_device()
 
-                net, subnet = helpers.create_test_network(self.provider, name)
+                net, subnet = helpers.create_test_network(self.provider, label)
 
                 with helpers.cleanup_action(lambda:
                                             helpers.delete_test_network(net)):
 
                     inst = helpers.create_test_instance(
                         self.provider,
-                        name,
+                        label,
                         subnet=subnet,
                         launch_config=lc)
 
@@ -302,7 +302,7 @@ class CloudComputeServiceTestCase(ProviderTestBase):
     @helpers.skipIfNoService(['compute.instances', 'networking.networks',
                               'security.vm_firewalls'])
     def test_instance_methods(self):
-        name = "cb_instmethods-{0}".format(helpers.get_uuid())
+        label = "cb_instmethods-{0}".format(helpers.get_uuid())
 
         # Declare these variables and late binding will allow
         # the cleanup method access to the most current values
@@ -311,11 +311,11 @@ class CloudComputeServiceTestCase(ProviderTestBase):
         fw = None
         with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
                 test_inst, net, fw)):
-            net, subnet = helpers.create_test_network(self.provider, name)
-            test_inst = helpers.get_test_instance(self.provider, name,
+            net, subnet = helpers.create_test_network(self.provider, label)
+            test_inst = helpers.get_test_instance(self.provider, label,
                                                   subnet=subnet)
             fw = self.provider.security.vm_firewalls.create(
-                name=name, description=name, network_id=net.id)
+                label=label, description=label, network_id=net.id)
 
             # Check adding a VM firewall to a running instance
             test_inst.add_vm_firewall(fw)
@@ -334,7 +334,7 @@ class CloudComputeServiceTestCase(ProviderTestBase):
                 (fw, test_inst.vm_firewalls))
 
             # check floating ips
-            router = self.provider.networking.routers.create(name, net)
+            router = self.provider.networking.routers.create(net, label=label)
             gateway = None
 
             def cleanup_router(router, gateway):
@@ -346,7 +346,7 @@ class CloudComputeServiceTestCase(ProviderTestBase):
             with helpers.cleanup_action(lambda: cleanup_router(router,
                                                                gateway)):
                 router.attach_subnet(subnet)
-                gateway = net.gateways.get_or_create_inet_gateway(name)
+                gateway = net.gateways.get_or_create_inet_gateway(label=label)
                 router.attach_gateway(gateway)
                 # check whether adding an elastic ip works
                 fip = gateway.floating_ips.create()

+ 6 - 6
test/test_image_service.py

@@ -16,9 +16,9 @@ class CloudImageServiceTestCase(ProviderTestBase):
         """
         Create a new image and check whether that image can be listed.
         This covers waiting till the image is ready, checking that the image
-        name is the expected one and whether list_images is functional.
+        label is the expected one and whether list_images is functional.
         """
-        instance_name = "cb_crudimage-{0}".format(helpers.get_uuid())
+        instance_label = "cb_crudimage-{0}".format(helpers.get_uuid())
 
         # Declare these variables and late binding will allow
         # the cleanup method access to the most current values
@@ -26,8 +26,8 @@ class CloudImageServiceTestCase(ProviderTestBase):
         net = None
         subnet = None
 
-        def create_img(name):
-            return test_instance.create_image(name)
+        def create_img(label):
+            return test_instance.create_image(label=label)
 
         def cleanup_img(img):
             if img:
@@ -44,9 +44,9 @@ class CloudImageServiceTestCase(ProviderTestBase):
         with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
                 test_instance, net)):
             net, subnet = helpers.create_test_network(
-                self.provider, instance_name)
+                self.provider, instance_label)
             test_instance = helpers.get_test_instance(
-                self.provider, instance_name, subnet=subnet)
+                self.provider, instance_label, subnet=subnet)
             sit.check_crud(self, self.provider.compute.images, MachineImage,
                            "cb_listimg", create_img, cleanup_img,
                            extra_test_func=extra_tests)

+ 17 - 17
test/test_network_service.py

@@ -16,9 +16,9 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
     @helpers.skipIfNoService(['networking.networks'])
     def test_crud_network(self):
 
-        def create_net(name):
+        def create_net(label):
             return self.provider.networking.networks.create(
-                name=name, cidr_block='10.0.0.0/16')
+                label=label, cidr_block='10.0.0.0/16')
 
         def cleanup_net(net):
             if net:
@@ -29,10 +29,10 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
 
     @helpers.skipIfNoService(['networking.networks'])
     def test_network_properties(self):
-        name = 'cb_propnetwork-{0}'.format(helpers.get_uuid())
-        subnet_name = 'cb_propsubnet-{0}'.format(helpers.get_uuid())
+        label = 'cb_propnetwork-{0}'.format(helpers.get_uuid())
+        subnet_label = 'cb_propsubnet-{0}'.format(helpers.get_uuid())
         net = self.provider.networking.networks.create(
-            name=name, cidr_block='10.0.0.0/16')
+            label=label, cidr_block='10.0.0.0/16')
         with helpers.cleanup_action(
             lambda: net.delete()
         ):
@@ -50,7 +50,7 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
 
             cidr = '10.0.1.0/24'
             sn = net.create_subnet(
-                name=subnet_name, cidr_block=cidr,
+                label=subnet_label, cidr_block=cidr,
                 zone=helpers.get_provider_test_data(self.provider,
                                                     'placement'))
             with helpers.cleanup_action(lambda: sn.delete()):
@@ -84,9 +84,9 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
         # correct value
         net = None
 
-        def create_subnet(name):
+        def create_subnet(label):
             return self.provider.networking.subnets.create(
-                network=net, cidr_block="10.0.0.0/24", name=name,
+                network=net, cidr_block="10.0.0.0/24", label=label,
                 zone=helpers.get_provider_test_data(
                     self.provider, 'placement'))
 
@@ -94,9 +94,9 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
             if subnet:
                 self.provider.networking.subnets.delete(subnet=subnet)
 
-        net_name = 'cb_crudsubnet-{0}'.format(helpers.get_uuid())
+        net_label = 'cb_crudsubnet-{0}'.format(helpers.get_uuid())
         net = self.provider.networking.networks.create(
-            name=net_name, cidr_block='10.0.0.0/16')
+            label=net_label, cidr_block='10.0.0.0/16')
         with helpers.cleanup_action(
             lambda:
                 self.provider.networking.networks.delete(network_id=net.id)
@@ -108,7 +108,7 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
         net, gw = helpers.get_test_gateway(
             self.provider, 'cb_crudfipgw-{0}'.format(helpers.get_uuid()))
 
-        def create_fip(name):
+        def create_fip(label):
             fip = gw.floating_ips.create()
             return fip
 
@@ -120,7 +120,7 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
                 lambda: helpers.delete_test_gateway(net, gw)):
             sit.check_crud(self, gw.floating_ips, FloatingIP,
                            "cb_crudfip", create_fip, cleanup_fip,
-                           skip_name_check=True)
+                           skip_label_check=True)
 
     def test_floating_ip_properties(self):
         # Check floating IP address
@@ -157,7 +157,7 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
                             router.detach_subnet(subnet)
                             router.detach_gateway(gateway)
 
-        name = 'cb_crudrouter-{0}'.format(helpers.get_uuid())
+        label = 'cb_crudrouter-{0}'.format(helpers.get_uuid())
         # Declare these variables and late binding will allow
         # the cleanup method access to the most current values
         net = None
@@ -166,11 +166,11 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
         gteway = None
         with helpers.cleanup_action(lambda: _cleanup(net, sn, router, gteway)):
             net = self.provider.networking.networks.create(
-                name=name, cidr_block='10.0.0.0/16')
+                label=label, cidr_block='10.0.0.0/16')
             router = self.provider.networking.routers.create(network=net,
-                                                             name=name)
+                                                             label=label)
             cidr = '10.0.1.0/24'
-            sn = net.create_subnet(name=name, cidr_block=cidr,
+            sn = net.create_subnet(label=label, cidr_block=cidr,
                                    zone=helpers.get_provider_test_data(
                                        self.provider, 'placement'))
 
@@ -188,7 +188,7 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
 #                     router.id, router.network_id))
 
             router.attach_subnet(sn)
-            gteway = net.gateways.get_or_create_inet_gateway(name)
+            gteway = net.gateways.get_or_create_inet_gateway(label=label)
             router.attach_gateway(gteway)
             # TODO: add a check for routes after that's been implemented
 

+ 3 - 3
test/test_object_life_cycle.py

@@ -14,11 +14,11 @@ class CloudObjectLifeCycleTestCase(ProviderTestBase):
         """
         Test object life cycle methods by using a volume.
         """
-        name = "cb_objlifecycle-{0}".format(helpers.get_uuid())
+        label = "cb_objlifecycle-{0}".format(helpers.get_uuid())
         test_vol = self.provider.storage.volumes.create(
-            name,
             1,
-            helpers.get_provider_test_data(self.provider, "placement"))
+            helpers.get_provider_test_data(self.provider, "placement"),
+            label=label)
 
         # Waiting for an invalid timeout should raise an exception
         with self.assertRaises(AssertionError):

+ 7 - 7
test/test_object_store_service.py

@@ -8,7 +8,7 @@ from unittest import skip
 
 import requests
 
-from cloudbridge.cloud.interfaces.exceptions import InvalidNameException
+from cloudbridge.cloud.interfaces.exceptions import InvalidLabelException
 from cloudbridge.cloud.interfaces.provider import TestMockHelperMixin
 from cloudbridge.cloud.interfaces.resources import Bucket
 from cloudbridge.cloud.interfaces.resources import BucketObject
@@ -36,25 +36,25 @@ class CloudObjectStoreServiceTestCase(ProviderTestBase):
             if bucket:
                 bucket.delete()
 
-        with self.assertRaises(InvalidNameException):
+        with self.assertRaises(InvalidLabelException):
             # underscores are not allowed in bucket names
             create_bucket("cb_bucket")
 
-        with self.assertRaises(InvalidNameException):
+        with self.assertRaises(InvalidLabelException):
             # names of length less than 3 should raise an exception
             create_bucket("cb")
 
-        with self.assertRaises(InvalidNameException):
+        with self.assertRaises(InvalidLabelException):
             # names of length greater than 63 should raise an exception
             create_bucket("a" * 64)
 
-        with self.assertRaises(InvalidNameException):
+        with self.assertRaises(InvalidLabelException):
             # bucket name cannot be an IP address
             create_bucket("197.10.100.42")
 
         sit.check_crud(self, self.provider.storage.buckets, Bucket,
                        "cb-crudbucket", create_bucket, cleanup_bucket,
-                       skip_name_check=True)
+                       skip_label_check=True)
 
     @helpers.skipIfNoService(['storage.buckets'])
     def test_crud_bucket_object(self):
@@ -79,7 +79,7 @@ class CloudObjectStoreServiceTestCase(ProviderTestBase):
 
             sit.check_crud(self, test_bucket.objects, BucketObject,
                            "cb_bucketobj", create_bucket_obj,
-                           cleanup_bucket_obj, skip_name_check=True)
+                           cleanup_bucket_obj, skip_label_check=True)
 
     @helpers.skipIfNoService(['storage.buckets'])
     def test_crud_bucket_object_properties(self):

+ 27 - 27
test/test_security_service.py

@@ -60,15 +60,15 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
 
     @helpers.skipIfNoService(['security.vm_firewalls'])
     def test_crud_vm_firewall(self):
-        name = 'cb_crudfw-{0}'.format(helpers.get_uuid())
+        label = 'cb_crudfw-{0}'.format(helpers.get_uuid())
 
         # Declare these variables and late binding will allow
         # the cleanup method access to the most current values
         net = None
 
-        def create_fw(name):
+        def create_fw(label):
             return self.provider.security.vm_firewalls.create(
-                name=name, description=name, network_id=net.id)
+                label=label, description=label, network_id=net.id)
 
         def cleanup_fw(fw):
             if fw:
@@ -76,14 +76,14 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
 
         with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
                 network=net)):
-            net, _ = helpers.create_test_network(self.provider, name)
+            net, _ = helpers.create_test_network(self.provider, label)
 
             sit.check_crud(self, self.provider.security.vm_firewalls,
                            VMFirewall, "cb_crudfw", create_fw, cleanup_fw)
 
     @helpers.skipIfNoService(['security.vm_firewalls'])
     def test_vm_firewall_properties(self):
-        name = 'cb_propfw-{0}'.format(helpers.get_uuid())
+        label = 'cb_propfw-{0}'.format(helpers.get_uuid())
 
         # Declare these variables and late binding will allow
         # the cleanup method access to the most current values
@@ -91,29 +91,29 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
         fw = None
         with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
                 network=net, vm_firewall=fw)):
-            net, _ = helpers.create_test_network(self.provider, name)
+            net, _ = helpers.create_test_network(self.provider, label)
             fw = self.provider.security.vm_firewalls.create(
-                name=name, description=name, network_id=net.id)
+                label=label, description=label, network_id=net.id)
 
-            self.assertEqual(name, fw.description)
+            self.assertEqual(label, fw.description)
 
     @helpers.skipIfNoService(['security.vm_firewalls'])
     def test_crud_vm_firewall_rules(self):
-        name = 'cb_crudfw_rules-{0}'.format(helpers.get_uuid())
+        label = 'cb_crudfw_rules-{0}'.format(helpers.get_uuid())
 
         # Declare these variables and late binding will allow
         # the cleanup method access to the most current values
         net = None
         with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
                 network=net)):
-            net, _ = helpers.create_test_network(self.provider, name)
+            net, _ = helpers.create_test_network(self.provider, label)
 
             fw = None
             with helpers.cleanup_action(lambda: fw.delete()):
                 fw = self.provider.security.vm_firewalls.create(
-                    name=name, description=name, network_id=net.id)
+                    label=label, description=label, network_id=net.id)
 
-                def create_fw_rule(name):
+                def create_fw_rule(label):
                     return fw.rules.create(
                         direction=TrafficDirection.INBOUND, protocol='tcp',
                         from_port=1111, to_port=1111, cidr='0.0.0.0/0')
@@ -124,11 +124,11 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
 
                 sit.check_crud(self, fw.rules, VMFirewallRule, "cb_crudfwrule",
                                create_fw_rule, cleanup_fw_rule,
-                               skip_name_check=True)
+                               skip_label_check=True)
 
     @helpers.skipIfNoService(['security.vm_firewalls'])
     def test_vm_firewall_rule_properties(self):
-        name = 'cb_propfwrule-{0}'.format(helpers.get_uuid())
+        label = 'cb_propfwrule-{0}'.format(helpers.get_uuid())
 
         # Declare these variables and late binding will allow
         # the cleanup method access to the most current values
@@ -136,9 +136,9 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
         fw = None
         with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
                 network=net, vm_firewall=fw)):
-            net, _ = helpers.create_test_network(self.provider, name)
+            net, _ = helpers.create_test_network(self.provider, label)
             fw = self.provider.security.vm_firewalls.create(
-                name=name, description=name, network_id=net.id)
+                label=label, description=label, network_id=net.id)
 
             rule = fw.rules.create(
                 direction=TrafficDirection.INBOUND, protocol='tcp',
@@ -151,7 +151,7 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
 
     @helpers.skipIfNoService(['security.vm_firewalls'])
     def test_vm_firewall_rule_add_twice(self):
-        name = 'cb_fwruletwice-{0}'.format(helpers.get_uuid())
+        label = 'cb_fwruletwice-{0}'.format(helpers.get_uuid())
 
         # Declare these variables and late binding will allow
         # the cleanup method access to the most current values
@@ -160,9 +160,9 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
         with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
                 network=net, vm_firewall=fw)):
 
-            net, _ = helpers.create_test_network(self.provider, name)
+            net, _ = helpers.create_test_network(self.provider, label)
             fw = self.provider.security.vm_firewalls.create(
-                name=name, description=name, network_id=net.id)
+                label=label, description=label, network_id=net.id)
 
             rule = fw.rules.create(
                 direction=TrafficDirection.INBOUND, protocol='tcp',
@@ -175,7 +175,7 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
 
     @helpers.skipIfNoService(['security.vm_firewalls'])
     def test_vm_firewall_group_rule(self):
-        name = 'cb_fwrule-{0}'.format(helpers.get_uuid())
+        label = 'cb_fwrule-{0}'.format(helpers.get_uuid())
 
         # Declare these variables and late binding will allow
         # the cleanup method access to the most current values
@@ -183,9 +183,9 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
         fw = None
         with helpers.cleanup_action(lambda: helpers.cleanup_test_resources(
                 network=net, vm_firewall=fw)):
-            net, _ = helpers.create_test_network(self.provider, name)
+            net, _ = helpers.create_test_network(self.provider, label)
             fw = self.provider.security.vm_firewalls.create(
-                name=name, description=name, network_id=net.id)
+                label=label, description=label, network_id=net.id)
             rules = list(fw.rules)
             self.assertTrue(
                 # TODO: This should be made consistent across all providers.
@@ -200,9 +200,9 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
                 direction=TrafficDirection.INBOUND, src_dest_fw=fw,
                 protocol='tcp', from_port=1, to_port=65535)
             self.assertTrue(
-                rule.src_dest_fw.name == name,
-                "Expected VM firewall rule name {0}. Got {1}."
-                .format(name, rule.src_dest_fw.name))
+                rule.src_dest_fw.label == label,
+                "Expected VM firewall rule label {0}. Got {1}."
+                .format(label, rule.src_dest_fw.label))
             for r in fw.rules:
                 r.delete()
             fw = self.provider.security.vm_firewalls.get(fw.id)  # update
@@ -211,8 +211,8 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
                 "Deleting VMFirewallRule should delete it: {0}".format(
                     fw.rules))
         fwl = self.provider.security.vm_firewalls.list()
-        found_fw = [f for f in fwl if f.name == name]
+        found_fw = [f for f in fwl if f.label == label]
         self.assertTrue(
             len(found_fw) == 0,
             "VM firewall {0} should have been deleted but still exists."
-            .format(name))
+            .format(label))