Просмотр исходного кода

Added support for empty labels since label is optional

Nuwan Goonasekera 7 лет назад
Родитель
Сommit
a88523b0e1

+ 34 - 7
cloudbridge/cloud/base/resources.py

@@ -60,27 +60,54 @@ class BaseCloudResource(CloudResource):
     """
     Base implementation of a CloudBridge Resource.
     """
-
-    # Regular expression for valid cloudbridge resource labels and names.
-    CB_LABEL_PATTERN = re.compile(r"^[a-z][-a-z0-9]{1,61}[a-z0-9]$")
+    # Regular expression for valid cloudbridge resource labels.
+    # Can be None or any alphanumeric string that does not start
+    # or end with a dash
+    # based on: https://stackoverflow.com/questions/2525327/regex-for-a-za-z0-9
+    # -with-dashes-allowed-in-between-but-not-at-the-start-or-e
+    CB_LABEL_PATTERN = re.compile(
+        r"(?=[a-z0-9\-]{1,63}$)^[a-z0-9]+(\-[a-z0-9]+)*$")
+    # Same as the above, but must be at least 3 characters in length
+    CB_NAME_PATTERN = re.compile(r"^[a-z][-a-z0-9]{1,61}[a-z0-9]$")
 
     def __init__(self, provider):
         self.__provider = provider
 
+    @staticmethod
+    def is_valid_resource_label(label):
+        if not label:
+            return True
+        else:
+            return (True if BaseCloudResource.CB_LABEL_PATTERN.match(label)
+                    else False)
+
     @staticmethod
     def is_valid_resource_name(name):
-        return (True if BaseCloudResource.CB_LABEL_PATTERN.match(name)
-                else False)
+        if not name:
+            return False
+        else:
+            return (True if BaseCloudResource.CB_NAME_PATTERN.match(name)
+                    else False)
 
     @staticmethod
-    def assert_valid_resource_name(name):
-        if not BaseCloudResource.is_valid_resource_name(name):
+    def assert_valid_resource_label(name):
+        if not BaseCloudResource.is_valid_resource_label(name):
             log.debug("InvalidLabelException raised on %s", name)
             raise InvalidLabelException(
                 u"Invalid label: %s. Label must be at most 63 characters "
                 "long and consist of lowercase letters, numbers, or dashes. "
                 "The label must not start or end with a dash." % name)
 
+    @staticmethod
+    def assert_valid_resource_name(name):
+        if not BaseCloudResource.is_valid_resource_name(name):
+            log.debug("InvalidLabelException raised on %s", name)
+            raise InvalidLabelException(
+                u"Invalid name: %s. Name must be at least 3 characters long"
+                " and at most 63 characters. It must consist of lowercase"
+                " letters, numbers, or dashes. The label must not start or"
+                " end with a dash." % name)
+
     @staticmethod
     def _generate_name_from_label(label, default):
         if not label:

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

@@ -1119,19 +1119,19 @@ class VMFirewallService(PageableObjectMixin, CloudService):
         pass
 
     @abstractmethod
-    def create(self, name, description, network_id):
+    def create(self, network_id, label=None, description=None):
         """
         Create a new VMFirewall.
 
-        :type name: str
-        :param name: The name of the new VM firewall.
+        :type  network_id: ``str``
+        :param network_id: Network ID under which to create the VM firewall.
+
+        :type label: str
+        :param label: The label for the new VM firewall.
 
         :type description: str
         :param description: The description of the new VM firewall.
 
-        :type  network_id: ``str``
-        :param network_id: Network ID under which to create the VM firewall.
-
         :rtype: ``object`` of :class:`.VMFirewall`
         :return:  A VMFirewall instance or ``None`` if one was not created.
         """

+ 29 - 24
cloudbridge/cloud/providers/aws/resources.py

@@ -92,8 +92,9 @@ class AWSMachineImage(BaseMachineImage):
     @label.setter
     # pylint:disable=arguments-differ
     def label(self, value):
-        self.assert_valid_resource_name(value)
-        self._ec2_image.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
+        self.assert_valid_resource_label(value)
+        self._ec2_image.create_tags(Tags=[{'Key': 'Name',
+                                           'Value': value or ""}])
 
     @property
     def description(self):
