Merge "Fix test_project_create_with_parent"
diff --git a/requirements.txt b/requirements.txt
index 7d01f69..84be219 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -14,7 +14,7 @@
 oslo.i18n>=2.1.0 # Apache-2.0
 oslo.log>=1.14.0 # Apache-2.0
 oslo.serialization>=1.10.0 # Apache-2.0
-oslo.utils>=3.11.0 # Apache-2.0
+oslo.utils>=3.14.0 # Apache-2.0
 six>=1.9.0 # MIT
 fixtures>=3.0.0 # Apache-2.0/BSD
 testscenarios>=0.4 # Apache-2.0/BSD
diff --git a/tempest/api/network/test_allowed_address_pair.py b/tempest/api/network/test_allowed_address_pair.py
index b2892e5..92dfc56 100644
--- a/tempest/api/network/test_allowed_address_pair.py
+++ b/tempest/api/network/test_allowed_address_pair.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 import netaddr
+import six
 
 from tempest.api.network import base
 from tempest import config
@@ -90,7 +91,8 @@
         body = self.ports_client.update_port(
             port_id, allowed_address_pairs=allowed_address_pairs)
         allowed_address_pair = body['port']['allowed_address_pairs']
-        self.assertEqual(allowed_address_pair, allowed_address_pairs)
+        six.assertCountEqual(self, allowed_address_pair,
+                             allowed_address_pairs)
 
     @test.idempotent_id('9599b337-272c-47fd-b3cf-509414414ac4')
     def test_update_port_with_address_pair(self):
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 66bab51..b6dc488 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -27,17 +27,17 @@
 CONF = config.CONF
 
 
-class VolumesBackupsV2Test(base.BaseVolumeAdminTest):
+class VolumesBackupsAdminV2Test(base.BaseVolumeAdminTest):
 
     @classmethod
     def skip_checks(cls):
-        super(VolumesBackupsV2Test, cls).skip_checks()
+        super(VolumesBackupsAdminV2Test, cls).skip_checks()
         if not CONF.volume_feature_enabled.backup:
             raise cls.skipException("Cinder backup feature disabled")
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesBackupsV2Test, cls).resource_setup()
+        super(VolumesBackupsAdminV2Test, cls).resource_setup()
 
         cls.volume = cls.create_volume()
 
@@ -167,5 +167,5 @@
                                                          'available')
 
 
-class VolumesBackupsV1Test(VolumesBackupsV2Test):
+class VolumesBackupsAdminV1Test(VolumesBackupsAdminV2Test):
     _api_version = 1
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index e9be529..087b9a8 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -76,6 +76,7 @@
         else:
             cls.snapshots_client = cls.os.snapshots_v2_client
             cls.volumes_client = cls.os.volumes_v2_client
+            cls.backups_client = cls.os.backups_v2_client
             cls.volumes_extension_client = cls.os.volumes_v2_extension_client
             cls.availability_zone_client = (
                 cls.os.volume_v2_availability_zone_client)
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
new file mode 100644
index 0000000..87146db
--- /dev/null
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -0,0 +1,72 @@
+# Copyright 2016 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.volume import base
+from tempest.common.utils import data_utils
+from tempest.common import waiters
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+
+class VolumesBackupsV2Test(base.BaseVolumeTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumesBackupsV2Test, cls).skip_checks()
+        if not CONF.volume_feature_enabled.backup:
+            raise cls.skipException("Cinder backup feature disabled")
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumesBackupsV2Test, cls).resource_setup()
+
+        cls.volume = cls.create_volume()
+
+    @test.idempotent_id('07af8f6d-80af-44c9-a5dc-c8427b1b62e6')
+    @test.services('compute')
+    def test_backup_create_attached_volume(self):
+        """Test backup create using force flag.
+
+        Cinder allows to create a volume backup, whether the volume status
+        is "available" or "in-use".
+        """
+        # Create a server
+        server_name = data_utils.rand_name('instance')
+        server = self.create_server(name=server_name, wait_until='ACTIVE')
+        self.addCleanup(self.servers_client.delete_server, server['id'])
+        # Attach volume to instance
+        self.servers_client.attach_volume(server['id'],
+                                          volumeId=self.volume['id'])
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       self.volume['id'], 'in-use')
+        self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
+                        self.volume['id'], 'available')
+        self.addCleanup(self.servers_client.detach_volume, server['id'],
+                        self.volume['id'])
+        # Create backup using force flag
+        backup_name = data_utils.rand_name('Backup')
+        backup = self.backups_client.create_backup(
+            volume_id=self.volume['id'],
+            name=backup_name, force=True)['backup']
+        self.addCleanup(self.backups_client.delete_backup, backup['id'])
+        self.backups_client.wait_for_backup_status(backup['id'],
+                                                   'available')
+        self.assertEqual(backup_name, backup['name'])
+
+
+class VolumesBackupsV1Test(VolumesBackupsV2Test):
+    _api_version = 1
diff --git a/tempest/common/fixed_network.py b/tempest/common/fixed_network.py
index 5f0685e..f57c18a 100644
--- a/tempest/common/fixed_network.py
+++ b/tempest/common/fixed_network.py
@@ -14,7 +14,7 @@
 from oslo_log import log as logging
 
 from tempest import exceptions
