Merge "Adding network api xml support"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index f1aaa07..703d92a 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -1,5 +1,5 @@
 [DEFAULT]
-# log_config = /opt/stack/tempest/etc/logging.conf.sample
+#log_config = /opt/stack/tempest/etc/logging.conf.sample
 
 # disable logging to the stderr
 use_stderr = False
@@ -272,6 +272,9 @@
 # Set to True if the Account Quota middleware is enabled
 accounts_quotas_available = True
 
+# Set operator role for tests that require creating a container
+operator_role = Member
+
 [boto]
 # This section contains configuration options used when executing tests
 # with boto.
@@ -285,7 +288,7 @@
 aws_access =
 aws_secret =
 
-#Image materials for S3 upload
+# Image materials for S3 upload
 # ALL content of the specified directory will be uploaded to S3
 s3_materials_path = /opt/stack/devstack/files/images/s3-materials/cirros-0.3.1
 
@@ -293,22 +296,22 @@
 # Subdirectories not allowed!
 # The filenames will be used as a Keys in the S3 Buckets
 
-#ARI Ramdisk manifest. Must be in the above s3_materials_path
+# ARI Ramdisk manifest. Must be in the above s3_materials_path
 ari_manifest = cirros-0.3.1-x86_64-initrd.manifest.xml
 
-#AMI Machine Image manifest. Must be in the above s3_materials_path
+# AMI Machine Image manifest. Must be in the above s3_materials_path
 ami_manifest = cirros-0.3.1-x86_64-blank.img.manifest.xml
 
-#AKI Kernel Image manifest, Must be in the above s3_materials_path
+# AKI Kernel Image manifest, Must be in the above s3_materials_path
 aki_manifest = cirros-0.3.1-x86_64-vmlinuz.manifest.xml
 
-#Instance type
+# Instance type
 instance_type = m1.tiny
 
-#TCP/IP connection timeout
+# TCP/IP connection timeout
 http_socket_timeout = 5
 
-#Number of retries actions on connection or 5xx error
+# Number of retries actions on connection or 5xx error
 num_retries = 1
 
 # Status change wait timout
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index a3b051e..b67a5e0 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -55,6 +55,7 @@
         # Start a server and wait for it to become ready
         resp, server = self.create_server(wait_until='ACTIVE',
                                           adminPass='password')
+        self.server = server
 
         # Record addresses so that we can ssh later
         resp, server['addresses'] = \
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index cab84c0..980323a 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -54,7 +54,7 @@
         resp[1], _ = cls.v3_client.delete_group(cls.group_body['id'])
         resp[2], _ = cls.v3_client.delete_user(cls.user_body['id'])
         resp[3], _ = cls.v3_client.delete_project(cls.project['id'])
-        #NOTE(harika-vakadi): It is necessary to disable the domian
+        # NOTE(harika-vakadi): It is necessary to disable the domian
         # before deleting,or else it would result in unauthorized error
         cls.v3_client.update_domain(cls.domain['id'], enabled=False)
         resp[4], _ = cls.v3_client.delete_domain(cls.domain['id'])
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 820328c..e6e8d17 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -18,6 +18,7 @@
 
 from tempest.api.identity.base import DataGenerator
 from tempest import clients
+from tempest.common import isolated_creds
 from tempest import exceptions
 import tempest.test
 
