almahmoud 7 лет назад
Родитель
Сommit
bd9164c7bb

+ 9 - 3
cloudbridge/cloud/base/helpers.py

@@ -1,3 +1,5 @@
+import fnmatch
+import re
 import sys
 import sys
 import traceback
 import traceback
 from contextlib import contextmanager
 from contextlib import contextmanager
@@ -38,9 +40,13 @@ def filter_by(prop_name, kwargs, objs):
     """
     """
     prop_val = kwargs.pop(prop_name, None)
     prop_val = kwargs.pop(prop_name, None)
     if prop_val:
     if prop_val:
-        match = (o for o in objs if getattr(o, prop_name) == prop_val)
-        return match
-    return objs
+        regex = fnmatch.translate(prop_val)
+        results = [o for o in objs
+                   if getattr(o, prop_name)
+                   and re.search(regex, getattr(o, prop_name))]
+    else:
+        return objs
+    return results
 
 
 
 
 def generic_find(filter_names, kwargs, objs):
 def generic_find(filter_names, kwargs, objs):

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

@@ -64,31 +64,31 @@ class CloudResource(object):
         pass
         pass
 
 
     @abstractproperty
     @abstractproperty
-    def display_id(self):
+    def name(self):
         """
         """
         Get the displayable id for the resource.
         Get the displayable 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:
         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.
            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.
            be used in place of the id.
         3. The id is what will uniquely identify a resource, and will be used
         3. The id is what will uniquely identify a resource, and will be used
            internally by cloudbridge for all get operations etc.
            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
            reason why it should not be used for uniquely identifying a
            resource.
            resource.
         Example:
         Example:
-        The AWS machine image name is a display_id. It is not editable and is
+        The AWS machine image name is a name. It is not editable and is
         a user friendly name such as 'Ubuntu 14.04' and corresponds to the
         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
         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
         cloudbridge's id property. The ami-name cannot be edited, and is set
         at creation time. It is not necessarily unique.
         at creation time. It is not necessarily unique.
-        In Azure, the machine image's display_id corresponds to the name
+        In Azure, the machine image's name corresponds to the name
         property. In Azure, it also happens to be the same as the id property.
         property. In Azure, it also happens to be the same as the id property.
         """
         """
         pass
         pass
@@ -488,9 +488,9 @@ class Instance(ObjectLifeCycleMixin, CloudResource):
 
 
     __metaclass__ = ABCMeta
     __metaclass__ = ABCMeta
 
 
-    @CloudResource.name.setter
+    @CloudResource.label.setter
     @abstractmethod
     @abstractmethod
-    def name(self, value):
+    def label(self, value):
         """
         """
         Set the instance name.
         Set the instance name.
 
 
@@ -934,7 +934,7 @@ class Network(ObjectLifeCycleMixin, CloudResource):
         pass
         pass
 
 
     @abstractmethod
     @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.
         Create a new network subnet and associate it with this Network.
 
 
@@ -1294,7 +1294,7 @@ class GatewayContainer(PageableObjectMixin):
     __metaclass__ = ABCMeta
     __metaclass__ = ABCMeta
 
 
     @abstractmethod
     @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.
         Creates new or returns an existing internet gateway for a network.
 
 
@@ -1438,9 +1438,9 @@ class Volume(ObjectLifeCycleMixin, CloudResource):
 
 
     __metaclass__ = ABCMeta
     __metaclass__ = ABCMeta
 
 
-    @CloudResource.name.setter
+    @CloudResource.label.setter
     @abstractmethod
     @abstractmethod
-    def name(self, value):
+    def label(self, value):
         """
         """
         Set the volume name.
         Set the volume name.
 
 
@@ -1623,9 +1623,9 @@ class Snapshot(ObjectLifeCycleMixin, CloudResource):
 
 
     __metaclass__ = ABCMeta
     __metaclass__ = ABCMeta
 
 
-    @CloudResource.name.setter
+    @CloudResource.label.setter
     @abstractmethod
     @abstractmethod
-    def name(self, value):
+    def label(self, value):
         """
         """
         Set the snapshot name.
         Set the snapshot name.
 
 

+ 32 - 3
cloudbridge/cloud/providers/azure/azure_client.py

@@ -2,6 +2,7 @@ import datetime
 import logging
 import logging
 from io import BytesIO
 from io import BytesIO
 
 
+from azure.common import AzureConflictHttpError
 from azure.common.credentials import ServicePrincipalCredentials
 from azure.common.credentials import ServicePrincipalCredentials
 from azure.cosmosdb.table.tableservice import TableService
 from azure.cosmosdb.table.tableservice import TableService
 from azure.mgmt.compute import ComputeManagementClient
 from azure.mgmt.compute import ComputeManagementClient
@@ -19,7 +20,8 @@ from msrestazure.azure_exceptions import CloudError
 import tenacity
 import tenacity
 
 
 from cloudbridge.cloud.interfaces.exceptions import \
 from cloudbridge.cloud.interfaces.exceptions import \
-    InvalidNameException, ProviderConnectionException, WaitStateException
+    DuplicateResourceException, InvalidNameException, \
+    ProviderConnectionException, WaitStateException
 
 
 from . import helpers as azure_helpers
 from . import helpers as azure_helpers
 
 
@@ -43,6 +45,10 @@ PUBLIC_IP_RESOURCE_ID = ['/subscriptions/{subscriptionId}/resourceGroups'
                          '/{resourceGroupName}/providers/Microsoft.Network'
                          '/{resourceGroupName}/providers/Microsoft.Network'
                          '/publicIPAddresses/{publicIpAddressName}',
                          '/publicIPAddresses/{publicIpAddressName}',
                          '{publicIpAddressName}']
                          '{publicIpAddressName}']
