Merge "Add XML support for flavors_client"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index ecd1e2a..8e7d8fb 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -33,6 +33,12 @@
 # are known.
 allow_tenant_isolation = true
 
+# Allows test cases to create/destroy tenants and users. This option
+# enables isolated test cases and better parallel execution,
+# but also requires that OpenStack Identity API admin credentials
+# are known.
+allow_tenant_reuse = true
+
 # This should be the username of a user WITHOUT administrative privileges
 username = demo
 # The above non-administrative user's password
diff --git a/etc/tempest.conf.tpl b/etc/tempest.conf.tpl
index f268de2..ac1664a 100644
--- a/etc/tempest.conf.tpl
+++ b/etc/tempest.conf.tpl
@@ -29,6 +29,12 @@
 # are known.
 allow_tenant_isolation = %COMPUTE_ALLOW_TENANT_ISOLATION%
 
+# Allows test cases to create/destroy tenants and users. This option
+# enables isolated test cases and better parallel execution,
+# but also requires that OpenStack Identity API admin credentials
+# are known.
+allow_tenant_reuse = %COMPUTE_ALLOW_TENANT_REUSE%
+
 # This should be the username of a user WITHOUT administrative privileges
 username = %USERNAME%
 # The above non-administrative user's password
diff --git a/tempest/config.py b/tempest/config.py
index 28a0c5e..003f03e 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -125,6 +125,17 @@
         return self.get("allow_tenant_isolation", 'false').lower() != 'false'
 
     @property
+    def allow_tenant_reuse(self):
+        """
+        If allow_tenant_isolation is True and a tenant that would be created
+        for a given test already exists (such as from a previously-failed run),
+        re-use that tenant instead of failing because of the conflict. Note
+        that this would result in the tenant being deleted at the end of a
+        subsequent successful run.
+        """
+        return self.get("allow_tenant_reuse", 'true').lower() != 'false'
+
+    @property
     def username(self):
         """Username to use for Nova API requests."""
         return self.get("username", "demo")
diff --git a/tempest/services/identity/json/admin_client.py b/tempest/services/identity/json/admin_client.py
index 89a068e..cb9c10b 100644
--- a/tempest/services/identity/json/admin_client.py
+++ b/tempest/services/identity/json/admin_client.py
@@ -101,6 +101,13 @@
         body = json.loads(body)
         return resp, body['tenants']
 
+    def get_tenant_by_name(self, tenant_name):
+        resp, tenants = self.list_tenants()
+        for tenant in tenants:
+            if tenant['name'] == tenant_name:
+                return tenant
+        raise exceptions.NotFound('No such tenant')
+
     def update_tenant(self, tenant_id, **kwargs):
         """Updates a tenant"""
         resp, body = self.get_tenant(tenant_id)
@@ -165,6 +172,13 @@
         body = json.loads(body)
         return resp, body['users']
 
+    def get_user_by_username(self, tenant_id, username):
+        resp, users = self.list_users_for_tenant(tenant_id)
+        for user in users:
+            if user['name'] == username:
+                return user
+        raise exceptions.NotFound('No such user')
+
     def create_service(self, name, type, **kwargs):
         """Create a service"""
         post_body = {
diff --git a/tempest/tests/compute/base.py b/tempest/tests/compute/base.py
index ba4f518..5e6eb7d 100644
--- a/tempest/tests/compute/base.py
+++ b/tempest/tests/compute/base.py
@@ -101,12 +101,37 @@
         tenant_desc = tenant_name + "-desc"
         password = "pass"
 
-        resp, tenant = admin_client.create_tenant(name=tenant_name,
-                                                  description=tenant_desc)
-        resp, user = admin_client.create_user(username,
-                                              password,
-                                              tenant['id'],
-                                              email)
+        try:
+            resp, tenant = admin_client.create_tenant(name=tenant_name,
+                                                      description=tenant_desc)
+        except exceptions.Duplicate:
+            if cls.config.compute.allow_tenant_reuse:
+                tenant = admin_client.get_tenant_by_name(tenant_name)
+                LOG.info('Re-using existing tenant %s' % tenant)
+            else:
+                msg = ('Unable to create isolated tenant %s because ' +
+                       'it already exists. If this is related to a ' +
+                       'previous test failure, try using ' +
+                       'allow_tentant_reuse in tempest.conf') % tenant_name
+                raise exceptions.Duplicate(msg)
+
+        try:
+            resp, user = admin_client.create_user(username,
+                                                  password,
+                                                  tenant['id'],
+                                                  email)
+        except exceptions.Duplicate:
+            if cls.config.compute.allow_tenant_reuse:
+                user = admin_client.get_user_by_username(tenant['id'],
+                                                         username)
+                LOG.info('Re-using existing user %s' % user)
+            else:
+                msg = ('Unable to create isolated tenant %s because ' +
+                       'it already exists. If this is related to a ' +
+                       'previous test failure, try using ' +
+                       'allow_tentant_reuse in tempest.conf') % tenant_name
+                raise exceptions.Duplicate(msg)
+
         # Store the complete creds (including UUID ids...) for later
         # but return just the username, tenant_name, password tuple
         # that the various clients will use.