@@ -250,8 +251,9 @@ class AWSInstance(BaseInstance):
     @label.setter
     # pylint:disable=arguments-differ
     def label(self, value):
-        self.assert_valid_resource_name(value)
-        self._ec2_instance.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
+        self.assert_valid_resource_label(value)
+        self._ec2_instance.create_tags(Tags=[{'Key': 'Name',
+                                              'Value': value or ""}])
 
     @property
     def public_ips(self):
@@ -309,7 +311,7 @@ class AWSInstance(BaseInstance):
         return self._ec2_instance.key_name
 
     def create_image(self, label=None):
-        self.assert_valid_resource_name(label)
+        self.assert_valid_resource_label(label)
         name = self._generate_name_from_label(label, 'cb-img')
 
         image = AWSMachineImage(self._provider,
@@ -420,8 +422,8 @@ class AWSVolume(BaseVolume):
     @label.setter
     # pylint:disable=arguments-differ
     def label(self, value):
-        self.assert_valid_resource_name(value)
-        self._volume.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
+        self.assert_valid_resource_label(value)
+        self._volume.create_tags(Tags=[{'Key': 'Name', 'Value': value or ""}])
 
     @property
     def description(self):
@@ -429,7 +431,8 @@ class AWSVolume(BaseVolume):
 
     @description.setter
     def description(self, value):
-        self._volume.create_tags(Tags=[{'Key': 'Description', 'Value': value}])
+        self._volume.create_tags(Tags=[{'Key': 'Description',
+                                        'Value': value or ""}])
 
     @property
     def size(self):
@@ -478,7 +481,7 @@ class AWSVolume(BaseVolume):
         snap = AWSSnapshot(
             self._provider,
             self._volume.create_snapshot(
-                Description=description))
+                Description=description or ""))
         snap.label = label
         return snap
 
@@ -537,8 +540,9 @@ class AWSSnapshot(BaseSnapshot):
     @label.setter
     # pylint:disable=arguments-differ
     def label(self, value):
-        self.assert_valid_resource_name(value)
-        self._snapshot.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
+        self.assert_valid_resource_label(value)
+        self._snapshot.create_tags(Tags=[{'Key': 'Name',
+                                          'Value': value or ""}])
 
     @property
     def description(self):
@@ -547,7 +551,7 @@ class AWSSnapshot(BaseSnapshot):
     @description.setter
     def description(self, value):
         self._snapshot.create_tags(Tags=[{
-            'Key': 'Description', 'Value': value}])
+            'Key': 'Description', 'Value': value or ""}])
 
     @property
     def size(self):
@@ -621,8 +625,9 @@ class AWSVMFirewall(BaseVMFirewall):
     @label.setter
     # pylint:disable=arguments-differ
     def label(self, value):
-        self.assert_valid_resource_name(value)
-        self._vm_firewall.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
+        self.assert_valid_resource_label(value)
+        self._vm_firewall.create_tags(Tags=[{'Key': 'Name',
+                                             'Value': value or ""}])
 
     @property
     def network_id(self):
@@ -966,8 +971,8 @@ class AWSNetwork(BaseNetwork):
     @label.setter
     # pylint:disable=arguments-differ
     def label(self, value):
-        self.assert_valid_resource_name(value)
-        self._vpc.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
+        self.assert_valid_resource_label(value)
+        self._vpc.create_tags(Tags=[{'Key': 'Name', 'Value': value or ""}])
 
     @property
     def external(self):
@@ -1042,8 +1047,8 @@ class AWSSubnet(BaseSubnet):
     @label.setter
     # pylint:disable=arguments-differ
     def label(self, value):
-        self.assert_valid_resource_name(value)
-        self._subnet.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
+        self.assert_valid_resource_label(value)
+        self._subnet.create_tags(Tags=[{'Key': 'Name', 'Value': value or ""}])
 
     @property
     def cidr_block(self):
@@ -1155,8 +1160,9 @@ class AWSRouter(BaseRouter):
     @label.setter
     # pylint:disable=arguments-differ
     def label(self, value):
