Merge "Add new base manager with default network" into mcp/caracal
diff --git a/tempest/api/compute/admin/test_volume.py b/tempest/api/compute/admin/test_volume.py
index dc631e5..b0f20f6 100644
--- a/tempest/api/compute/admin/test_volume.py
+++ b/tempest/api/compute/admin/test_volume.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute import base
 from tempest.common import waiters
 from tempest import config
@@ -41,6 +43,9 @@
 class AttachSCSIVolumeTestJSON(BaseAttachSCSIVolumeTest):
     """Test attaching scsi volume to server"""
 
+    @testtools.skipIf(
+        CONF.compute_feature_enabled.barbican_integration_enabled,
+        "Not supported when barbican integration enabled.")
     @decorators.idempotent_id('777e468f-17ca-4da4-b93d-b7dbf56c0494')
     def test_attach_scsi_disk_with_config_drive(self):
         """Test the attach/detach volume with config drive/scsi disk
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 8984d1d..688b31b 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -109,21 +109,24 @@
         """
         port = self.ports_client.show_port(port_id)['port']
         device_id = port['device_id']
+        dns_name = port.get('dns_name')
         start = int(time.time())
 
         # NOTE(mriedem): Nova updates the port's device_id to '' rather than
         # None, but it's not contractual so handle Falsey either way.
-        while device_id:
+        while any([device_id, dns_name]):
             time.sleep(self.build_interval)
             port = self.ports_client.show_port(port_id)['port']
             device_id = port['device_id']
+            dns_name = port.get('dns_name')
 
             timed_out = int(time.time()) - start >= self.build_timeout
 
-            if device_id and timed_out:
-                message = ('Port %s failed to detach (device_id %s) within '
-                           'the required time (%s s).' %
-                           (port_id, device_id, self.build_timeout))
+            if any([device_id, dns_name]) and timed_out:
+                message = ('Port %s failed to detach (device_id %s), '
+                           '(dns_name %s) within the required time (%s s).' %
+                           (port_id, device_id or 'is out',
+                            dns_name or 'is out', self.build_timeout))
                 raise lib_exc.TimeoutException(message)
 
         return port
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index 1308b19..c90aea8 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -64,7 +64,15 @@
 
     def _validate_novnc_html(self, vnc_url):
         """Verify we can connect to novnc and get back the javascript."""
-        resp = urllib3.PoolManager().request('GET', vnc_url)
+        cert_params = {}
+
+        if CONF.identity.disable_ssl_certificate_validation:
+            cert_params['cert_reqs'] = "CERT_NONE"
+        else:
+            cert_params["cert_reqs"] = "CERT_REQUIRED"
+            cert_params["ca_certs"] = CONF.identity.ca_certificates_file
+
+        resp = urllib3.PoolManager(**cert_params).request('GET', vnc_url)
         # Make sure that the GET request was accepted by the novncproxy
         self.assertEqual(resp.status, 200, 'Got a Bad HTTP Response on the '
                          'initial call: ' + str(resp.status))
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 38ca53b..286e0a5 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -13,10 +13,15 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
+from tempest import config
 from tempest.lib import decorators
 
+CONF = config.CONF
+
 
 class QuotasTestJSON(base.BaseV2ComputeTest):
     """Test compute quotas"""
@@ -76,6 +81,8 @@
         for quota in expected_quota_set:
             self.assertIn(quota, quota_set.keys())
 
+    @testtools.skipIf(not CONF.auth.use_dynamic_credentials,
+                      'does not support static credentials')
     @decorators.idempotent_id('cd65d997-f7e4-4966-a7e9-d5001b674fdc')
     def test_compare_tenant_quotas_with_default_quotas(self):
         """Test tenants are created with the default compute quota values"""
diff --git a/tempest/api/network/test_subnetpools_extensions.py b/tempest/api/network/test_subnetpools_extensions.py
index 689844b..f398062 100644
--- a/tempest/api/network/test_subnetpools_extensions.py
+++ b/tempest/api/network/test_subnetpools_extensions.py
@@ -45,6 +45,9 @@
         if not utils.is_extension_enabled('subnet_allocation', 'network'):
             msg = "subnet_allocation extension not enabled."
             raise cls.skipException(msg)