+ROUTER_RESOURCE_ID = ['/subscriptions/{subscriptionId}'
+                      '/resourceGroups/{resourceGroupName}'
+                      '/providers/Microsoft.Network/routeTables/{routerName}',
+                      '{routerName}']
 SNAPSHOT_RESOURCE_ID = ['/subscriptions/{subscriptionId}/resourceGroups/'
 SNAPSHOT_RESOURCE_ID = ['/subscriptions/{subscriptionId}/resourceGroups/'
                         '{resourceGroupName}/providers/Microsoft.Compute/'
                         '{resourceGroupName}/providers/Microsoft.Compute/'
                         'snapshots/{snapshotName}',
                         'snapshots/{snapshotName}',
@@ -78,6 +84,7 @@ IMAGE_NAME = 'imageName'
 NETWORK_NAME = 'virtualNetworkName'
 NETWORK_NAME = 'virtualNetworkName'
 NETWORK_INTERFACE_NAME = 'networkInterfaceName'
 NETWORK_INTERFACE_NAME = 'networkInterfaceName'
 PUBLIC_IP_NAME = 'publicIpAddressName'
 PUBLIC_IP_NAME = 'publicIpAddressName'
+ROUTER_NAME = 'routerName'
 SNAPSHOT_NAME = 'snapshotName'
 SNAPSHOT_NAME = 'snapshotName'
 SUBNET_NAME = 'subnetName'
 SUBNET_NAME = 'subnetName'
 VM_NAME = 'vmName'
 VM_NAME = 'vmName'
@@ -405,7 +412,18 @@ class AzureClient(object):
         return self.blob_service.list_containers(prefix=prefix)
         return self.blob_service.list_containers(prefix=prefix)
 
 
     def create_container(self, container_name):
     def create_container(self, container_name):
-        self.blob_service.create_container(container_name)
+        try:
+            self.blob_service.create_container(container_name,
+                                               fail_on_exist=True)
+        except AzureConflictHttpError as cloud_error:
+            if cloud_error.error_code == "ContainerAlreadyExists":
+                msg = "The given Bucket name '%s' already exists. Please " \
+                      "use the `get` or `find` method to get a reference to " \
+                      "an existing Bucket, or specify a new Bucket name to " \
+                      "create.\nNote that in Azure, Buckets are contained " \
+                      "in Storage Accounts." % container_name
+                raise DuplicateResourceException(msg)
+
         return self.blob_service.get_container_properties(container_name)
         return self.blob_service.get_container_properties(container_name)
 
 
     def get_container(self, container_name):
     def get_container(self, container_name):
@@ -694,6 +712,14 @@ class AzureClient(object):
             public_ip_addresses.delete(self.resource_group,
             public_ip_addresses.delete(self.resource_group,
                                        public_ip_name).wait()
                                        public_ip_name).wait()
 
 
+    def update_fip_tags(self, fip_id, tags):
+        url_params = azure_helpers.parse_url(PUBLIC_IP_RESOURCE_ID,
+                                             fip_id)
+        fip_name = url_params.get(PUBLIC_IP_NAME)
+        self.network_management_client.public_ip_addresses. \
+            create_or_update(self.resource_group,
+                             fip_name, tags).result()
+
     def list_floating_ips(self):
     def list_floating_ips(self):
         return self.network_management_client.public_ip_addresses.list(
         return self.network_management_client.public_ip_addresses.list(
             self.resource_group)
             self.resource_group)
@@ -891,8 +917,11 @@ class AzureClient(object):
             route_tables.list(self.resource_group)
             route_tables.list(self.resource_group)
 
 
     def get_route_table(self, router_id):
     def get_route_table(self, router_id):
+        url_params = azure_helpers.parse_url(ROUTER_RESOURCE_ID,
+                                             router_id)
+        router_name = url_params.get(ROUTER_NAME)
         return self.network_management_client. \
         return self.network_management_client. \
-            route_tables.get(self.resource_group, router_id)
+            route_tables.get(self.resource_group, router_name)
 
 
     def create_route_table(self, route_table_name, params):
     def create_route_table(self, route_table_name, params):
         return self.network_management_client. \
         return self.network_management_client. \

+ 188 - 119
cloudbridge/cloud/providers/azure/resources.py

@@ -52,12 +52,16 @@ class AzureVMFirewall(BaseVMFirewall):
 
 
     @property
     @property
     def name(self):
     def name(self):
-        return self._vm_firewall.tags.get('Name', self._vm_firewall.name)
+        return self._vm_firewall.name
 
 
-    @name.setter
-    def name(self, value):
+    @property
+    def label(self):
+        return self._vm_firewall.tags.get('Label', None)
+
+    @label.setter
+    def label(self, value):
         self.assert_valid_resource_name(value)
         self.assert_valid_resource_name(value)
-        self._vm_firewall.tags.update(Name=value)
+        self._vm_firewall.tags.update(Label=value)
         self._provider.azure_client.update_vm_firewall_tags(
         self._provider.azure_client.update_vm_firewall_tags(
             self.id, self._vm_firewall.tags)
             self.id, self._vm_firewall.tags)
 
 
@@ -141,7 +145,7 @@ class AzureVMFirewallRuleContainer(BaseVMFirewallRuleContainer):
             cidr = '0.0.0.0/0'
             cidr = '0.0.0.0/0'
 
 
         count = len(self.firewall._vm_firewall.security_rules) + 1
         count = len(self.firewall._vm_firewall.security_rules) + 1
-        rule_name = "Rule - " + str(count)
+        rule_name = "cb-rule-" + str(count)
         priority = 1000 + count
         priority = 1000 + count
         destination_port_range = str(from_port) + "-" + str(to_port)
         destination_port_range = str(from_port) + "-" + str(to_port)
         source_port_range = '*'
         source_port_range = '*'
@@ -177,15 +181,19 @@ class AzureVMFirewallRule(BaseVMFirewallRule):
     def id(self):
     def id(self):
         return self._rule.id
         return self._rule.id
 
 
+    @property
+    def name(self):
+        return self._rule.name
+
+    @property
+    def label(self):
+        raise NotImplementedError("Azure Firewall Rules do not support labels")
+
     @property
     @property
     def direction(self):
     def direction(self):
         return (TrafficDirection.INBOUND if self._rule.direction == "Inbound"
         return (TrafficDirection.INBOUND if self._rule.direction == "Inbound"
                 else TrafficDirection.OUTBOUND)
                 else TrafficDirection.OUTBOUND)
 
 
-    @property
-    def name(self):
-        return self._rule.name
-
     @property
     @property
     def protocol(self):
     def protocol(self):
         return self._rule.protocol
         return self._rule.protocol
@@ -240,11 +248,12 @@ class AzureBucketObject(BaseBucketObject):
 
 
     @property
     @property
     def name(self):
     def name(self):
-        """
-        Get this object's name.
-        """
         return self._key.name
         return self._key.name
 
 
+    @property
+    def label(self):
+        raise NotImplementedError("Azure Bucket Objects do not support labels")
+
     @property
     @property
     def size(self):
     def size(self):
         """
         """
@@ -267,7 +276,7 @@ class AzureBucketObject(BaseBucketObject):
         iterable.
         iterable.
         """
         """
         content_stream = self._provider.azure_client. \
         content_stream = self._provider.azure_client. \
-            get_blob_content(self._container.name, self._key.name)
+            get_blob_content(self._container.id, self._key.name)
         if content_stream:
         if content_stream:
             content_stream.seek(0)
             content_stream.seek(0)
         return content_stream
         return content_stream
@@ -279,7 +288,7 @@ class AzureBucketObject(BaseBucketObject):
         """
         """
         try:
         try:
             self._provider.azure_client.create_blob_from_text(
             self._provider.azure_client.create_blob_from_text(
-                self._container.name, self.name, data)
+                self._container.id, self.id, data)
             return True
             return True
         except AzureException as azureEx:
         except AzureException as azureEx:
             log.exception(azureEx)
             log.exception(azureEx)
@@ -291,7 +300,7 @@ class AzureBucketObject(BaseBucketObject):
         """
         """
         try:
         try:
             self._provider.azure_client.create_blob_from_file(
             self._provider.azure_client.create_blob_from_file(
-                self._container.name, self.name, path)
+                self._container.id, self.id, path)
             return True
             return True
         except AzureException as azureEx:
         except AzureException as azureEx:
             log.exception(azureEx)
             log.exception(azureEx)
@@ -304,19 +313,19 @@ class AzureBucketObject(BaseBucketObject):
         :rtype: bool
         :rtype: bool
         :return: True if successful
         :return: True if successful
         """
         """
-        self._provider.azure_client.delete_blob(self._container.name,
-                                                self.name)
+        self._provider.azure_client.delete_blob(self._container.id,
+                                                self.id)
 
 
     def generate_url(self, expires_in):
     def generate_url(self, expires_in):
         """
         """
         Generate a URL to this object.
         Generate a URL to this object.
         """
         """
         return self._provider.azure_client.get_blob_url(
         return self._provider.azure_client.get_blob_url(
-            self._container.name, self.name, expires_in)
+            self._container.id, self.id, expires_in)
 
 
     def refresh(self):
     def refresh(self):
         self._key = self._provider.azure_client.get_blob(
         self._key = self._provider.azure_client.get_blob(
-            self._container.name, self._key.name)
+            self._container.id, self._key.id)
 
 
 
 
 class AzureBucket(BaseBucket):
 class AzureBucket(BaseBucket):
@@ -336,6 +345,11 @@ class AzureBucket(BaseBucket):
         """
         """
         return self._bucket.name
         return self._bucket.name
 
 
+    @property
+    def label(self):
+        raise NotImplementedError("Azure Buckets do not support labels")
+
+
     def delete(self, delete_contents=True):
     def delete(self, delete_contents=True):
         """
         """
         Delete this bucket.
         Delete this bucket.
@@ -363,7 +377,8 @@ class AzureBucketContainer(BaseBucketContainer):
         Retrieve a given object from this bucket.
         Retrieve a given object from this bucket.
         """
         """
         try:
         try:
-            obj = self._provider.azure_client.get_blob(self.bucket.name, key)
+            obj = self._provider.azure_client.get_blob(self.bucket.name,
+                                                       key)
             return AzureBucketObject(self._provider, self.bucket, obj)
             return AzureBucketObject(self._provider, self.bucket, obj)
         except AzureException as azureEx:
         except AzureException as azureEx:
             log.exception(azureEx)
             log.exception(azureEx)
@@ -433,28 +448,31 @@ class AzureVolume(BaseVolume):
     def resource_id(self):
     def resource_id(self):
         return self._volume.id
         return self._volume.id
 
 
+    @property
+    def name(self):
+        return self._volume.name
+
     @property
     @property
     def tags(self):
     def tags(self):
         return self._volume.tags
         return self._volume.tags
 
 
     @property
     @property
-    def name(self):
+    def label(self):
         """
         """
-        Get the volume name.
+        Get the volume label.
 
 
-        .. note:: an instance must have a (case sensitive) tag ``Name``
+        .. note:: an instance must have a (case sensitive) tag ``Label``
         """
         """
-        return self._volume.tags.get('Name', self._volume.name)
+        return self._volume.tags.get('Label', None)
 
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
     # pylint:disable=arguments-differ
-    def name(self, value):
+    def label(self, value):
         """
         """
-        Set the volume name.
+        Set the volume label.
         """
         """
-        # self._volume.name = value
         self.assert_valid_resource_name(value)
         self.assert_valid_resource_name(value)
-        self._volume.tags.update(Name=value)
+        self._volume.tags.update(Label=value)
         self._provider.azure_client. \
         self._provider.azure_client. \
             update_disk_tags(self.id,
             update_disk_tags(self.id,
                              self._volume.tags)
                              self._volume.tags)
@@ -587,27 +605,31 @@ class AzureSnapshot(BaseSnapshot):
     def id(self):
     def id(self):
         return self._snapshot.id
         return self._snapshot.id
 
 
+    @property
+    def name(self):
+        return self._snapshot.name
+
     @property
     @property
     def resource_id(self):
     def resource_id(self):
         return self._snapshot.id
         return self._snapshot.id
 
 
     @property
     @property
-    def name(self):
+    def label(self):
         """
         """
-        Get the snapshot name.
+        Get the snapshot label.
 
 
-        .. note:: an instance must have a (case sensitive) tag ``Name``
+        .. note:: an instance must have a (case sensitive) tag ``Label``
         """
         """
-        return self._snapshot.tags.get('Name', self._snapshot.name)
+        return self._snapshot.tags.get('Label', None)
 
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
     # 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_name(value)
-        self._snapshot.tags.update(Name=value)
+        self._snapshot.tags.update(Label=value)
         self._provider.azure_client. \
         self._provider.azure_client. \
             update_snapshot_tags(self.id,
             update_snapshot_tags(self.id,
                                  self._snapshot.tags)
                                  self._snapshot.tags)
@@ -698,39 +720,40 @@ class AzureMachineImage(BaseMachineImage):
         :rtype: ``str``
         :rtype: ``str``
         :return: ID for this instance as returned by the cloud middleware.
         :return: ID for this instance as returned by the cloud middleware.
         """
         """
-        if isinstance(self._image, GalleryImageReference):
+        if self.is_gallery_image:
             return azure_helpers.generate_urn(self._image)
             return azure_helpers.generate_urn(self._image)
         else:
         else:
             return self._image.id
             return self._image.id
 
 
+    @property
+    def name(self):
+        if self.is_gallery_image:
+            return azure_helpers.generate_urn(self._image)
+        else:
+            return self._image.name
+
     @property
     @property
     def resource_id(self):
     def resource_id(self):
-        if isinstance(self._image, GalleryImageReference):
+        if self.is_gallery_image:
             return azure_helpers.generate_urn(self._image)
             return azure_helpers.generate_urn(self._image)
         else:
         else:
             return self._image.id
             return self._image.id
 
 
     @property
     @property
-    def name(self):
-        """
-        Get the image name.
-
-        :rtype: ``str``
-        :return: Name for this image as returned by the cloud middleware.
-        """
-        if isinstance(self._image, GalleryImageReference):
+    def label(self):
+        if self.is_gallery_image:
             return azure_helpers.generate_urn(self._image)
             return azure_helpers.generate_urn(self._image)
         else:
         else:
-            return self._image.tags.get('Name', self._image.name)
+            return self._image.tags.get('Label', None)
 
 
-    @name.setter
-    def name(self, value):
+    @label.setter
+    def label(self, value):
         """
         """
-        Set the image name.
+        Set the image label when it is a private image.
         """
         """
-        if not isinstance(self._image, GalleryImageReference):
+        if not self.is_gallery_image:
             self.assert_valid_resource_name(value)
             self.assert_valid_resource_name(value)
-            self._image.tags.update(Name=value)
+            self._image.tags.update(Label=value)
             self._provider.azure_client. \
             self._provider.azure_client. \
                 update_image_tags(self.id, self._image.tags)
                 update_image_tags(self.id, self._image.tags)
 
 
@@ -742,7 +765,7 @@ class AzureMachineImage(BaseMachineImage):
         :rtype: ``str``
         :rtype: ``str``
         :return: Description for this image as returned by the cloud middleware
         :return: Description for this image as returned by the cloud middleware
         """
         """
-        if isinstance(self._image, GalleryImageReference):
+        if self.is_gallery_image:
             return 'Public gallery image from the Azure Marketplace: '\
             return 'Public gallery image from the Azure Marketplace: '\
                     + self.name
                     + self.name
         else:
         else:
@@ -753,7 +776,7 @@ class AzureMachineImage(BaseMachineImage):
         """
         """
         Set the image description.
         Set the image description.
         """
         """
-        if not isinstance(self._image, GalleryImageReference):
+        if not self.is_gallery_image:
             self._image.tags.update(Description=value)
             self._image.tags.update(Description=value)
             self._provider.azure_client. \
             self._provider.azure_client. \
                 update_image_tags(self.id, self._image.tags)
                 update_image_tags(self.id, self._image.tags)
@@ -769,7 +792,7 @@ class AzureMachineImage(BaseMachineImage):
         :rtype: ``int``
         :rtype: ``int``
         :return: The minimum disk size needed by this image
         :return: The minimum disk size needed by this image
         """
         """
-        if isinstance(self._image, GalleryImageReference):
+        if self.is_gallery_image:
             return 0
             return 0
         else:
         else:
             return self._image.storage_profile.os_disk.disk_size_gb or 0
             return self._image.storage_profile.os_disk.disk_size_gb or 0
@@ -778,12 +801,12 @@ class AzureMachineImage(BaseMachineImage):
         """
         """
         Delete this image
         Delete this image
         """
         """
-        if not isinstance(self._image, GalleryImageReference):
+        if not self.is_gallery_image:
             self._provider.azure_client.delete_image(self.id)
             self._provider.azure_client.delete_image(self.id)
 
 
     @property
     @property
     def state(self):
     def state(self):
-        if isinstance(self._image, GalleryImageReference):
+        if self.is_gallery_image:
             return MachineImageState.AVAILABLE
             return MachineImageState.AVAILABLE
         else:
         else:
             return AzureMachineImage.IMAGE_STATE_MAP.get(
             return AzureMachineImage.IMAGE_STATE_MAP.get(
@@ -802,7 +825,7 @@ class AzureMachineImage(BaseMachineImage):
         Refreshes the state of this instance by re-querying the cloud provider
         Refreshes the state of this instance by re-querying the cloud provider
         for its latest state.
         for its latest state.
         """
         """
-        if not isinstance(self._image, dict):
+        if not self.is_gallery_image:
             try:
             try:
                 self._image = self._provider.azure_client.get_image(self.id)
                 self._image = self._provider.azure_client.get_image(self.id)
                 self._state = self._image.provisioning_state
                 self._state = self._image.provisioning_state
@@ -823,12 +846,8 @@ class AzureGatewayContainer(BaseGatewayContainer):
         self.gateway_singleton = AzureInternetGateway(self._provider, None,
         self.gateway_singleton = AzureInternetGateway(self._provider, None,
                                                       network)
                                                       network)
 
 
-    def get_or_create_inet_gateway(self, name=None):
-        if name:
-            AzureInternetGateway.assert_valid_resource_name(name)
+    def get_or_create_inet_gateway(self, label=None):
         gateway = AzureInternetGateway(self._provider, None, self._network)
         gateway = AzureInternetGateway(self._provider, None, self._network)
-        if name:
-            gateway.name = name
         return gateway
         return gateway
 
 
     def list(self, limit=None, marker=None):
     def list(self, limit=None, marker=None):
@@ -856,27 +875,31 @@ class AzureNetwork(BaseNetwork):
     def id(self):
     def id(self):
         return self._network.id
         return self._network.id
 
 
+    @property
+    def name(self):
+        return self._network.name
+
     @property
     @property
     def resource_id(self):
     def resource_id(self):
         return self._network.id
         return self._network.id
 
 
     @property
     @property
-    def name(self):
+    def label(self):
         """
         """
-        Get the network name.
+        Get the network label.
 
 
-        .. note:: the network must have a (case sensitive) tag ``Name``
+        .. note:: the network must have a (case sensitive) tag ``Label``
         """
         """
-        return self._network.tags.get('Name', self._network.name)
+        return self._network.tags.get('Label', None)
 
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
     # pylint:disable=arguments-differ
-    def name(self, value):
+    def label(self, value):
         """
         """
-        Set the network name.
+        Set the network label.
         """
         """
         self.assert_valid_resource_name(value)
         self.assert_valid_resource_name(value)
-        self._network.tags.update(Name=value)
+        self._network.tags.update(Label=value)
         self._provider.azure_client. \
         self._provider.azure_client. \
             update_network_tags(self.id, self._network)
             update_network_tags(self.id, self._network)
 
 
@@ -930,16 +953,16 @@ class AzureNetwork(BaseNetwork):
         """
         """
         return self._provider.networking.subnets.list(network=self.id)
         return self._provider.networking.subnets.list(network=self.id)
 
 
-    def create_subnet(self, cidr_block, name=None, zone=None):
+    def create_subnet(self, cidr_block, label=None, zone=None):
         """
         """
         Create the subnet with cidr_block
         Create the subnet with cidr_block
         :param cidr_block:
         :param cidr_block:
-        :param name:
+        :param label:
         :param zone:
         :param zone:
         :return:
         :return:
         """
         """
         return self._provider.networking.subnets. \
         return self._provider.networking.subnets. \
-            create(network=self.id, cidr_block=cidr_block, name=name)
+            create(network=self.id, cidr_block=cidr_block, label=label)
 
 
     @property
     @property
     def gateways(self):
     def gateways(self):
@@ -966,13 +989,19 @@ class AzureFloatingIPContainer(BaseFloatingIPContainer):
         return ClientPagedResultList(self._provider, floating_ips,
         return ClientPagedResultList(self._provider, floating_ips,
                                      limit=limit, marker=marker)
                                      limit=limit, marker=marker)
 
 
-    def create(self):
-        public_ip_address_name = "{0}-{1}".format(
-            'public_ip', uuid.uuid4().hex[:6])
+    def create(self, label=None):
         public_ip_parameters = {
         public_ip_parameters = {
             'location': self._provider.azure_client.region_name,
             'location': self._provider.azure_client.region_name,
             'public_ip_allocation_method': 'Static'
             'public_ip_allocation_method': 'Static'
         }
         }
+
+        if label:
+            public_ip_parameters.update(tags={'Label':label})
+        else:
+            label = 'cb-ip-'
+
+        public_ip_address_name = "{0}-{1}".format(
+            label, uuid.uuid4().hex[:6])
         floating_ip = self._provider.azure_client.\
         floating_ip = self._provider.azure_client.\
             create_floating_ip(public_ip_address_name, public_ip_parameters)
             create_floating_ip(public_ip_address_name, public_ip_parameters)
         return AzureFloatingIP(self._provider, floating_ip, self._network_id)
         return AzureFloatingIP(self._provider, floating_ip, self._network_id)
@@ -989,10 +1018,34 @@ class AzureFloatingIP(BaseFloatingIP):
     def id(self):
     def id(self):
         return self._ip.id
         return self._ip.id
 
 
+    @property
+    def name(self):
+        return self._ip.name
+
     @property
     @property
     def resource_id(self):
     def resource_id(self):
         return self._ip.id
         return self._ip.id
 
 
+    @property
+    def label(self):
+        """
+        Get the floating IP label.
+
+        .. note:: an instance must have a (case sensitive) tag ``Label``
+        """
+        return self._ip.tags.get('Label', None)
+
+    @label.setter
+    # pylint:disable=arguments-differ
+    def label(self, value):
+        """
+        Set the floating IP label.
+        """
+        self.assert_valid_resource_name(value)
+        self._ip.tags.update(Label=value)
+        self._provider.azure_client. \
+            update_fip_tags(self.id, self._ip)
+
     @property
     @property
     def public_ip(self):
     def public_ip(self):
         return self._ip.ip_address
         return self._ip.ip_address
@@ -1072,6 +1125,11 @@ class AzurePlacementZone(BasePlacementZone):
         """
         """
         return self._azure_region
         return self._azure_region
 
 
+    @property
+    def label(self):
+        raise NotImplementedError("Azure Placement Zones do not support "
+                                  "labels")
+
     @property
     @property
     def region_name(self):
     def region_name(self):
         """
         """
@@ -1099,17 +1157,18 @@ class AzureSubnet(BaseSubnet):
         return self._subnet.id
         return self._subnet.id
 
 
     @property
     @property
-    def resource_id(self):
-        return self._subnet.id
+    def name(self):
+        net_name = self.network_id.split('/')[-1]
+        sn_name = self._subnet.name
+        return '{0}/{1}'.format(net_name, sn_name)
 
 
     @property
     @property
-    def name(self):
-        """
-        Get the subnet name.
+    def label(self):
+        raise NotImplementedError("Azure Subnets do not support labels")
 
 
-        .. note:: the subnet must have a (case sensitive) tag ``Name``
-        """
-        return self._subnet.name
+    @property
+    def resource_id(self):
+        return self._subnet.id
 
 
     @property
     @property
     def zone(self):
     def zone(self):
@@ -1203,27 +1262,34 @@ class AzureInstance(BaseInstance):
         """
         """
         return self._vm.id
         return self._vm.id
 
 
+    @property
+    def name(self):
+        """
+        Get the instance name.
+        """
+        return self._vm.name
+
     @property
     @property
     def resource_id(self):
     def resource_id(self):
         return self._vm.id
         return self._vm.id
 
 
     @property
     @property
-    def name(self):
+    def label(self):
         """
         """
-        Get the instance name.
+        Get the instance label.
 
 
-        .. note:: an instance must have a (case sensitive) tag ``Name``
+        .. note:: an instance must have a (case sensitive) tag ``Label``
         """
         """
-        return self._vm.tags.get('Name', self._vm.name)
+        return self._vm.tags.get('Label', None)
 
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
     # 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_name(value)
-        self._vm.tags.update(Name=value)
+        self._vm.tags.update(Label=value)
         self._provider.azure_client. \
         self._provider.azure_client. \
             update_vm_tags(self.id, self._vm)
             update_vm_tags(self.id, self._vm)
 
 
@@ -1370,10 +1436,11 @@ class AzureInstance(BaseInstance):
             'source_virtual_machine': {
             'source_virtual_machine': {
                 'id': self.resource_id
                 'id': self.resource_id
             },
             },
-            'tags': {'Name': name}
+            'tags': {'Label': name}
         }
         }
 
 
-        image = self._provider.azure_client.create_image(name, create_params)
+        image = self._provider.azure_client.create_image(name,
+                                                         create_params)
         return AzureMachineImage(self._provider, image)
         return AzureMachineImage(self._provider, image)
 
 
     def _deprovision(self, private_key_path):
     def _deprovision(self, private_key_path):
@@ -1525,6 +1592,10 @@ class AzureVMType(BaseVMType):
     def name(self):
     def name(self):
         return self._vm_type.name
         return self._vm_type.name
 
 
+    @property
+    def label(self):
+        raise NotImplementedError("Azure VMTypes do not support labels")
+
     @property
     @property
     def family(self):
     def family(self):
         """
         """
@@ -1579,6 +1650,10 @@ class AzureKeyPair(BaseKeyPair):
     def name(self):
     def name(self):
         return self._key_pair.Name
         return self._key_pair.Name
 
 
+    @property
+    def label(self):
+        raise NotImplementedError("Azure Key Pairs do not support labels")
+
     def delete(self):
     def delete(self):
         self._provider.azure_client.delete_public_key(self._key_pair)
         self._provider.azure_client.delete_public_key(self._key_pair)
 
 
@@ -1592,6 +1667,10 @@ class AzureRouter(BaseRouter):
 
 
     @property
     @property
     def id(self):
     def id(self):
+        return self._route_table.id
+
+    @property
+    def name(self):
         return self._route_table.name
         return self._route_table.name
 
 
     @property
     @property
@@ -1599,22 +1678,22 @@ class AzureRouter(BaseRouter):
         return self._route_table.id
         return self._route_table.id
 
 
     @property
     @property
-    def name(self):
+    def label(self):
         """
         """
-        Get the router name.
+        Get the router label.
 
 
-        .. note:: the router must have a (case sensitive) tag ``Name``
+        .. note:: the router must have a (case sensitive) tag ``Label``
         """
         """
-        return self._route_table.tags.get('Name', self._route_table.name)
+        return self._route_table.tags.get('Label', None)
 
 
-    @name.setter
+    @label.setter
     # pylint:disable=arguments-differ
     # pylint:disable=arguments-differ
-    def name(self, value):
+    def label(self, value):
         """
         """
-        Set the router name.
+        Set the router label.
         """
         """
         self.assert_valid_resource_name(value)
         self.assert_valid_resource_name(value)
-        self._route_table.tags.update(Name=value)
+        self._route_table.tags.update(Label=value)
         self._provider.azure_client. \
         self._provider.azure_client. \
             update_route_table_tags(self._route_table.name,
             update_route_table_tags(self._route_table.name,
                                     self._route_table)
                                     self._route_table)
@@ -1669,25 +1748,15 @@ class AzureInternetGateway(BaseInternetGateway):
 
 
     @property
     @property
     def id(self):
     def id(self):
-        return self._name
+        return None
 
 
     @property
     @property
     def name(self):
     def name(self):
-        """
-        Get the gateway name.
-
-        .. note:: the gateway must have a (case sensitive) tag ``Name``
-        """
-        return self._name
+        return None
 
 
-    @name.setter
-    # pylint:disable=arguments-differ
-    def name(self, value):
-        """
-        Set the router name.
-        """
-        self.assert_valid_resource_name(value)
-        self._name = value
+    @property
+    def label(self):
+        None
 
 
     def refresh(self):
     def refresh(self):
         pass
         pass

+ 177 - 119
cloudbridge/cloud/providers/azure/services.py

@@ -67,15 +67,24 @@ class AzureVMFirewallService(BaseVMFirewallService):
                for fw in self.provider.azure_client.list_vm_firewall()]
                for fw in self.provider.azure_client.list_vm_firewall()]
         return ClientPagedResultList(self.provider, fws, limit, marker)
         return ClientPagedResultList(self.provider, fws, limit, marker)
 
 