-        self.assert_valid_resource_name(value)
-        self._route_table.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
+        self.assert_valid_resource_label(value)
+        self._route_table.create_tags(Tags=[{'Key': 'Name',
+                                             'Value': value or ""}])
 
     def refresh(self):
         try:
@@ -1216,8 +1222,7 @@ class AWSGatewayContainer(BaseGatewayContainer):
     def get_or_create_inet_gateway(self, label=None):
         log.debug("Get or create inet gateway %s on net %s", label,
                   self._network)
-        if label:
-            AWSInternetGateway.assert_valid_resource_name(label)
+        AWSInternetGateway.assert_valid_resource_label(label)
 
         network_id = self._network.id if isinstance(
             self._network, AWSNetwork) else self._network
@@ -1273,8 +1278,8 @@ class AWSInternetGateway(BaseInternetGateway):
     @label.setter
     # pylint:disable=arguments-differ
     def label(self, value):
-        self.assert_valid_resource_name(value)
-        self._gateway.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
+        self.assert_valid_resource_label(value)
+        self._gateway.create_tags(Tags=[{'Key': 'Name', 'Value': value or ""}])
 
     def refresh(self):
         try:

+ 10 - 9
cloudbridge/cloud/providers/aws/services.py

@@ -132,14 +132,15 @@ class AWSVMFirewallService(BaseVMFirewallService):
     def list(self, limit=None, marker=None):
         return self.svc.list(limit=limit, marker=marker)
 
-    def create(self, description, network_id, label=None):
+    def create(self, network_id, label=None, description=None):
         log.debug("Creating Firewall Service with the parameters "
                   "[label: %s id: %s description: %s]", label, network_id,
                   description)
-        AWSVMFirewall.assert_valid_resource_name(label)
+        AWSVMFirewall.assert_valid_resource_label(label)
         name = AWSVMFirewall._generate_name_from_label(label, 'cb-fw')
         obj = self.svc.create('create_security_group', GroupName=name,
-                              Description=description, VpcId=network_id)
+                              Description=description or name,
+                              VpcId=network_id)
         obj.label = label
         return obj
 
@@ -216,7 +217,7 @@ class AWSVolumeService(BaseVolumeService):
                   "[label: %s size: %s zone: %s snapshot: %s "
                   "description: %s]", label, size, zone, snapshot,
                   description)
-        AWSVolume.assert_valid_resource_name(label)
+        AWSVolume.assert_valid_resource_label(label)
 
         zone_id = zone.id if isinstance(zone, PlacementZone) else zone
         snapshot_id = snapshot.id if isinstance(
@@ -267,7 +268,7 @@ class AWSSnapshotService(BaseSnapshotService):
         log.debug("Creating a new AWS snapshot Service with the "
                   "parameters [label: %s volume: %s description: %s]",
                   label, volume, description)
-        AWSSnapshot.assert_valid_resource_name(label)
+        AWSSnapshot.assert_valid_resource_label(label)
 
         volume_id = volume.id if isinstance(volume, AWSVolume) else volume
 
@@ -416,7 +417,7 @@ class AWSInstanceService(BaseInstanceService):
                   "key pair: %s firewalls: %s user data: %s config %s "
                   "others: %s]", label, image, vm_type, subnet, zone,
                   key_pair, vm_firewalls, user_data, launch_config, kwargs)
-        AWSInstance.assert_valid_resource_name(label)
+        AWSInstance.assert_valid_resource_label(label)
 
         image_id = image.id if isinstance(image, MachineImage) else image
         vm_size = vm_type.id if \
@@ -669,7 +670,7 @@ class AWSNetworkService(BaseNetworkService):
     def create(self, cidr_block, label=None):
         log.debug("Creating AWS Network Service with the params "
                   "[label: %s block: %s]", label, cidr_block)
-        AWSNetwork.assert_valid_resource_name(label)
+        AWSNetwork.assert_valid_resource_label(label)
 
         cb_net = self.svc.create('create_vpc', CidrBlock=cidr_block)
         # Wait until ready to tag instance
@@ -715,7 +716,7 @@ class AWSSubnetService(BaseSubnetService):
         log.debug("Creating AWS Subnet Service with the params "
                   "[label: %s network: %s block: %s zone: %s]",
                   label, network, cidr_block, zone)