+        if not utils.is_extension_enabled('default-subnetpools', 'network'):
+            msg = "default-subnetpools extension not enabled."
+            raise cls.skipException(msg)
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('62595970-ab1c-4b7f-8fcc-fddfe55e9811')
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
index f055d19..d4bf0d7 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -88,9 +88,12 @@
         nafter = self._get_bytes_used()
         self.assertEqual(nbefore, nafter)
 
+    # NOTE(vsaienko): the quotas imlementation on active/active rgw deployment
+    # have no coordination, as result quota overflow might happen, since
+    # this is upstream and we can't fix downstream. Remove test from smoke
+    # Related-Prod: PRODX-11581
     @decorators.idempotent_id('3a387039-697a-44fc-a9c0-935de31f426b')
     @utils.requires_ext(extension='container_quotas', service='object')
-    @decorators.attr(type="smoke")
     def test_upload_too_many_objects(self):
         """Attempts to upload many objects that exceeds the count limit."""
         for _ in range(QUOTA_COUNT):
diff --git a/tempest/api/volume/admin/test_volume_retype.py b/tempest/api/volume/admin/test_volume_retype.py
index 7c25f3d..4cb2262 100644
--- a/tempest/api/volume/admin/test_volume_retype.py
+++ b/tempest/api/volume/admin/test_volume_retype.py
@@ -179,10 +179,11 @@
         keys_with_change = ('volume_type',)
 
         # NOTE(vsaienko): with active-active cluster deployment volume
-        # services registered with different hostname.
-        if CONF.volume_feature_enabled.cluster_active_active:
-            keys_with_change += ('os-vol-host-attr:host',)
-        else:
+        # services registered with different hostname since we don't know
+        # which service process request host might or might not be changed.
+        # TODO(vsaienko): Revisit logic when is fixed
+        # https://bugs.launchpad.net/cinder/+bug/1874414
+        if not CONF.volume_feature_enabled.cluster_active_active:
             keys_with_no_change += ('os-vol-host-attr:host',)
 
         # Check the volume information after the retype
diff --git a/tempest/lib/api_schema/response/compute/v2_19/servers.py b/tempest/lib/api_schema/response/compute/v2_19/servers.py
index ba3d787..5d1f315 100644
--- a/tempest/lib/api_schema/response/compute/v2_19/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_19/servers.py
@@ -14,9 +14,12 @@
 
 import copy
 
+from tempest.lib.api_schema.response.compute.v2_1 import servers \
+    as servers
 from tempest.lib.api_schema.response.compute.v2_16 import servers \
     as serversv216
 
+
 # Compute microversion 2.19:
 # 1. New attributes in 'server' dict.
 #      'description'
@@ -63,3 +66,4 @@
 list_volume_attachments = copy.deepcopy(serversv216.list_volume_attachments)
 show_instance_action = copy.deepcopy(serversv216.show_instance_action)
 create_backup = copy.deepcopy(serversv216.create_backup)
+list_instance_actions = copy.deepcopy(servers.list_instance_actions)
diff --git a/tempest/lib/api_schema/response/compute/v2_58/servers.py b/tempest/lib/api_schema/response/compute/v2_58/servers.py
index 637b765..5c22c79 100644
--- a/tempest/lib/api_schema/response/compute/v2_58/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_58/servers.py
@@ -12,8 +12,10 @@
 import copy
 
 from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.compute.v2_1 import servers as servers
 from tempest.lib.api_schema.response.compute.v2_57 import servers as servers257
 