-    def create(self, name, description, network_id=None):
-        AzureVMFirewall.assert_valid_resource_name(name)
-        parameters = {"location": self.provider.region_name,
-                      'tags': {'Name': name}}
+    def create(self, label=None, description=None, network_id=None):
+        parameters = {"location": self.provider.region_name}
+        if label:
+            AzureVMFirewall.assert_valid_resource_name(label)
+            parameters.update({'tags': {'Label': label}})
+        else:
+            label = "cb-fw"
 
 
         if description:
         if description:
-            parameters['tags'].update(Description=description)
+            tags = parameters.get('tags')
+            if tags:
+                tags.update(Description=description)
+            else:
+                parameters.update({'tags': {'Description': description}})
 
 
-        fw = self.provider.azure_client.create_vm_firewall(name, parameters)
+        name = "{0}-{1}".format(label, uuid.uuid4().hex[:6])
+        fw = self.provider.azure_client.create_vm_firewall(name,
+                                                           parameters)
 
 
         # Add default rules to negate azure default rules.
         # Add default rules to negate azure default rules.
         # See: https://github.com/CloudVE/cloudbridge/issues/106
         # See: https://github.com/CloudVE/cloudbridge/issues/106
@@ -86,7 +95,7 @@ class AzureVMFirewallService(BaseVMFirewallService):
             # only 0-4096 are allowed for custom rules
             # only 0-4096 are allowed for custom rules
             rule.priority = rule.priority - 61440
             rule.priority = rule.priority - 61440
             rule.access = "Deny"
             rule.access = "Deny"