-        AWSSubnet.assert_valid_resource_name(label)
+        AWSSubnet.assert_valid_resource_label(label)
 
         network_id = network.id if isinstance(network, AWSNetwork) else network
 
@@ -802,7 +803,7 @@ class AWSRouterService(BaseRouterService):
     def create(self, network, label=None):
         log.debug("Creating AWS Router Service with the params "
                   "[label: %s network: %s]", label, network)
-        AWSRouter.assert_valid_resource_name(label)
+        AWSRouter.assert_valid_resource_label(label)
 
         network_id = network.id if isinstance(network, AWSNetwork) else network
 

+ 11 - 11
cloudbridge/cloud/providers/azure/resources.py

@@ -59,7 +59,7 @@ class AzureVMFirewall(BaseVMFirewall):
 
     @label.setter
     def label(self, value):
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         self._vm_firewall.tags.update(Label=value)
         self._provider.azure_client.update_vm_firewall_tags(
             self.id, self._vm_firewall.tags)
@@ -469,7 +469,7 @@ class AzureVolume(BaseVolume):
         """
         Set the volume label.
         """
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         self._volume.tags.update(Label=value)
         self._provider.azure_client. \
             update_disk_tags(self.id,
@@ -627,7 +627,7 @@ class AzureSnapshot(BaseSnapshot):
         """
         Set the snapshot label.
         """
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         self._snapshot.tags.update(Label=value)
         self._provider.azure_client. \
             update_snapshot_tags(self.id,
@@ -751,7 +751,7 @@ class AzureMachineImage(BaseMachineImage):
         Set the image label when it is a private image.
         """
         if not self.is_gallery_image:
-            self.assert_valid_resource_name(value)
+            self.assert_valid_resource_label(value)
             self._image.tags.update(Label=value)
             self._provider.azure_client. \
                 update_image_tags(self.id, self._image.tags)
@@ -897,7 +897,7 @@ class AzureNetwork(BaseNetwork):
         """
         Set the network label.
         """
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         self._network.tags.update(Label=value)
         self._provider.azure_client. \
             update_network_tags(self.id, self._network)
@@ -996,7 +996,7 @@ class AzureFloatingIPContainer(BaseFloatingIPContainer):
         if label:
             public_ip_parameters.update(tags={'Label': label})
 
-        AzureFloatingIP.assert_valid_resource_name(label)
+        AzureFloatingIP.assert_valid_resource_label(label)
         public_ip_name = AzureFloatingIP._generate_name_from_label(
             label, 'cb-fip')
 
@@ -1039,7 +1039,7 @@ class AzureFloatingIP(BaseFloatingIP):
         """
         Set the floating IP label.
         """
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         self._ip.tags.update(Label=value)
         self._provider.azure_client. \
             update_fip_tags(self.id, self._ip)
@@ -1170,7 +1170,7 @@ class AzureSubnet(BaseSubnet):
     @label.setter
     # pylint:disable=arguments-differ
     def label(self, value):
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         az_network = self._network._network
         kwargs = {'SubnetLabel_' + self.name: value}
         az_network.tags.update(**kwargs)
@@ -1298,7 +1298,7 @@ class AzureInstance(BaseInstance):
         """
         Set the instance label.
         """
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         self._vm.tags.update(Label=value)
         self._provider.azure_client. \
             update_vm_tags(self.id, self._vm)
@@ -1430,7 +1430,7 @@ class AzureInstance(BaseInstance):
         CloudBridge interface to pass the private key file path
         """
 
-        self.assert_valid_resource_name(label)
+        self.assert_valid_resource_label(label)
         name = self._generate_name_from_label(label, 'cb-img')
 
         if not self._state == 'VM generalized':
@@ -1703,7 +1703,7 @@ class AzureRouter(BaseRouter):
         """
         Set the router label.
         """
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         self._route_table.tags.update(Label=value)
         self._provider.azure_client. \
             update_route_table_tags(self._route_table.name,

+ 14 - 15
cloudbridge/cloud/providers/openstack/resources.py

@@ -116,7 +116,7 @@ class OpenStackMachineImage(BaseMachineImage):
         """
         Set the image label.
         """
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         self._provider.os_conn.image.update_image(self._os_image, name=value)
 
     @property
@@ -316,7 +316,7 @@ class OpenStackInstance(BaseInstance):
         """
         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)
@@ -451,7 +451,7 @@ class OpenStackInstance(BaseInstance):
         Create a new image based on this instance.
         """
         log.debug("Creating OpenStack Image with the label %s", label)
-        self.assert_valid_resource_name(label)
+        self.assert_valid_resource_label(label)
         name = self._generate_name_from_label(label, 'cb-img')
 
         image_id = self._os_instance.create_image(name)
@@ -599,7 +599,7 @@ class OpenStackVolume(BaseVolume):
         """
         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)
 
@@ -729,7 +729,7 @@ class OpenStackSnapshot(BaseSnapshot):
         """
         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)
 
@@ -818,8 +818,7 @@ class OpenStackGatewayContainer(BaseGatewayContainer):
 
     def get_or_create_inet_gateway(self, label=None):
         """For OS, inet gtw is any net that has `external` property set."""
-        if label:
-            OpenStackInternetGateway.assert_valid_resource_name(label)
+        OpenStackInternetGateway.assert_valid_resource_label(label)
 
         external_nets = (n for n in self._provider.networking.networks
                          if n.external)
@@ -878,7 +877,7 @@ class OpenStackNetwork(BaseNetwork):
         """
         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()
@@ -959,7 +958,7 @@ class OpenStackSubnet(BaseSubnet):
         """
         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
@@ -1085,7 +1084,7 @@ class OpenStackRouter(BaseRouter):
         """
         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()
@@ -1162,14 +1161,14 @@ class OpenStackInternetGateway(BaseInternetGateway):
 
     @property
     def label(self):
-        return self._gateway_net.get('name', None)
+        return self._gateway_net.tags.get('gateway_name', None)
 
     @label.setter
     # pylint:disable=arguments-differ
     def label(self, value):
-        self.assert_valid_resource_name(value)
-        self._provider.neutron.update_network(self.id,
-                                              {'network': {'name': value}})
+        self.assert_valid_resource_label(value)
+        self._provider.neutron_client.add_tag('network', self.id,
+                                              {'gateway_name': value})
         self.refresh()
 
     @property
@@ -1235,7 +1234,7 @@ class OpenStackVMFirewall(BaseVMFirewall):
     @label.setter
     # pylint:disable=arguments-differ
     def label(self, value):
-        self.assert_valid_resource_name(value)
+        self.assert_valid_resource_label(value)
         self._provider.os_conn.network.update_security_group(self.id,
                                                              name=value)
         self.refresh()

+ 9 - 11
cloudbridge/cloud/providers/openstack/services.py

@@ -209,7 +209,7 @@ class OpenStackVMFirewallService(BaseVMFirewallService):
                                      limit=limit, marker=marker)
 
     def create(self, description, network_id, label=None):
-        OpenStackVMFirewall.assert_valid_resource_name(label)
+        OpenStackVMFirewall.assert_valid_resource_label(label)
         log.debug("Creating OpenStack VM Firewall with the params: "
                   "[label: %s network id: %s description: %s]", label,
                   network_id, description)
@@ -374,7 +374,7 @@ class OpenStackVolumeService(BaseVolumeService):
         log.debug("Creating a new volume with the params: "
                   "[label: %s size: %s zone: %s snapshot: %s description: %s]",
                   label, size, zone, snapshot, description)
-        OpenStackVolume.assert_valid_resource_name(label)
+        OpenStackVolume.assert_valid_resource_label(label)
 
         zone_id = zone.id if isinstance(zone, PlacementZone) else zone
         snapshot_id = snapshot.id if isinstance(
@@ -441,14 +441,13 @@ class OpenStackSnapshotService(BaseSnapshotService):
         Creates a new snapshot of a given volume.
         """
         log.debug("Creating a new snapshot of the %s volume.", label)
-        OpenStackSnapshot.assert_valid_resource_name(label)
-        name = OpenStackSnapshot._generate_name_from_label(label, 'cb-snap')
+        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)
 
@@ -587,7 +586,7 @@ class OpenStackInstanceService(BaseInstanceService):
                launch_config=None,
                **kwargs):
         """Create a new virtual machine instance."""