+
 # microversion 2.58 added updated_at to the response
 show_instance_action = copy.deepcopy(servers257.show_instance_action)
 show_instance_action['response_body']['properties']['instanceAction'][
@@ -21,6 +23,14 @@
 show_instance_action['response_body']['properties']['instanceAction'][
     'required'].append('updated_at')
 
+# microversion 2.58 added updated_at to the response
+list_instance_actions = copy.deepcopy(servers.list_instance_actions)
+list_instance_actions['response_body']['properties']['instanceActions'][
+    'items']['properties'].update({'updated_at': parameter_types.date_time})
+list_instance_actions['response_body']['properties']['instanceActions'][
+    'items']['required'].append('updated_at')
+
+
 # Below are the unchanged schema in this microversion. We need
 # to keep this schema in this file to have the generic way to select the
 # right schema based on self.schema_versions_info mapping in service client.
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index a2f2931..4e1dc59 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -94,6 +94,7 @@
         self.build_interval = build_interval
         self.build_timeout = build_timeout
         self.trace_requests = trace_requests
+        self.ca_certs = ca_certs
 
         self._skip_path = False
         self.general_header_lc = set(('cache-control', 'connection',
diff --git a/tempest/lib/common/utils/linux/remote_client.py b/tempest/lib/common/utils/linux/remote_client.py
index 662b452..bdf35e7 100644
--- a/tempest/lib/common/utils/linux/remote_client.py
+++ b/tempest/lib/common/utils/linux/remote_client.py
@@ -31,35 +31,33 @@
             return function(self, *args, **kwargs)
         except Exception as e:
             caller = test_utils.find_test_caller() or "not found"
-            if not isinstance(e, tempest.lib.exceptions.SSHTimeout):
-                message = ('Executing command on %(ip)s failed. '
-                           'Error: %(error)s' % {'ip': self.ip_address,
-                                                 'error': e})
-                message = '(%s) %s' % (caller, message)
-                LOG.error(message)
-                raise
-            else:
-                try:
-                    original_exception = sys.exc_info()
-                    if self.server:
+            message = ('Executing command on %(ip)s failed. '
+                       'Error: %(error)s' % {'ip': self.ip_address,
+                                             'error': e})
+            message = '(%s) %s' % (caller, message)
+            LOG.error(message)
+            try:
+                original_exception = sys.exc_info()
+                if self.server:
+                    if isinstance(e, tempest.lib.exceptions.SSHTimeout):
                         msg = 'Caller: %s. Timeout trying to ssh to server %s'
                         LOG.debug(msg, caller, self.server)
-                        if self.console_output_enabled and self.servers_client:
-                            try:
-                                msg = 'Console log for server %s: %s'
-                                console_log = (
-                                    self.servers_client.get_console_output(
-                                        self.server['id'])['output'])
-                                LOG.debug(msg, self.server['id'], console_log)
-                            except Exception:
-                                msg = 'Could not get console_log for server %s'
-                                LOG.debug(msg, self.server['id'])
-                    # raise the original ssh timeout exception
-                    raise
-                finally:
-                    # Delete the traceback to avoid circular references
-                    _, _, trace = original_exception
-                    del trace
+                    if self.console_output_enabled and self.servers_client:
+                        try:
+                            msg = 'Console log for server %s: %s'
+                            console_log = (
+                                self.servers_client.get_console_output(
+                                    self.server['id'])['output'])
+                            LOG.debug(msg, self.server['id'], console_log)
+                        except Exception:
+                            msg = 'Could not get console_log for server %s'
+                            LOG.debug(msg, self.server['id'])
+                # raise the original ssh exception
+                raise
+            finally:
+                # Delete the traceback to avoid circular references
+                _, _, trace = original_exception
+                del trace
     return wrapper
 
 
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 58008aa..274e62d 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -721,6 +721,8 @@
         resp, body = self.get("servers/%s/os-instance-actions" %
                               server_id)
         body = json.loads(body)
+        # select proper schema depending on microverion
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.list_instance_actions, resp, body)
         return rest_client.ResponseBody(resp, body)
 
diff --git a/tempest/lib/services/object_storage/object_client.py b/tempest/lib/services/object_storage/object_client.py
index 65e8227..c7ac80f 100644
--- a/tempest/lib/services/object_storage/object_client.py
+++ b/tempest/lib/services/object_storage/object_client.py
@@ -167,11 +167,14 @@
         :param parsed_url: parsed url of the remote location
         """
         context = None
-        # If CONF.identity.disable_ssl_certificate_validation is true,
-        # do not check ssl certification.
-        if self.dscv:
-            context = ssl._create_unverified_context()
         if parsed_url.scheme == 'https':
+            # If CONF.identity.disable_ssl_certificate_validation is true,
+            # do not check ssl certification.
+            if self.dscv:
+                context = ssl._create_unverified_context()
+            else:
+                context = ssl.create_default_context(
+                    cafile=self.ca_certs)
             conn = httplib.HTTPSConnection(parsed_url.netloc,
                                            context=context)
         else:
diff --git a/tempest/serial_tests/api/admin/test_aggregates.py b/tempest/serial_tests/api/admin/test_aggregates.py
index cedeec0..ce54957 100644
--- a/tempest/serial_tests/api/admin/test_aggregates.py
+++ b/tempest/serial_tests/api/admin/test_aggregates.py
@@ -222,6 +222,9 @@
     @decorators.idempotent_id('96be03c7-570d-409c-90f8-e4db3c646996')
     def test_aggregate_add_host_create_server_with_az(self):
         """Test adding a host to the given aggregate and creating a server"""
+        if CONF.production:
+            raise self.skipException("Not allowed to run this test "
+                                     "on production environment")
         self.useFixture(fixtures.LockFixture('availability_zone'))
         az_name = data_utils.rand_name(
             prefix=CONF.resource_name_prefix, name=self.az_name_prefix)
@@ -235,12 +238,20 @@
             if agg['availability_zone']:
                 hosts_in_zone.extend(agg['hosts'])
         hosts = [v for v in self.hosts_available if v not in hosts_in_zone]
-        if not hosts:
+        hosts_available = []
+        for host in hosts:
+            hypervisor_servers = (
+                self.os_admin.hypervisor_client.list_servers_on_hypervisor(
+                    host)["hypervisors"][0].get("servers", None))
+            if not hypervisor_servers:
+                hosts_available.append(host)
+        if not hosts_available:
             raise self.skipException("All hosts are already in other "
-                                     "availability zones, so can't add "
+                                     "availability zones or have running "
+                                     "instances, so can't add "
                                      "host to aggregate. \nAggregates list: "
                                      "%s" % aggregates)
-        host = hosts[0]
+        host = hosts_available[0]
 
         self.client.add_host(aggregate['id'], host=host)
         self.addCleanup(self.client.remove_host, aggregate['id'], host=host)
diff --git a/tempest/serial_tests/scenario/test_aggregates_basic_ops.py b/tempest/serial_tests/scenario/test_aggregates_basic_ops.py
index a831fe5..cc45297 100644
--- a/tempest/serial_tests/scenario/test_aggregates_basic_ops.py
+++ b/tempest/serial_tests/scenario/test_aggregates_basic_ops.py
@@ -63,7 +63,11 @@
         hosts_available = []
         for host in svc_list:
             if (host['state'] == 'up' and host['status'] == 'enabled'):
-                hosts_available.append(host['host'])
+                hypervisor_servers = (
+                    self.os_admin.hypervisor_client.list_servers_on_hypervisor(
+                        host["host"])["hypervisors"][0].get("servers", None))
+                if not hypervisor_servers:
+                    hosts_available.append(host["host"])
         aggregates = self.aggregates_client.list_aggregates()['aggregates']
         hosts_in_zone = []
         for agg in aggregates:
@@ -72,7 +76,8 @@
         hosts = [v for v in hosts_available if v not in hosts_in_zone]
         if not hosts:
             raise self.skipException("All hosts are already in other "
-                                     "availability zones, so can't add "
+                                     "availability zones or have running "
+                                     "instances, so can't add "
                                      "host to aggregate. \nAggregates list: "
                                      "%s" % aggregates)
         return hosts[0]
@@ -120,6 +125,9 @@
     @decorators.attr(type='slow')
     @utils.services('compute')
     def test_aggregate_basic_ops(self):
+        if CONF.production:
+            raise self.skipException("Not allowed to run this test "
+                                     "on production environment")
         self.useFixture(fixtures.LockFixture('availability_zone'))
         az = 'foo_zone'
         aggregate_name = data_utils.rand_name(
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 937f93a..5801f04 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -180,9 +180,7 @@
     def test_validate_debug_ssh_console(self):
         self.assertRaises(lib_exc.SSHTimeout,
                           self.conn.validate_authentication)
-        msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
-            'TestRemoteClientWithServer:test_validate_debug_ssh_console',
-            self.server)
+        msg = 'Executing command on 127.0.0.1 failed.'
         self.assertIn(msg, self.log.output)
         self.assertIn('Console output for', self.log.output)
 
@@ -190,9 +188,7 @@
         self.assertRaises(lib_exc.SSHTimeout,
                           self.conn.exec_command, 'fake command')
         self.assertIn('fake command', self.log.output)
-        msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
-            'TestRemoteClientWithServer:test_exec_command_debug_ssh_console',
-            self.server)
+        msg = 'Executing command on 127.0.0.1 failed.'
         self.assertIn(msg, self.log.output)
         self.assertIn('Console output for', self.log.output)
 
@@ -204,9 +200,7 @@
     def test_validate_debug_ssh_console(self):
         self.assertRaises(lib_exc.SSHTimeout,
                           self.conn.validate_authentication)
-        msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
-            'TestRemoteClientWithBrokenServer:test_validate_debug_ssh_console',
-            self.server)
+        msg = 'Executing command on 127.0.0.1 failed.'
         self.assertIn(msg, self.log.output)
         msg = 'Could not get console_log for server %s' % self.server['id']
         self.assertIn(msg, self.log.output)
@@ -215,10 +209,7 @@
         self.assertRaises(lib_exc.SSHTimeout,
                           self.conn.exec_command, 'fake command')
         self.assertIn('fake command', self.log.output)
-        caller = ":".join(['TestRemoteClientWithBrokenServer',
-                           'test_exec_command_debug_ssh_console'])
-        msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
-            caller, self.server)
+        msg = 'Executing command on 127.0.0.1 failed.'
         self.assertIn(msg, self.log.output)
         msg = 'Could not get console_log for server %s' % self.server['id']
         self.assertIn(msg, self.log.output)
diff --git a/tempest/tests/lib/common/utils/linux/test_remote_client.py b/tempest/tests/lib/common/utils/linux/test_remote_client.py
index df23e63..c41f178 100644
--- a/tempest/tests/lib/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/lib/common/utils/linux/test_remote_client.py
@@ -15,6 +15,8 @@
 
 from unittest import mock
 
+import fixtures
+
 from tempest.lib.common import ssh
 from tempest.lib.common.utils.linux import remote_client
 from tempest.lib import exceptions as lib_exc
@@ -29,6 +31,12 @@
 
 class TestRemoteClient(base.TestCase):
 
+    def setUp(self):
+        super(TestRemoteClient, self).setUp()
+        self.log = self.useFixture(fixtures.FakeLogger(
+            name='tempest.lib.common.utils.linux.remote_client',
+            level='DEBUG'))
+
     @mock.patch.object(ssh.Client, 'exec_command', return_value='success')
     def test_exec_command(self, mock_ssh_exec_command):
         client = remote_client.RemoteClient('192.168.1.10', 'username')
@@ -50,9 +58,8 @@
         client = remote_client.RemoteClient('192.168.1.10', 'username',
                                             server=server)
         self.assertRaises(lib_exc.SSHTimeout, client.exec_command, 'ls')
-        mock_debug.assert_called_with(
-            'Caller: %s. Timeout trying to ssh to server %s',
-            'TestRemoteClient:test_debug_ssh_without_console', server)
+        msg = 'Executing command on 192.168.1.10 failed.'
+        self.assertIn(msg, self.log.output)
 
     @mock.patch.object(remote_client.LOG, 'debug')
     @mock.patch.object(ssh.Client, 'exec_command')