-            self._provider.azure_client.create_vm_firewall_rule(
+            self.provider.azure_client.create_vm_firewall_rule(
                 fw.id, rule_name, rule)
                 fw.id, rule_name, rule)
 
 
         # Add a new custom rule allowing all outbound traffic to the internet
         # Add a new custom rule allowing all outbound traffic to the internet
@@ -98,7 +107,7 @@ class AzureVMFirewallService(BaseVMFirewallService):
                       "destination_address_prefix": "Internet",
                       "destination_address_prefix": "Internet",
                       "access": "Allow",
                       "access": "Allow",
                       "direction": "Outbound"}
                       "direction": "Outbound"}
-        result = self._provider.azure_client.create_vm_firewall_rule(
+        result = self.provider.azure_client.create_vm_firewall_rule(
             fw.id, "cb-default-internet-outbound", parameters)
             fw.id, "cb-default-internet-outbound", parameters)
         fw.security_rules.append(result)
         fw.security_rules.append(result)
 
 
@@ -106,18 +115,18 @@ class AzureVMFirewallService(BaseVMFirewallService):
         return cb_fw
         return cb_fw
 
 
     def find(self, **kwargs):
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        obj_list = self
+        filters = ['label', 'name']
+        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
 
 
         # All kwargs should have been popped at this time.
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs,
+                                                           ", ".join(filters)))
 
 
-        filters = {'Name': name}
-        fws = [AzureVMFirewall(self.provider, vm_firewall)
-               for vm_firewall in azure_helpers.filter_by_tag(
-               self.provider.azure_client.list_vm_firewall(), filters)]
-        return ClientPagedResultList(self.provider, fws)
+        return ClientPagedResultList(self.provider,
+                                     matches if matches else [])
 
 
     def delete(self, group_id):
     def delete(self, group_id):
         self.provider.azure_client.delete_vm_firewall(group_id)
         self.provider.azure_client.delete_vm_firewall(group_id)