-        OpenStackInstance.assert_valid_resource_name(label)
+        OpenStackInstance.assert_valid_resource_label(label)
 
         image_id = image.id if isinstance(image, MachineImage) else image
         vm_size = vm_type.id if \
@@ -814,9 +813,8 @@ class OpenStackNetworkService(BaseNetworkService):
     def create(self, cidr_block, label=None):
         log.debug("Creating OpenStack Network with the params: "
                   "[label: %s Cinder Block: %s]", label, cidr_block)
-        OpenStackNetwork.assert_valid_resource_name(label)
-        name = OpenStackNetwork._generate_name_from_label(label, 'cb-net')
-        net_info = {'name': name}
+        OpenStackNetwork.assert_valid_resource_label(label)
+        net_info = {'name': label}
         network = self.provider.neutron.create_network({'network': net_info})
         return OpenStackNetwork(self.provider, network.get('network'))
 
@@ -848,7 +846,7 @@ class OpenStackSubnetService(BaseSubnetService):
         log.debug("Creating OpenStack Subnet with the params: "
                   "[Label: %s Network: %s Cinder Block: %s Zone: -ignored-]",
                   label, network, cidr_block)
-        OpenStackSubnet.assert_valid_resource_name(label)
+        OpenStackSubnet.assert_valid_resource_label(label)
 
         network_id = (network.id if isinstance(network, OpenStackNetwork)
                       else network)