@@ -30,16 +31,41 @@
         if not cls.config.service_available.swift:
             skip_msg = ("%s skipped as swift is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
-        cls.os = clients.Manager()
+        cls.isolated_creds = isolated_creds.IsolatedCreds(cls.__name__)
+        if cls.config.compute.allow_tenant_isolation:
+            # Get isolated creds for normal user
+            creds = cls.isolated_creds.get_primary_creds()
+            username, tenant_name, password = creds
+            cls.os = clients.Manager(username=username,
+                                     password=password,
+                                     tenant_name=tenant_name)
+            # Get isolated creds for admin user
+            admin_creds = cls.isolated_creds.get_admin_creds()
+            admin_username, admin_tenant_name, admin_password = admin_creds
+            cls.os_admin = clients.Manager(username=admin_username,
+                                           password=admin_password,
+                                           tenant_name=admin_tenant_name)
+            # Get isolated creds for alt user
+            alt_creds = cls.isolated_creds.get_alt_creds()
+            alt_username, alt_tenant, alt_password = alt_creds
+            cls.os_alt = clients.Manager(username=alt_username,
+                                         password=alt_password,
+                                         tenant_name=alt_tenant)
+            # Add isolated users to operator role so that they can create a
+            # container in swift.
+            cls._assign_member_role()
+        else:
+            cls.os = clients.Manager()
+            cls.os_admin = clients.AdminManager()
+            cls.os_alt = clients.AltManager()
+
         cls.object_client = cls.os.object_client
         cls.container_client = cls.os.container_client
         cls.account_client = cls.os.account_client
         cls.custom_object_client = cls.os.custom_object_client
-        cls.os_admin = clients.AdminManager()
         cls.token_client = cls.os_admin.token_client
         cls.identity_admin_client = cls.os_admin.identity_client
         cls.custom_account_client = cls.os.custom_account_client
-        cls.os_alt = clients.AltManager()
         cls.object_client_alt = cls.os_alt.object_client
         cls.container_client_alt = cls.os_alt.container_client
         cls.identity_client_alt = cls.os_alt.identity_client
@@ -47,6 +73,22 @@
         cls.data = DataGenerator(cls.identity_admin_client)
 
     @classmethod
+    def _assign_member_role(cls):
+        primary_user = cls.isolated_creds.get_primary_user()
+        alt_user = cls.isolated_creds.get_alt_user()
+        swift_role = cls.config.object_storage.operator_role
+        try:
+            resp, roles = cls.os_admin.identity_client.list_roles()
+            role = next(r for r in roles if r['name'] == swift_role)
+        except StopIteration:
+            msg = "No role named %s found" % swift_role
+            raise exceptions.NotFound(msg)
+        for user in [primary_user, alt_user]:
+            cls.os_admin.identity_client.assign_user_role(user['tenantId'],
+                                                          user['id'],
+                                                          role['id'])
+
+    @classmethod
     def delete_containers(cls, containers, container_client=None,
                           object_client=None):
         """Remove given containers and all objects in them.
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index f04d23f..b34d516 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -93,7 +93,7 @@
     def cmd_with_auth(self, cmd, action, flags='', params='',
                       admin=True, fail_ok=False):
         """Executes given command with auth attributes appended."""
-        #TODO(jogo) make admin=False work
+        # TODO(jogo) make admin=False work
         creds = ('--os-username %s --os-tenant-name %s --os-password %s '
                  '--os-auth-url %s ' % (self.identity.admin_username,
                  self.identity.admin_tenant_name, self.identity.admin_password,
diff --git a/tempest/cli/simple_read_only/test_compute.py b/tempest/cli/simple_read_only/test_compute.py
index 4c7f604..9b358e6 100644
--- a/tempest/cli/simple_read_only/test_compute.py
+++ b/tempest/cli/simple_read_only/test_compute.py
@@ -48,7 +48,7 @@
                           self.nova,
                           'this-does-nova-exist')
 
-    #NOTE(jogo): Commands in order listed in 'nova help'
+    # NOTE(jogo): Commands in order listed in 'nova help'
 
     # Positional arguments:
 
diff --git a/tempest/cli/simple_read_only/test_compute_manage.py b/tempest/cli/simple_read_only/test_compute_manage.py
index 1848827..523c65f 100644
--- a/tempest/cli/simple_read_only/test_compute_manage.py
+++ b/tempest/cli/simple_read_only/test_compute_manage.py
@@ -41,7 +41,7 @@
                           self.nova_manage,
                           'this-does-nova-exist')
 
-    #NOTE(jogo): Commands in order listed in 'nova-manage -h'
+    # NOTE(jogo): Commands in order listed in 'nova-manage -h'
 
     # test flags
     def test_help_flag(self):
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 759ab81..ea5b4f4 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -323,7 +323,7 @@
         if (resp.status in set((204, 205, 304)) or resp.status < 200 or
                 method.upper() == 'HEAD') and resp_body:
             raise exceptions.ResponseWithNonEmptyBody(status=resp.status)
-        #NOTE(afazekas):
+        # NOTE(afazekas):
         # If the HTTP Status Code is 205
         #   'The response MUST NOT include an entity.'
         # A HTTP entity has an entity-body and an 'entity-header'.
@@ -336,7 +336,7 @@
             0 != len(set(resp.keys()) - set(('status',)) -
                      self.response_header_lc - self.general_header_lc)):
                         raise exceptions.ResponseWithEntity()
-        #NOTE(afazekas)
+        # NOTE(afazekas)
         # Now the swift sometimes (delete not empty container)
         # returns with non json error response, we can create new rest class
         # for swift.
@@ -458,8 +458,8 @@
             message = resp_body
             if parse_resp:
                 resp_body = self._parse_resp(resp_body)
-                #I'm seeing both computeFault and cloudServersFault come back.
-                #Will file a bug to fix, but leave as is for now.
+                # I'm seeing both computeFault and cloudServersFault come back.
+                # Will file a bug to fix, but leave as is for now.
                 if 'cloudServersFault' in resp_body:
                     message = resp_body['cloudServersFault']['message']
                 elif 'computeFault' in resp_body:
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index de2bf43..2cbb74d 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -24,7 +24,7 @@
 
 class RemoteClient():
 
-    #Note(afazekas): It should always get an address instead of server
+    # NOTE(afazekas): It should always get an address instead of server
     def __init__(self, server, username, password=None, pkey=None):
         ssh_timeout = TempestConfig().compute.ssh_timeout
         network = TempestConfig().compute.network_for_ssh
diff --git a/tempest/config.py b/tempest/config.py
index 9b1a91e..e0ac843 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -369,6 +369,10 @@
     cfg.BoolOpt('accounts_quotas_available',
                 default=True,
                 help="Set to True if the Account Quota middleware is enabled"),
+    cfg.StrOpt('operator_role',
+               default='Member',
+               help="Role to add to users created for swift tests to "
+                    "enable creating containers"),
 ]
 
 
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index d512ae8..dded93d 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -283,6 +283,16 @@
         LOG.debug("Created server: %s", server)
         return server
 
+    def create_keypair(self, client=None, name=None):
+        if client is None:
+            client = self.compute_client
+        if name is None:
+            name = rand_name('scenario-keypair-')
+        keypair = client.keypairs.create(name)
+        self.assertEqual(keypair.name, name)
+        self.set_resource(name, keypair)
+        return keypair
+
 
 class NetworkScenarioTest(OfficialClientTest):
     """
@@ -293,7 +303,7 @@
     def check_preconditions(cls):
         if (cls.config.service_available.neutron):
             cls.enabled = True
-            #verify that neutron_available is telling the truth
+            # verify that neutron_available is telling the truth
             try:
                 cls.network_client.list_networks()
             except exc.EndpointNotFound:
@@ -312,16 +322,6 @@
             cls.config.identity.password,
             cls.config.identity.tenant_name).tenant_id
 