@@ -153,16 +162,18 @@ class AzureKeyPairService(BaseKeyPairService):
                                      data=results)
                                      data=results)
 
 
     def find(self, **kwargs):
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        obj_list = self
+        filters = ['name']
+        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
 
 
         # All kwargs should have been popped at this time.
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs,
+                                                           ", ".join(filters)))
 
 
-        key_pair = self.get(name)
         return ClientPagedResultList(self.provider,
         return ClientPagedResultList(self.provider,
-                                     [key_pair] if key_pair else [])
+                                     matches if matches else [])
 
 
     def create(self, name, public_key_material=None):
     def create(self, name, public_key_material=None):
         AzureKeyPair.assert_valid_resource_name(name)
         AzureKeyPair.assert_valid_resource_name(name)
@@ -207,17 +218,18 @@ class AzureBucketService(BaseBucketService):
             return None
             return None
 
 
     def find(self, **kwargs):
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        obj_list = self
+        filters = ['name']
+        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
 
 
         # All kwargs should have been popped at this time.
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs,
+                                                           ", ".join(filters)))
 
 
-        buckets = [AzureBucket(self.provider, bucket)
-                   for bucket in
-                   self.provider.azure_client.list_containers(prefix=name)]
-        return ClientPagedResultList(self.provider, buckets)
+        return ClientPagedResultList(self.provider,
+                                     matches if matches else [])
 
 
     def list(self, limit=None, marker=None):
     def list(self, limit=None, marker=None):
         """
         """
@@ -276,18 +288,18 @@ class AzureVolumeService(BaseVolumeService):
             return None
             return None
 
 
     def find(self, **kwargs):
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        obj_list = self
+        filters = ['name', 'label']
+        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
 
 
         # All kwargs should have been popped at this time.
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs,
+                                                           ", ".join(filters)))
 
 
-        filters = {'Name': name}
-        cb_vols = [AzureVolume(self.provider, volume)
-                   for volume in azure_helpers.filter_by_tag(
-                   self.provider.azure_client.list_disks(), filters)]
-        return ClientPagedResultList(self.provider, cb_vols)
+        return ClientPagedResultList(self.provider,
+                                     matches if matches else [])
 
 
     def list(self, limit=None, marker=None):
     def list(self, limit=None, marker=None):
         """
         """
@@ -298,18 +310,29 @@ class AzureVolumeService(BaseVolumeService):
         return ClientPagedResultList(self.provider, cb_vols,
         return ClientPagedResultList(self.provider, cb_vols,
                                      limit=limit, marker=marker)
                                      limit=limit, marker=marker)
 
 
-    def create(self, name, size, zone=None, snapshot=None, description=None):
+    def create(self, size, label=None, description=None,
+               zone=None, snapshot=None):
         """
         """
         Creates a new volume.
         Creates a new volume.
         """
         """
-        AzureVolume.assert_valid_resource_name(name)
+        AzureVolume.assert_valid_resource_name(label)
+        if label:
+            AzureVolume.assert_valid_resource_name(label)
+            tags = {'Label': label}
+        else:
+            label = "cb-vol"
+
+        disk_name = "{0}-{1}".format(label, uuid.uuid4().hex[:6])
+
         zone_id = zone.id if isinstance(zone, PlacementZone) else zone
         zone_id = zone.id if isinstance(zone, PlacementZone) else zone
         snapshot = (self.provider.storage.snapshots.get(snapshot)
         snapshot = (self.provider.storage.snapshots.get(snapshot)
                     if snapshot and isinstance(snapshot, str) else snapshot)
                     if snapshot and isinstance(snapshot, str) else snapshot)
-        disk_name = "{0}-{1}".format(name, uuid.uuid4().hex[:6])
-        tags = {'Name': name}
+
         if description:
         if description:
+            if not tags:
+                tags = {}
             tags.update(Description=description)
             tags.update(Description=description)
+
         if snapshot:
         if snapshot:
             params = {
             params = {
                 'location':
                 'location':
@@ -317,10 +340,12 @@ class AzureVolumeService(BaseVolumeService):
                 'creation_data': {
                 'creation_data': {
                     'create_option': DiskCreateOption.copy,
                     'create_option': DiskCreateOption.copy,
                     'source_uri': snapshot.resource_id
                     'source_uri': snapshot.resource_id
-                },
-                'tags': tags
+                }
             }
             }
 
 