@@ -924,7 +922,7 @@ class OpenStackRouterService(BaseRouterService):
             ?expanded=delete-router-detail,create-router-detail#create-router
         """
         log.debug("Creating OpenStack Router with the label: %s", label)
-        OpenStackRouter.assert_valid_resource_name(label)
+        OpenStackRouter.assert_valid_resource_label(label)
 
         body = {'router': {'name': label}} if label else None
         router = self.provider.neutron.create_router(body)

+ 48 - 10
test/helpers/standard_interface_tests.py

@@ -140,10 +140,11 @@ def check_obj_label(test, obj):
     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_LABEL = u"hello-world-123"
         original_label = obj.label
+        # A none value should be allowed
+        obj.label = None
+        test.assertFalse(obj.label)
+        VALID_LABEL = u"hello-world-123"
         obj.label = VALID_LABEL
         # setting spaces should raise an exception
         with test.assertRaises(InvalidLabelException):
@@ -154,6 +155,12 @@ def check_obj_label(test, obj):
         # setting special characters should raise an exception
         with test.assertRaises(InvalidLabelException):
             obj.label = "hello.world:how_goes_it"
+        # Starting with a dash should raise an exception
+        with test.assertRaises(InvalidLabelException):
+            obj.label = "-hello"
+        # Ending with a dash should raise an exception
+        with test.assertRaises(InvalidLabelException):
+            obj.label = "hello-"
         # setting a length > 63 should result in an exception
         with test.assertRaises(InvalidLabelException,
                                msg="Label of length > 64 is not allowed"):
@@ -207,7 +214,7 @@ def check_standard_behaviour(test, service, obj):
 
 
 def check_create(test, service, iface, name_prefix,
-                 create_func, cleanup_func):
+                 create_func, cleanup_func, supports_labels):
     # check create with invalid label
     with test.assertRaises(InvalidLabelException):
         # spaces should raise an exception
@@ -219,15 +226,41 @@ def check_create(test, service, iface, name_prefix,
     # setting special characters should raise an exception
     with test.assertRaises(InvalidLabelException):
         create_func("hello.world:how_goes_it")
+    # Starting with a dash should raise an exception
+    with test.assertRaises(InvalidLabelException):
+        create_func("-hello")
+    # Ending with a dash should raise an exception
+    with test.assertRaises(InvalidLabelException):
+        create_func("hello-")
+    # underscores are not allowed
+    with test.assertRaises(InvalidLabelException):
+        create_func("hello_bucket")
     # setting a length > 63 should result in an exception
     with test.assertRaises(InvalidLabelException,
                            msg="Label of length > 63 should be disallowed"):
         create_func("a" * 64)
+    #  name cannot be an IP address
+    with test.assertRaises(InvalidLabelException):
+        create_func("197.10.100.42")
+
+    if supports_labels:
+        # empty labels should be allowed
+        obj = None
+        with helpers.cleanup_action(lambda: cleanup_func(obj)):
+            obj = create_func(None)
+    else:  # supports name only
+        # empty name are not allowed
+        with test.assertRaises(InvalidLabelException):
+            create_func(None)
+        # names of length less than 3 should raise an exception
+        with test.assertRaises(InvalidLabelException):
+            create_func("cb")
 
 
 def check_crud(test, service, iface, label_prefix,
                create_func, cleanup_func, extra_test_func=None,
-               custom_check_delete=None, skip_label_check=False):
+               custom_check_delete=None, supports_labels=True,
+               skip_name_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
@@ -273,21 +306,26 @@ def check_crud(test, service, iface, label_prefix,
                                 instead of the standard check_delete function
                                 to make sure that the object has been deleted.
 
-    :type  skip_label_check: ``boolean``
-    :param skip_label_check:  If True, the invalid label checking will be
+    :type  supports_labels: ``boolean``
+    :param supports_labels:  Indicates whether the resource supports labels.
+        If so, label related tests will be run.
+
+    :type  skip_name_check: ``boolean``
+    :param skip_name_check:  If True, the name related checking will be
                              skipped.
+
     """
 
     obj = None
     with helpers.cleanup_action(lambda: cleanup_func(obj)):
