Merge "Update volume and volume transfer schema"
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 0ed73a8..a69dbb3 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -43,7 +43,7 @@
         super(ServerActionsTestJSON, self).setUp()
         # Check if the server is in a clean state after test
         try:
-            validation_resources = self.get_class_validation_resources(
+            self.validation_resources = self.get_class_validation_resources(
                 self.os_primary)
             # _test_rebuild_server test compares ip address attached to the
             # server before and after the rebuild, in order to avoid
@@ -53,18 +53,18 @@
             waiters.wait_for_server_floating_ip(
                 self.client,
                 self.client.show_server(self.server_id)['server'],
-                validation_resources['floating_ip'])
+                self.validation_resources['floating_ip'])
             waiters.wait_for_server_status(self.client,
                                            self.server_id, 'ACTIVE')
         except lib_exc.NotFound:
             # The server was deleted by previous test, create a new one
             # Use class level validation resources to avoid them being
             # deleted once a test is over
-            validation_resources = self.get_class_validation_resources(
+            self.validation_resources = self.get_class_validation_resources(
                 self.os_primary)
             server = self.create_test_server(
                 validatable=True,
-                validation_resources=validation_resources,
+                validation_resources=self.validation_resources,
                 wait_until='SSHABLE')
             self.__class__.server_id = server['id']
         except Exception:
@@ -106,11 +106,9 @@
         """
         # Since this test messes with the password and makes the
         # server unreachable, it should create its own server
-        validation_resources = self.get_test_validation_resources(
-            self.os_primary)
         newserver = self.create_test_server(
             validatable=True,
-            validation_resources=validation_resources,
+            validation_resources=self.validation_resources,
             wait_until='ACTIVE')
         self.addCleanup(self.delete_server, newserver['id'])
         # The server's password should be set to the provided password
@@ -122,7 +120,7 @@
             # Verify that the user can authenticate with the new password
             server = self.client.show_server(newserver['id'])['server']
             linux_client = remote_client.RemoteClient(
-                self.get_server_ip(server, validation_resources),
+                self.get_server_ip(server, self.validation_resources),
                 self.ssh_user,
                 new_password,
                 server=server,
@@ -131,15 +129,13 @@
 
     def _test_reboot_server(self, reboot_type):
         if CONF.validation.run_validation:
-            validation_resources = self.get_class_validation_resources(
-                self.os_primary)
             # Get the time the server was last rebooted,
             server = self.client.show_server(self.server_id)['server']
             linux_client = remote_client.RemoteClient(
-                self.get_server_ip(server, validation_resources),
+                self.get_server_ip(server, self.validation_resources),
                 self.ssh_user,
                 self.password,
-                validation_resources['keypair']['private_key'],
+                self.validation_resources['keypair']['private_key'],
                 server=server,
                 servers_client=self.client)
             boot_time = linux_client.get_boot_time()
@@ -153,10 +149,10 @@
         if CONF.validation.run_validation:
             # Log in and verify the boot time has changed
             linux_client = remote_client.RemoteClient(
-                self.get_server_ip(server, validation_resources),
+                self.get_server_ip(server, self.validation_resources),
                 self.ssh_user,
                 self.password,
-                validation_resources['keypair']['private_key'],
+                self.validation_resources['keypair']['private_key'],
                 server=server,
                 servers_client=self.client)
             new_boot_time = linux_client.get_boot_time()
@@ -185,10 +181,18 @@
         server = self.client.show_server(server['id'])['server']
         self.assertNotIn('security_groups', server)
 
-    def _rebuild_server_and_check(self, image_ref):
-        rebuilt_server = (self.client.rebuild_server(self.server_id, image_ref)
+    def _rebuild_server_and_check(self, image_ref, server):
+        rebuilt_server = (self.client.rebuild_server(server['id'], image_ref)
                           ['server'])
-        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+        if CONF.validation.run_validation:
+            tenant_network = self.get_tenant_network()
+            compute.wait_for_ssh_or_ping(
+                server, self.os_primary, tenant_network,
+                True, self.validation_resources, "SSHABLE", True)
+        else:
+            waiters.wait_for_server_status(self.client, self.server['id'],
+                                           'ACTIVE')
+
         msg = ('Server was not rebuilt to the original image. '
                'The original image: {0}. The current image: {1}'
                .format(image_ref, rebuilt_server['image']['id']))
@@ -212,7 +216,8 @@
         # If the server was rebuilt on a different image, restore it to the
         # original image once the test ends
         if self.image_ref_alt != self.image_ref:
-            self.addCleanup(self._rebuild_server_and_check, self.image_ref)
+            self.addCleanup(self._rebuild_server_and_check, self.image_ref,
+                            rebuilt_server)
 
         # Verify the properties in the initial response are correct
         self.assertEqual(self.server_id, rebuilt_server['id'])
@@ -230,8 +235,6 @@
         self.assertEqual(original_addresses, server['addresses'])
 
         if CONF.validation.run_validation:
-            validation_resources = self.get_class_validation_resources(
-                self.os_primary)
             # Authentication is attempted in the following order of priority:
             # 1.The key passed in, if one was passed in.
             # 2.Any key we can find through an SSH agent (if allowed).
@@ -239,10 +242,10 @@
             #   ~/.ssh/ (if allowed).
             # 4.Plain username/password auth, if a password was given.
             linux_client = remote_client.RemoteClient(
-                self.get_server_ip(rebuilt_server, validation_resources),
+                self.get_server_ip(rebuilt_server, self.validation_resources),
                 self.ssh_alt_user,
                 password,
-                validation_resources['keypair']['private_key'],
+                self.validation_resources['keypair']['private_key'],
                 server=rebuilt_server,
                 servers_client=self.client)
             linux_client.validate_authentication()
@@ -273,7 +276,7 @@
         # If the server was rebuilt on a different image, restore it to the
         # original image once the test ends
         if self.image_ref_alt != self.image_ref:
-            self.addCleanup(self._rebuild_server_and_check, old_image)
+            self.addCleanup(self._rebuild_server_and_check, old_image, server)
 
         # Verify the properties in the initial response are correct
         self.assertEqual(self.server_id, rebuilt_server['id'])
@@ -318,13 +321,11 @@
         self.assertEqual(self.server_id,
                          vol_after_rebuild['attachments'][0]['server_id'])
         if CONF.validation.run_validation:
-            validation_resources = self.get_class_validation_resources(
-                self.os_primary)
             linux_client = remote_client.RemoteClient(
-                self.get_server_ip(server, validation_resources),
+                self.get_server_ip(server, self.validation_resources),
                 self.ssh_alt_user,
                 password=None,
-                pkey=validation_resources['keypair']['private_key'],
+                pkey=self.validation_resources['keypair']['private_key'],
                 server=server,
                 servers_client=self.client)
             linux_client.validate_authentication()
@@ -376,10 +377,8 @@
         kwargs = {'volume_backed': True,
                   'wait_until': 'ACTIVE'}
         if CONF.validation.run_validation:
-            validation_resources = self.get_test_validation_resources(
-                self.os_primary)
             kwargs.update({'validatable': True,
-                           'validation_resources': validation_resources})
+                           'validation_resources': self.validation_resources})
         server = self.create_test_server(**kwargs)
 
         # NOTE(mgoddard): Get detailed server to ensure addresses are present
@@ -395,10 +394,10 @@
             self.client.get_console_output(server['id'])
         if CONF.validation.run_validation:
             linux_client = remote_client.RemoteClient(
-                self.get_server_ip(server, validation_resources),
+                self.get_server_ip(server, self.validation_resources),
                 self.ssh_user,
                 password=None,
-                pkey=validation_resources['keypair']['private_key'],
+                pkey=self.validation_resources['keypair']['private_key'],
                 server=server,
                 servers_client=self.client)
             linux_client.validate_authentication()
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index d283ab3..96031ac 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -131,7 +131,7 @@
         # import image from web to backend
         image_uri = CONF.image.http_image
         self.client.image_import(image['id'], method='web-download',
-                                 image_uri=image_uri)
+                                 import_params={'uri': image_uri})
         waiters.wait_for_image_imported_to_stores(self.client, image['id'])
 
     @decorators.idempotent_id('e04761a1-22af-42c2-b8bc-a34a3f12b585')
diff --git a/tempest/api/image/v2/test_images_negative.py b/tempest/api/image/v2/test_images_negative.py
index a3802a9..80c01a5 100644
--- a/tempest/api/image/v2/test_images_negative.py
+++ b/tempest/api/image/v2/test_images_negative.py
@@ -206,7 +206,7 @@
         # import image from web to backend
         image_uri = 'http://does-not.exist/no/possible/way'
         self.client.image_import(image['id'], method='web-download',
-                                 image_uri=image_uri,
+                                 import_params={'uri': image_uri},
                                  stores=[stores[0]['id']])
 
         start_time = int(time.time())
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 8d8039b..7107dc4 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -16,6 +16,7 @@
 import time
 
 from tempest.common import custom_matchers
+from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import exceptions as lib_exc
@@ -124,6 +125,9 @@
                                                 object_name,
                                                 data,
                                                 metadata=metadata)
+                waiters.wait_for_object_create(cls.object_client,
+                                               container_name,
+                                               object_name)
                 return object_name, data
             # after bucket creation we might see Conflict
             except lib_exc.Conflict as e:
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
index 7977a7a..fb67fb4 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -15,6 +15,7 @@
 
 from tempest.api.object_storage import base
 from tempest.common import utils
+from tempest.common import waiters
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
@@ -91,6 +92,9 @@
         for _ in range(QUOTA_COUNT):
             name = data_utils.rand_name(name="TestObject")
             self.object_client.create_object(self.container_name, name, "")
+            waiters.wait_for_object_create(self.object_client,
+                                           self.container_name,
+                                           name)
 
         nbefore = self._get_object_count()
         self.assertEqual(nbefore, QUOTA_COUNT)
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index a58da7e..b3a04f8 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -45,7 +45,7 @@
     def test_snapshot_create_delete_with_volume_in_use(self):
         """Test create/delete snapshot from volume attached to server"""
         # Create a test instance
-        server = self.create_server()
+        server = self.create_server(wait_until='SSHABLE')
         # NOTE(zhufl) Here we create volume from self.image_ref for adding
         # coverage for "creating snapshot from non-blank volume".
         volume = self.create_volume(imageRef=self.image_ref)
@@ -80,7 +80,7 @@
         snapshot1 = self.create_snapshot(self.volume_origin['id'])
 
         # Create a server and attach it
-        server = self.create_server()
+        server = self.create_server(wait_until='SSHABLE')
         self.attach_volume(server['id'], self.volume_origin['id'])
 
         # Now that the volume is attached, create other snapshots
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 39a4e5d..f207066 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -612,3 +612,17 @@
     if caller:
         message = '(%s) %s' % (caller, message)
     raise lib_exc.TimeoutException(message)
+
+
+def wait_for_object_create(object_client, container_name, object_name,
+                           interval=1):
+    """Waits for created object to become available"""
+    start_time = time.time()
+    while time.time() - start_time < object_client.build_timeout:
+        try:
+            return object_client.get_object(container_name, object_name)
+        except lib_exc.NotFound:
+            time.sleep(interval)
+    message = ('Object %s failed to create within the required time (%s s).' %
+               (object_name, object_client.build_timeout))
+    raise lib_exc.TimeoutException(message)
diff --git a/tempest/config.py b/tempest/config.py
index 92fb31b..39e7fb3 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -1148,6 +1148,9 @@
                help="One name of cluster which is set in the realm whose name "
                     "is set in 'realm_name' item in this file. Set the "
                     "same cluster name as Swift's container-sync-realms.conf"),
+    cfg.IntOpt('build_timeout',
+               default=10,
+               help="Timeout in seconds to wait for objects to create."),
 ]
 
 object_storage_feature_group = cfg.OptGroup(
diff --git a/tempest/lib/services/image/v2/images_client.py b/tempest/lib/services/image/v2/images_client.py
index abf427c..ae6ce25 100644
--- a/tempest/lib/services/image/v2/images_client.py
+++ b/tempest/lib/services/image/v2/images_client.py
@@ -206,7 +206,7 @@
 
     def image_import(self, image_id, method='glance-direct',
                      all_stores_must_succeed=None, all_stores=True,
-                     stores=None, image_uri=None):
+                     stores=None, import_params=None):
         """Import data from staging area to glance store.
 
         For a full list of available parameters, please refer to the official
@@ -222,9 +222,11 @@
                            all available stores (incompatible with stores)
         :param stores: A list of destination store names for the import. Must
                        be None if server does not support multistore.
-        :param image_uri: A URL to be used with the web-download method
+        :param import_params: A dict of import method parameters
         """
         url = 'images/%s/import' % image_id
+        if import_params is None:
+            import_params = {}
         data = {
             "method": {
                 "name": method
@@ -237,8 +239,8 @@
 
         if all_stores_must_succeed is not None:
             data['all_stores_must_succeed'] = all_stores_must_succeed
-        if image_uri:
-            data['method']['uri'] = image_uri
+        if import_params:
+            data['method'].update(import_params)
         data = json.dumps(data)
         headers = {'Content-Type': 'application/json'}
         resp, _ = self.post(url, data, headers=headers)