+            if tags:
+                params.update(tags=tags)
+
             disk = self.provider.azure_client.create_snapshot_disk(disk_name,
             disk = self.provider.azure_client.create_snapshot_disk(disk_name,
                                                                    params)
                                                                    params)
 
 
@@ -331,8 +356,11 @@ class AzureVolumeService(BaseVolumeService):
                 'disk_size_gb': size,
                 'disk_size_gb': size,
                 'creation_data': {
                 'creation_data': {
                     'create_option': DiskCreateOption.empty
                     'create_option': DiskCreateOption.empty
-                },
-                'tags': tags}
+                }
+            }
+
+            if tags:
+                params.update(tags=tags)
 
 
             disk = self.provider.azure_client.create_empty_disk(disk_name,
             disk = self.provider.azure_client.create_empty_disk(disk_name,
                                                                 params)
                                                                 params)
@@ -360,18 +388,18 @@ class AzureSnapshotService(BaseSnapshotService):
             return None
             return None
 
 
     def find(self, **kwargs):
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        obj_list = self
+        filters = ['name', 'label']
+        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
 
 
         # All kwargs should have been popped at this time.
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs,
+                                                           ", ".join(filters)))
 
 
-        filters = {'Name': name}
-        cb_snapshots = [AzureSnapshot(self.provider, snapshot)
-                        for snapshot in azure_helpers.filter_by_tag(
-                        self.provider.azure_client.list_snapshots(), filters)]
-        return ClientPagedResultList(self.provider, cb_snapshots)
+        return ClientPagedResultList(self.provider,
+                                     matches if matches else [])
 
 
     def list(self, limit=None, marker=None):
     def list(self, limit=None, marker=None):
         """
         """
@@ -382,18 +410,24 @@ class AzureSnapshotService(BaseSnapshotService):
                  self.provider.azure_client.list_snapshots()]
                  self.provider.azure_client.list_snapshots()]
         return ClientPagedResultList(self.provider, snaps, limit, marker)
         return ClientPagedResultList(self.provider, snaps, limit, marker)
 
 
-    def create(self, name, volume, description=None):
+    def create(self, volume, label=None, description=None):
         """
         """
         Creates a new snapshot of a given volume.
         Creates a new snapshot of a given volume.
         """
         """
-        AzureSnapshot.assert_valid_resource_name(name)
         volume = (self.provider.storage.volumes.get(volume)
         volume = (self.provider.storage.volumes.get(volume)
                   if isinstance(volume, str) else volume)
                   if isinstance(volume, str) else volume)
 
 
-        tags = {'Name': name}
-        snapshot_name = "{0}-{1}".format(name, uuid.uuid4().hex[:6])
+        if label:
+            AzureSnapshot.assert_valid_resource_name(label)
+            tags = {'Label': label}
+        else:
+            label = "cb-snap"
+
+        snapshot_name = "{0}-{1}".format(label, uuid.uuid4().hex[:6])
 
 
         if description:
         if description:
+            if not tags:
+                tags = {}
             tags.update(Description=description)
             tags.update(Description=description)
 
 
         params = {
         params = {
@@ -402,10 +436,12 @@ class AzureSnapshotService(BaseSnapshotService):
                 'create_option': DiskCreateOption.copy,
                 'create_option': DiskCreateOption.copy,
                 'source_uri': volume.resource_id
                 'source_uri': volume.resource_id
             },
             },
-            'disk_size_gb': volume.size,
-            'tags': tags
+            'disk_size_gb': volume.size
         }
         }
 
 
+        if tags:
+            params.update(tags=tags)
+
         azure_snap = self.provider.azure_client.create_snapshot(snapshot_name,
         azure_snap = self.provider.azure_client.create_snapshot(snapshot_name,
                                                                 params)
                                                                 params)
         return AzureSnapshot(self.provider, azure_snap)
         return AzureSnapshot(self.provider, azure_snap)
@@ -440,12 +476,16 @@ class AzureInstanceService(BaseInstanceService):
     def __init__(self, provider):
     def __init__(self, provider):
         super(AzureInstanceService, self).__init__(provider)
         super(AzureInstanceService, self).__init__(provider)
 
 
-    def create(self, name, image, vm_type, subnet=None, zone=None,
+    def create(self, image, vm_type, label=None, subnet=None, zone=None,
                key_pair=None, vm_firewalls=None, user_data=None,
                key_pair=None, vm_firewalls=None, user_data=None,
                launch_config=None, **kwargs):
                launch_config=None, **kwargs):
 
 
-        instance_name = name.replace("_", "-") if name \
-            else "{0} - {1}".format("cb", uuid.uuid4())
+        if label:
+            prefix = label
+        else:
+            prefix = "cb-ins"
+
+        instance_name = "{0}-{1}".format(prefix, uuid.uuid4().hex[:6])
 
 
         AzureInstance.assert_valid_resource_name(instance_name)
         AzureInstance.assert_valid_resource_name(instance_name)
 
 
@@ -478,7 +518,7 @@ class AzureInstanceService(BaseInstanceService):
                                                        instance_name, zone_id)
                                                        instance_name, zone_id)
 
 
         nic_params = {
         nic_params = {
-            'location': self._provider.region_name,
+            'location': self.provider.region_name,
             'ip_configurations': [{
             'ip_configurations': [{
                 'name': instance_name + '_ip_config',
                 'name': instance_name + '_ip_config',
                 'private_ip_allocation_method': 'Dynamic',
                 'private_ip_allocation_method': 'Dynamic',
@@ -520,7 +560,7 @@ class AzureInstanceService(BaseInstanceService):
             temp_key_pair = key_pair
             temp_key_pair = key_pair
 
 
         params = {
         params = {
-            'location': zone_id or self._provider.region_name,
+            'location': zone_id or self.provider.region_name,
             'os_profile': {
             'os_profile': {
                 'admin_username': self.provider.vm_default_user_name,
                 'admin_username': self.provider.vm_default_user_name,
                 'computer_name': instance_name,
                 'computer_name': instance_name,
@@ -545,9 +585,12 @@ class AzureInstanceService(BaseInstanceService):
                 }]
                 }]
             },
             },
             'storage_profile': storage_profile,
             'storage_profile': storage_profile,
-            'tags': {'Name': name}
+            'tags': {}
         }
         }
 
 
+        if label:
+            params['tags'].update(Label=label)
+
         for disk_def in storage_profile.get('data_disks', []):
         for disk_def in storage_profile.get('data_disks', []):
             params['tags'] = dict(disk_def.get('tags', {}), **params['tags'])
             params['tags'] = dict(disk_def.get('tags', {}), **params['tags'])
 
 
@@ -575,7 +618,7 @@ class AzureInstanceService(BaseInstanceService):
                 temp_key_pair.delete()
                 temp_key_pair.delete()
         return AzureInstance(self.provider, vm)
         return AzureInstance(self.provider, vm)
 
 
-    def _resolve_launch_options(self, name, subnet=None, zone_id=None,
+    def _resolve_launch_options(self, inst_name, subnet=None, zone_id=None,
                                 vm_firewalls=None):
                                 vm_firewalls=None):
         if subnet:
         if subnet:
             # subnet's zone takes precedence
             # subnet's zone takes precedence
@@ -595,7 +638,8 @@ class AzureInstanceService(BaseInstanceService):
 
 
             if len(vm_firewalls) > 1:
             if len(vm_firewalls) > 1:
                 new_fw = self.provider.security.vm_firewalls.\
                 new_fw = self.provider.security.vm_firewalls.\
-                    create('{0}-fw'.format(name), 'Merge vm firewall {0}'.
+                    create(label='{0}-fw'.format(inst_name),
+                           description='Merge vm firewall {0}'.
                            format(','.join(vm_firewalls_ids)))
                            format(','.join(vm_firewalls_ids)))
 
 
                 for fw in vm_firewalls:
                 for fw in vm_firewalls:
@@ -735,18 +779,18 @@ class AzureInstanceService(BaseInstanceService):
             return None
             return None
 
 
     def find(self, **kwargs):
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        obj_list = self
+        filters = ['name', 'label']
+        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
 
 
         # All kwargs should have been popped at this time.
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs,
+                                                           ", ".join(filters)))
 
 
-        filtr = {'Name': name}
-        instances = [AzureInstance(self.provider, inst)
-                     for inst in azure_helpers.filter_by_tag(
-                     self.provider.azure_client.list_vm(), filtr)]
-        return ClientPagedResultList(self.provider, instances)
+        return ClientPagedResultList(self.provider,
+                                     matches if matches else [])
 
 
 
 
 class AzureImageService(BaseImageService):
 class AzureImageService(BaseImageService):