-        if not skip_label_check:
-            check_create(test, service, iface, label_prefix,
-                         create_func, cleanup_func)
         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)
+        if not skip_name_check:
+            check_create(test, service, iface, label_prefix,
+                         create_func, cleanup_func, supports_labels)
         if extra_test_func:
             extra_test_func(obj)
     if custom_check_delete:

+ 1 - 1
test/test_network_service.py

@@ -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_label_check=True)
+                           skip_name_check=True)
 
     def test_floating_ip_properties(self):
         # Check floating IP address

+ 2 - 19
test/test_object_store_service.py

@@ -8,7 +8,6 @@ from unittest import skip
 
 import requests
 
-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 +35,9 @@ class CloudObjectStoreServiceTestCase(ProviderTestBase):
             if bucket:
                 bucket.delete()
 
-        with self.assertRaises(InvalidLabelException):
-            # underscores are not allowed in bucket names
-            create_bucket("cb_bucket")
-
-        with self.assertRaises(InvalidLabelException):
-            # names of length less than 3 should raise an exception
-            create_bucket("cb")
-
-        with self.assertRaises(InvalidLabelException):
-            # names of length greater than 63 should raise an exception
-            create_bucket("a" * 64)
-
-        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_label_check=True)
+                       supports_labels=False)
 
     @helpers.skipIfNoService(['storage.buckets'])
     def test_crud_bucket_object(self):
@@ -79,7 +62,7 @@ class CloudObjectStoreServiceTestCase(ProviderTestBase):
 
             sit.check_crud(self, test_bucket.objects, BucketObject,
                            "cb-bucketobj", create_bucket_obj,
-                           cleanup_bucket_obj, skip_label_check=True)
+                           cleanup_bucket_obj, skip_name_check=True)
 
     @helpers.skipIfNoService(['storage.buckets'])
     def test_crud_bucket_object_properties(self):

+ 2 - 2
test/test_security_service.py

@@ -32,7 +32,7 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
 
         sit.check_crud(self, self.provider.security.key_pairs, KeyPair,
                        "cb-crudkp", create_kp, cleanup_kp,
-                       extra_test_func=extra_tests)
+                       supports_labels=False, extra_test_func=extra_tests)
 
     @helpers.skipIfNoService(['security.key_pairs'])
     def test_key_pair_properties(self):
@@ -124,7 +124,7 @@ class CloudSecurityServiceTestCase(ProviderTestBase):
 
                 sit.check_crud(self, fw.rules, VMFirewallRule, "cb-crudfwrule",
                                create_fw_rule, cleanup_fw_rule,
-                               skip_label_check=True)
+                               skip_name_check=True)
 
     @helpers.skipIfNoService(['security.vm_firewalls'])
     def test_vm_firewall_rule_properties(self):