-    def _create_keypair(self, client, namestart='keypair-smoke-'):
-        kp_name = rand_name(namestart)
-        keypair = client.keypairs.create(kp_name)
-        try:
-            self.assertEqual(keypair.id, kp_name)
-            self.set_resource(kp_name, keypair)
-        except AttributeError:
-            self.fail("Keypair object not successfully created.")
-        return keypair
-
     def _create_security_group(self, client, namestart='secgroup-smoke-'):
         # Create security group
         sg_name = rand_name(namestart)
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index b789fa2..53d6435 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -83,11 +83,7 @@
                                         properties=properties)
 
     def nova_keypair_add(self):
-        name = rand_name('scenario-keypair-')
-
-        self.keypair = self.compute_client.keypairs.create(name=name)
-        self.addCleanup(self.compute_client.keypairs.delete, self.keypair)
-        self.assertEqual(name, self.keypair.name)
+        self.keypair = self.create_keypair()
 
     def nova_boot(self):
         create_kwargs = {'key_name': self.keypair.name}
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 99b0071..70939f6 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -16,8 +16,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
-
 from tempest.api.network import common as net_common
 from tempest.common.utils.data_utils import rand_name
 from tempest import config
@@ -43,7 +41,7 @@
          ssh server hosted at the IP address.  This check guarantees
          that the IP address is associated with the target VM.
 
-       #TODO(mnewby) - Need to implement the following:
+       # TODO(mnewby) - Need to implement the following:
        - the Tempest host can ssh into the VM via the IP address and
          successfully execute the following:
 
@@ -162,8 +160,8 @@
 
     @attr(type='smoke')
     def test_001_create_keypairs(self):
-        self.keypairs[self.tenant_id] = self._create_keypair(
-            self.compute_client)
+        self.keypairs[self.tenant_id] = self.create_keypair(
+            name=rand_name('keypair-smoke-'))
 
     @attr(type='smoke')
     def test_002_create_security_groups(self):
@@ -182,8 +180,8 @@
 
     @attr(type='smoke')
     def test_004_check_networks(self):
-        #Checks that we see the newly created network/subnet/router via
-        #checking the result of list_[networks,routers,subnets]
+        # Checks that we see the newly created network/subnet/router via
+        # checking the result of list_[networks,routers,subnets]
         seen_nets = self._list_networks()
         seen_names = [n['name'] for n in seen_nets]
         seen_ids = [n['id'] for n in seen_nets]