@@ -766,23 +810,18 @@ class AzureImageService(BaseImageService):
             return None
             return None
 
 
     def find(self, **kwargs):
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        obj_list = self
+        filters = ['name', 'label']
+        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
 
 
         # All kwargs should have been popped at this time.
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
-
-        filters = {'Name': name}
-        cb_images = [AzureMachineImage(self.provider, image)
-                     for image in azure_helpers.filter_by_tag(
-                     self.provider.azure_client.list_images(), filters)]
-        # All gallery image properties (id, resource_id, name) are the URN
-        # Improvement: wrap the filters by publisher, offer, etc...
-        cb_images.extend([AzureMachineImage(self.provider, image) for image
-                          in self.provider.azure_client.list_gallery_refs()
-                          if azure_helpers.generate_urn(image) == name])
-        return ClientPagedResultList(self.provider, cb_images)
+                            " Supported attributes: %s" % (kwargs,
+                                                           ", ".join(filters)))
+
+        return ClientPagedResultList(self.provider,
+                                     matches if matches else [])
 
 
     def list(self, limit=None, marker=None):
     def list(self, limit=None, marker=None):
         """
         """
@@ -859,26 +898,28 @@ class AzureNetworkService(BaseNetworkService):
                                      limit=limit, marker=marker)
                                      limit=limit, marker=marker)
 
 
     def find(self, **kwargs):
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        obj_list = self
+        filters = ['name', 'label']
+        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
 
 
         # All kwargs should have been popped at this time.
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
+                            " Supported attributes: %s" % (kwargs,
+                                                           ", ".join(filters)))
 
 
-        filters = {'Name': name}
-        networks = [
-            AzureNetwork(self.provider, network) for network
-            in azure_helpers.filter_by_tag(
-                self.provider.azure_client.list_networks(), filters)]
-        return ClientPagedResultList(self.provider, networks)
+        return ClientPagedResultList(self.provider,
+                                     matches if matches else [])
 
 
-    def create(self, name, cidr_block):
+    def create(self, cidr_block, label=None):
         # Azure requires CIDR block to be specified when creating a network
         # Azure requires CIDR block to be specified when creating a network
         # so set a default one and use the largest allowed netmask.
         # so set a default one and use the largest allowed netmask.
-        network_name = AzureNetwork.CB_DEFAULT_NETWORK_NAME
-        if name:
-            network_name = "{0}-{1}".format(name, uuid.uuid4().hex[:6])
+        if label:
+            tags = {'Label': label }
+        else:
+            label = 'cb-net'
+
+        network_name = "{0}-{1}".format(label, uuid.uuid4().hex[:6])
 
 
         AzureNetwork.assert_valid_resource_name(network_name)
         AzureNetwork.assert_valid_resource_name(network_name)
 
 
@@ -886,9 +927,12 @@ class AzureNetworkService(BaseNetworkService):
             'location': self.provider.azure_client.region_name,
             'location': self.provider.azure_client.region_name,
             'address_space': {
             'address_space': {
                 'address_prefixes': [cidr_block]
                 'address_prefixes': [cidr_block]
-            },
-            'tags': {'Name': name or AzureNetwork.CB_DEFAULT_NETWORK_NAME}
+            }
         }
         }
+
+        if tags:
+            params.update(tags=tags)
+
         az_network = self.provider.azure_client.create_network(network_name,
         az_network = self.provider.azure_client.create_network(network_name,
                                                                params)
                                                                params)
         cb_network = AzureNetwork(self.provider, az_network)
         cb_network = AzureNetwork(self.provider, az_network)
@@ -982,20 +1026,30 @@ class AzureSubnetService(BaseSubnetService):
         obj_list = self._list_subnets(network)
         obj_list = self._list_subnets(network)
         filters = ['name']
         filters = ['name']
         matches = cb_helpers.generic_find(filters, kwargs, obj_list)
         matches = cb_helpers.generic_find(filters, kwargs, obj_list)
-        return ClientPagedResultList(self._provider, list(matches))
 
 
-    def create(self, network, cidr_block, name=None, **kwargs):
+        # All kwargs should have been popped at this time.
+        if len(kwargs) > 0:
+            raise TypeError("Unrecognised parameters for search: %s."
+                            " Supported attributes: %s" % (kwargs,
+                                                           ", ".join(filters)))
+
+        return ClientPagedResultList(self.provider,
+                                     matches if matches else [])
+
+    def create(self, network, cidr_block, prefix=None, **kwargs):
         """
         """
         Create subnet
         Create subnet
         """
         """
-        AzureSubnet.assert_valid_resource_name(name)
         network_id = network.id \
         network_id = network.id \
             if isinstance(network, Network) else network
             if isinstance(network, Network) else network
 
 
-        if not name:
-            subnet_name = AzureSubnet.CB_DEFAULT_SUBNET_NAME
+        if prefix:
+            AzureSubnet.assert_valid_resource_name(prefix)
+
         else:
         else:
-            subnet_name = name
+            prefix = "cb-sn"
+
+        subnet_name = "{0}-{1}".format(prefix, uuid.uuid4().hex[:6])
 
 
         subnet_info = self.provider.azure_client\
         subnet_info = self.provider.azure_client\
             .create_subnet(
             .create_subnet(
@@ -1012,22 +1066,22 @@ class AzureSubnetService(BaseSubnetService):
         default_cidr = '10.0.1.0/24'
         default_cidr = '10.0.1.0/24'
 
 
         # No provider-default Subnet exists, look for a library-default one
         # No provider-default Subnet exists, look for a library-default one
-        matches = self.find(name=AzureSubnet.CB_DEFAULT_SUBNET_NAME)
+        matches = self.find(label=AzureSubnet.CB_DEFAULT_SUBNET_NAME)
         if matches:
         if matches:
             return matches[0]
             return matches[0]
 
 
         # No provider-default Subnet exists, try to create it (net + subnets)
         # No provider-default Subnet exists, try to create it (net + subnets)
         networks = self.provider.networking.networks.find(
         networks = self.provider.networking.networks.find(
-            name=AzureNetwork.CB_DEFAULT_NETWORK_NAME)
+            label=AzureNetwork.CB_DEFAULT_NETWORK_NAME)
 
 
         if networks:
         if networks:
             network = networks[0]
             network = networks[0]
         else:
         else:
-            network = self.provider.networking.networks.create(
-                AzureNetwork.CB_DEFAULT_NETWORK_NAME, '10.0.0.0/16')
+            network = self.provider.networking.networks.create('10.0.0.0/16',
+                label=AzureNetwork.CB_DEFAULT_NETWORK_NAME)
 
 
         subnet = self.create(network, default_cidr,
         subnet = self.create(network, default_cidr,
-                             name=AzureSubnet.CB_DEFAULT_SUBNET_NAME)
+                             prefix=AzureSubnet.CB_DEFAULT_SUBNET_NAME)
         return subnet
         return subnet
 
 
     def delete(self, subnet):
     def delete(self, subnet):
@@ -1049,19 +1103,18 @@ class AzureRouterService(BaseRouterService):
             return None
             return None
 
 
     def find(self, **kwargs):
     def find(self, **kwargs):
-        name = kwargs.pop('name', None)
+        obj_list = self
+        filters = ['name', 'label']
+        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
 
 
         # All kwargs should have been popped at this time.
         # All kwargs should have been popped at this time.
         if len(kwargs) > 0:
         if len(kwargs) > 0:
             raise TypeError("Unrecognised parameters for search: %s."
             raise TypeError("Unrecognised parameters for search: %s."
-                            " Supported attributes: %s" % (kwargs, 'name'))
-
-        filters = {'Name': name}
-        routes = [AzureRouter(self.provider, route)
-                  for route in azure_helpers.filter_by_tag(
-                  self.provider.azure_client.list_route_tables(), filters)]
+                            " Supported attributes: %s" % (kwargs,
+                                                           ", ".join(filters)))
 
 
-        return ClientPagedResultList(self.provider, routes)
+        return ClientPagedResultList(self.provider,
+                                     matches if matches else [])
 
 
     def list(self, limit=None, marker=None):
     def list(self, limit=None, marker=None):
         routes = [AzureRouter(self.provider, route)
         routes = [AzureRouter(self.provider, route)
@@ -1071,10 +1124,15 @@ class AzureRouterService(BaseRouterService):
                                      routes,
                                      routes,
                                      limit=limit, marker=marker)
                                      limit=limit, marker=marker)
 
 
-    def create(self, name, network):
-        AzureRouter.assert_valid_resource_name(name)
-        parameters = {"location": self.provider.region_name,
-                      'tags': {'Name': name}}
+    def create(self, network, label=None):
+        AzureRouter.assert_valid_resource_name(label)
+        parameters = {"location": self.provider.region_name}
+        if label:
+            parameters.update(tags={'Label': label})
+        else:
+            label = 'cb-router'
+
+        router_name = "{0}-{1}".format(label, uuid.uuid4().hex[:6])
         route = self.provider.azure_client. \
         route = self.provider.azure_client. \
-            create_route_table(name, parameters)
+            create_route_table(router_name, parameters)
         return AzureRouter(self.provider, route)
         return AzureRouter(self.provider, route)

+ 22 - 12
docs/getting_started.rst

@@ -86,7 +86,7 @@ Once you have a reference to a provider, explore the cloud platform:
 
 
 .. code-block:: python
 .. code-block:: python
 
 
-    provider.security.security_groups.list()
+    provider.security.firewalls.list()
     provider.compute.vm_types.list()
     provider.compute.vm_types.list()
     provider.storage.snapshots.list()
     provider.storage.snapshots.list()
     provider.storage.buckets.list()
     provider.storage.buckets.list()
@@ -116,12 +116,12 @@ attaching an internet gateway to the subnet via a router.
 
 
 .. code-block:: python
 .. code-block:: python
 
 
-    net = provider.networking.networks.create(
-        name='my-network', cidr_block='10.0.0.0/16')
-    sn = net.create_subnet(name='my-subnet', cidr_block='10.0.0.0/28')
-    router = provider.networking.routers.create(network=net, name='my-router')
+    net = provider.networking.networks.create(cidr_block='10.0.0.0/16',
+                                              label='my-network')
+    sn = net.create_subnet(cidr_block='10.0.0.0/28', label='my-subnet')
+    router = provider.networking.routers.create(network=net, label='my-router')
     router.attach_subnet(sn)
     router.attach_subnet(sn)
-    gateway = net.gateways.get_or_create_inet_gateway(name='my-gateway')
+    gateway = net.gateways.get_or_create_inet_gateway(label='my-gateway')
     router.attach_gateway(gateway)
     router.attach_gateway(gateway)
 
 
 
 
@@ -135,7 +135,8 @@ a private network.
 
 
     from cloudbridge.cloud.interfaces.resources import TrafficDirection
     from cloudbridge.cloud.interfaces.resources import TrafficDirection
     fw = provider.security.vm_firewalls.create(
     fw = provider.security.vm_firewalls.create(
-        'cloudbridge-intro', 'A VM firewall used by CloudBridge', net.id)
+        label='cloudbridge-intro', description='A VM firewall used by
+        CloudBridge', network=net.id)
     fw.rules.create(TrafficDirection.INBOUND, 'tcp', 22, 22, '0.0.0.0/0')
     fw.rules.create(TrafficDirection.INBOUND, 'tcp', 22, 22, '0.0.0.0/0')
 
 
 Launch an instance
 Launch an instance
@@ -151,7 +152,7 @@ also add the network interface as a launch argument.
                       if t.vcpus >= 2 and t.ram >= 4],
                       if t.vcpus >= 2 and t.ram >= 4],
                       key=lambda x: x.vcpus*x.ram)[0]
                       key=lambda x: x.vcpus*x.ram)[0]
     inst = provider.compute.instances.create(
     inst = provider.compute.instances.create(
-        name='cloudbridge-intro', image=img, vm_type=vm_type,
+        image=img, vm_type=vm_type, label='cloudbridge-intro',
         subnet=sn, key_pair=kp, vm_firewalls=[fw])
         subnet=sn, key_pair=kp, vm_firewalls=[fw])
     # Wait until ready
     # Wait until ready
     inst.wait_till_ready()  # This is a blocking call
     inst.wait_till_ready()  # This is a blocking call
@@ -190,10 +191,10 @@ From the command prompt, you can now ssh into the instance
 Get a resource
 Get a resource
 --------------
 --------------
 When a resource already exists, a reference to it can be retrieved using either
 When a resource already exists, a reference to it can be retrieved using either
-its ID or name. It is important to note that while IDs are unique, multiple
-resources of the same type could use the same name on some providers, thus the
+its ID, name, or label. It is important to note that while IDs and names are
+unique, multiple resources of the same type could use the same label, thus the
 `find` method always returns a list, while the `get` method returns a single
 `find` method always returns a list, while the `get` method returns a single
-object. While the methods are similar across resources, they are explicitely
+object. While the methods are similar across resources, they are explicitly
 listed in order to help map each resource with the service that handles it.
 listed in order to help map each resource with the service that handles it.
 
 
 .. code-block:: python
 .. code-block:: python
@@ -201,24 +202,30 @@ listed in order to help map each resource with the service that handles it.
     # Key Pair
     # Key Pair
     kp = provider.security.key_pairs.get('keypair ID')
     kp = provider.security.key_pairs.get('keypair ID')
     kp_list = provider.security.key_pairs.find(name='cloudbridge_intro')
     kp_list = provider.security.key_pairs.find(name='cloudbridge_intro')
+    kp_list = provider.security.key_pairs.find(label='cloudbridge_intro')
     kp = kp_list[0]
     kp = kp_list[0]
 
 
     # Network
     # Network
     net = provider.networking.networks.get('network ID')
     net = provider.networking.networks.get('network ID')
     net_list = provider.networking.networks.find(name='my-network')
     net_list = provider.networking.networks.find(name='my-network')
+    net_list = provider.networking.networks.find(label='my-network')
     net = net_list[0]
     net = net_list[0]
 
 
     # Subnet
     # Subnet
     sn = provider.networking.subnets.get('subnet ID')
     sn = provider.networking.subnets.get('subnet ID')
     # Unknown network
     # Unknown network
     sn_list = provider.networking.subnets.find(name='my-subnet')
     sn_list = provider.networking.subnets.find(name='my-subnet')
+    sn_list = provider.networking.subnets.find(label='my-subnet')
     # Known network
     # Known network
     sn_list = provider.networking.subnets.find(network=net.id, name='my-subnet')
     sn_list = provider.networking.subnets.find(network=net.id, name='my-subnet')
+    sn_list = provider.networking.subnets.find(network=net.id,
+                                               label='my-subnet')
     sn = sn_list(0)
     sn = sn_list(0)
 
 
     # Router
     # Router
     router = provider.networking.routers.get('router ID')
     router = provider.networking.routers.get('router ID')
     router_list = provider.networking.routers.find(name='my-router')
     router_list = provider.networking.routers.find(name='my-router')
+    router_list = provider.networking.routers.find(label='my-router')
     router = router_list[0]
     router = router_list[0]
 
 
     # Gateway
     # Gateway
@@ -228,18 +235,21 @@ listed in order to help map each resource with the service that handles it.
     fip = gateway.floating_ips.get('FloatingIP ID')
     fip = gateway.floating_ips.get('FloatingIP ID')
     # Find using public IP address
     # Find using public IP address
     fip_list = gateway.floating_ips.find(public_ip='IP address')
     fip_list = gateway.floating_ips.find(public_ip='IP address')
-    # Find using tagged name
+    # Find using name or tag
     fip_list = net.gateways.floating_ips.find(name='my-fip')
     fip_list = net.gateways.floating_ips.find(name='my-fip')
+    fip_list = net.gateways.floating_ips.find(label='my-fip')
     fip = fip_list[0]
     fip = fip_list[0]
 
 
     # Firewall
     # Firewall
     fw = provider.security.vm_firewalls.get('firewall ID')
     fw = provider.security.vm_firewalls.get('firewall ID')
     fw_list = provider.security.vm_firewalls.find(name='cloudbridge-intro')
     fw_list = provider.security.vm_firewalls.find(name='cloudbridge-intro')
+    fw_list = provider.security.vm_firewalls.find(label='cloudbridge-intro')
     fw = fw_list[0]
     fw = fw_list[0]
 
 
     # Instance
     # Instance
     inst = provider.compute.instances.get('instance ID')
     inst = provider.compute.instances.get('instance ID')
     inst_list = provider.compute.instances.list(name='cloudbridge-intro')
     inst_list = provider.compute.instances.list(name='cloudbridge-intro')
+    inst_list = provider.compute.instances.list(label='cloudbridge-intro')
     inst = inst_list[0]
     inst = inst_list[0]