-from tempest.lib.common.utils import misc as misc_utils
+from tempest.lib.common.utils import test_utils
 
 LOG = logging.getLogger(__name__)
 
@@ -31,7 +31,7 @@
         list returns a 404, there are no found networks, or the found network
         is invalid
     """
-    caller = misc_utils.find_test_caller()
+    caller = test_utils.find_test_caller()
 
     if not name:
         raise exceptions.InvalidTestResource(type='network', name=name)
@@ -84,7 +84,7 @@
            tenant network is available in the creds provider
     :returns: a dict with 'id' and 'name' of the network
     """
-    caller = misc_utils.find_test_caller()
+    caller = test_utils.find_test_caller()
     net_creds = creds_provider.get_primary_creds()
     network = getattr(net_creds, 'network', None)
     if not network or not network.get('name'):
diff --git a/tempest/common/image.py b/tempest/common/image.py
index 72e3a72..95a7d1a 100644
--- a/tempest/common/image.py
+++ b/tempest/common/image.py
@@ -47,6 +47,11 @@
     fields_copy = copy.deepcopy(metadata)
 
     copy_from = fields_copy.pop('copy_from', None)
+    purge = fields_copy.pop('purge_props', None)
+
+    if purge is not None:
+        headers['x-glance-registry-purge-props'] = purge
+
     if copy_from is not None:
         headers['x-glance-api-copy-from'] = copy_from
 
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index e083167..df08e30 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -18,7 +18,7 @@
 from tempest.common import image as common_image
 from tempest import config
 from tempest import exceptions
-from tempest.lib.common.utils import misc as misc_utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.image.v1 import images_client as images_v1_client
 
@@ -91,7 +91,7 @@
                         'timeout': timeout})
             message += ' Current status: %s.' % server_status
             message += ' Current task state: %s.' % task_state
-            caller = misc_utils.find_test_caller()
+            caller = test_utils.find_test_caller()
             if caller:
                 message = '(%s) %s' % (caller, message)
             raise exceptions.TimeoutException(message)
@@ -162,7 +162,7 @@
                                           'status': status,
                                           'current_status': current_status,
                                           'timeout': client.build_timeout})
-    caller = misc_utils.find_test_caller()
+    caller = test_utils.find_test_caller()
     if caller:
         message = '(%s) %s' % (caller, message)
     raise exceptions.TimeoutException(message)
@@ -235,7 +235,7 @@
                         'status': status,
                         'timeout': client.build_timeout})
             message += ' Current state of %s: %s.' % (attr, status_curr)
-            caller = misc_utils.find_test_caller()
+            caller = test_utils.find_test_caller()
             if caller:
                 message = '(%s) %s' % (caller, message)
             raise exceptions.TimeoutException(message)
diff --git a/tempest/tests/common/test_image.py b/tempest/tests/common/test_image.py
index 34772a2..240df4d 100644
--- a/tempest/tests/common/test_image.py
+++ b/tempest/tests/common/test_image.py
@@ -46,7 +46,8 @@
             disk_format='vhd',
             copy_from='http://localhost/images/10',
             properties={'foo': 'bar'},
-            api={'abc': 'def'})
+            api={'abc': 'def'},
+            purge_props=True)
 
         expected = {
             'x-image-meta-name': 'test',
@@ -54,6 +55,7 @@
             'x-image-meta-disk_format': 'vhd',
             'x-glance-api-copy-from': 'http://localhost/images/10',
             'x-image-meta-property-foo': 'bar',
-            'x-glance-api-property-abc': 'def'
+            'x-glance-api-property-abc': 'def',
+            'x-glance-registry-purge-props': True
         }
         self.assertEqual(expected, observed)