@@ -254,8 +252,6 @@
             self.floating_ips[server].append(floating_ip)
 
     @attr(type='smoke')
-    @testtools.skipIf(CONF.service_available.neutron,
-                      "Skipped unti bug #1210664 is resolved")
     def test_008_check_public_network_connectivity(self):
         if not self.floating_ips:
             raise self.skipTest('No floating ips have been allocated.')
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 43ac2d9..e89ff9c 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -36,14 +36,8 @@
      * Terminate the instance
     """
 
-    def create_keypair(self):
-        kp_name = rand_name('keypair-smoke')
-        self.keypair = self.compute_client.keypairs.create(kp_name)
-        try:
-            self.assertEqual(self.keypair.id, kp_name)
-            self.set_resource('keypair', self.keypair)
-        except AttributeError:
-            self.fail("Keypair object not successfully created.")
+    def add_keypair(self):
+        self.keypair = self.create_keypair()
 
     def create_security_group(self):
         sg_name = rand_name('secgroup-smoke')
@@ -83,7 +77,7 @@
 
     def boot_instance(self):
         create_kwargs = {
-            'key_name': self.get_resource('keypair').id
+            'key_name': self.keypair.id
         }
         instance = self.create_server(self.compute_client,
                                       create_kwargs=create_kwargs)
@@ -131,7 +125,7 @@
         self.remove_resource('instance')
 
     def test_server_basicops(self):
-        self.create_keypair()
+        self.add_keypair()
         self.create_security_group()
         self.boot_instance()
         self.pause_server()
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index e8ce1bd..41b0fda 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -51,10 +51,7 @@
                                   create_kwargs=create_kwargs)
 
     def _add_keypair(self):
-        name = rand_name('scenario-keypair-')
-        self.keypair = self.compute_client.keypairs.create(name=name)
-        self.addCleanup(self.compute_client.keypairs.delete, self.keypair)
-        self.assertEqual(name, self.keypair.name)
+        self.keypair = self.create_keypair()
 
     def _create_security_group_rule(self):
         sgs = self.compute_client.security_groups.list()
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 038d251..2155129 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -71,10 +71,7 @@
                                   create_kwargs=create_kwargs)
 
     def _add_keypair(self):
-        name = rand_name('scenario-keypair-')
-        self.keypair = self.compute_client.keypairs.create(name=name)
-        self.addCleanup(self.compute_client.keypairs.delete, self.keypair)
-        self.assertEqual(name, self.keypair.name)
+        self.keypair = self.create_keypair()
 
     def _create_floating_ip(self):
         floating_ip = self.compute_client.floating_ips.create()
diff --git a/tempest/whitebox/manager.py b/tempest/whitebox/manager.py
index b2632f1..3b1b107 100644
--- a/tempest/whitebox/manager.py
+++ b/tempest/whitebox/manager.py
@@ -72,7 +72,7 @@
         cls.flavor_ref = cls.config.compute.flavor_ref
         cls.flavor_ref_alt = cls.config.compute.flavor_ref_alt
 
-    #NOTE(afazekas): Mimics the helper method used in the api tests
+    # NOTE(afazekas): Mimics the helper method used in the api tests
     @classmethod
     def create_server(cls, **kwargs):
         flavor_ref = cls.config.compute.flavor_ref
@@ -127,7 +127,7 @@
             cmd = shlex.split(cmd)
             result = subprocess.Popen(cmd, stdout=subprocess.PIPE)
 
-        #Todo(rohitk): Need to define host connection parameters in config
+        # TODO(rohitk): Need to define host connection parameters in config
         else:
             client = self.get_ssh_connection(self.config.whitebox.api_host,
                                              self.config.whitebox.api_user,
diff --git a/tempest/whitebox/test_servers_whitebox.py b/tempest/whitebox/test_servers_whitebox.py
index 1c1cdeb..abe903c 100644
--- a/tempest/whitebox/test_servers_whitebox.py
+++ b/tempest/whitebox/test_servers_whitebox.py
@@ -26,7 +26,7 @@
     @classmethod
     def setUpClass(cls):
         super(ServersWhiteboxTest, cls).setUpClass()
-        #NOTE(afazekas): Strange relationship
+        # NOTE(afazekas): Strange relationship
         BaseIdentityAdminTest.setUpClass()
         cls.client = cls.servers_client
         cls.img_client = cls.images_client