Merge "Add tests for Nova SR-IOV cold migrate" into mcp/pike
diff --git a/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml b/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml
new file mode 100644
index 0000000..0da2ddc
--- /dev/null
+++ b/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml
@@ -0,0 +1,10 @@
+---
+features:
+  - |
+    New string configuration option ``vnc_server_header`` is added
+    to ``compute-feature-enabled`` section. It offers to provide VNC server
+    name that is to be expected in the responce header. For example, obvious
+    at hand names is 'WebSockify', 'nginx'.
+fixes:
+  - |
+    Fix VNC server response header issue when it is behind reverse proxy
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 705814c..aae2e9b 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -508,10 +508,10 @@
     def get_host_other_than(self, server_id):
         source_host = self.get_host_for_server(server_id)
 
-        hypers = self.os_admin.hypervisor_client.list_hypervisors(
-            )['hypervisors']
-        hosts = [hyper['hypervisor_hostname'] for hyper in hypers
-                 if hyper['state'] == 'up' and hyper['status'] == 'enabled']
+        svcs = self.os_admin.services_client.list_services(
+            binary='nova-compute')['services']
+        hosts = [svc['host'] for svc in svcs
+                 if svc['state'] == 'up' and svc['status'] == 'enabled']
 
         for target_host in hosts:
             if source_host != target_host:
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index d9581e3..1dfd0f9 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -151,11 +151,22 @@
         self.assertTrue(
             self._websocket.response.startswith(b'HTTP/1.1 101 Switching '
                                                 b'Protocols\r\n'),
-            'Did not get the expected 101 on the websockify call: '
-            + six.text_type(self._websocket.response))
-        self.assertTrue(
-            self._websocket.response.find(b'Server: WebSockify') > 0,
-            'Did not get the expected WebSocket HTTP Response.')
+            'Did not get the expected 101 on the {} call: {}'.format(
+                CONF.compute_feature_enabled.vnc_server_header,
+                six.text_type(self._websocket.response)
+            )
+        )
+        # Since every other server type returns Headers with different case
+        # (for example 'nginx'), lowercase must be applied to eliminate issues.
+        _desired_header = "server: {0}".format(
+            CONF.compute_feature_enabled.vnc_server_header
+        ).lower()
+        _response = six.text_type(self._websocket.response).lower()
+        self.assertIn(
+            _desired_header,
+            _response,
+            'Did not get the expected WebSocket HTTP Response.'
+        )
 
     @decorators.idempotent_id('c640fdff-8ab4-45a4-a5d8-7e6146cbd0dc')
     def test_novnc(self):
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index ee72163..70e71c3 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -95,8 +95,8 @@
         cls.policies = None
 
         if CONF.object_storage_feature_enabled.discoverability:
-            body = cls.capabilities_client.list_capabilities()
-
+            body = cls.capabilities_client.list_capabilities(
+                api_prefix=CONF.object_storage.api_prefix)
             if 'swift' in body and 'policies' in body['swift']:
                 cls.policies = body['swift']['policies']
 
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index d7c85a2..a190769 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -134,8 +134,8 @@
         not CONF.object_storage_feature_enabled.discoverability,
         'Discoverability function is disabled')
     def test_list_extensions(self):
-        resp = self.capabilities_client.list_capabilities()
-
+        resp = self.capabilities_client.list_capabilities(
+            api_prefix=CONF.object_storage.api_prefix)
         self.assertThat(resp, custom_matchers.AreAllWellFormatted())
 
     @decorators.idempotent_id('5cfa4ab2-4373-48dd-a41f-a532b12b08b2')
diff --git a/tempest/api/object_storage/test_container_services_negative.py b/tempest/api/object_storage/test_container_services_negative.py
index b8c83b7..9b9957f 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -32,7 +32,8 @@
 
         if CONF.object_storage_feature_enabled.discoverability:
             # use /info to get default constraints
-            body = cls.capabilities_client.list_capabilities()
+            body = cls.capabilities_client.list_capabilities(
+                api_prefix=CONF.object_storage.api_prefix)
             cls.constraints = body['swift']
 
     @decorators.attr(type=["negative"])
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 10afee0..b371af7 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -14,6 +14,7 @@
 import time
 
 from oslo_log import log as logging
+import six
 
 from tempest.common import image as common_image
 from tempest import config
@@ -267,13 +268,16 @@
 
 
 def wait_for_interface_status(client, server_id, port_id, status):
-    """Waits for an interface to reach a given status."""
+    """Waits for an interface to reach a (one of) given status(es)."""
+    if isinstance(status, six.string_types):
+        status = [status]
+
     body = (client.show_interface(server_id, port_id)
             ['interfaceAttachment'])
     interface_status = body['port_state']
     start = int(time.time())
 
-    while(interface_status != status):
+    while(interface_status not in status):
         time.sleep(client.build_interval)
         body = (client.show_interface(server_id, port_id)
                 ['interfaceAttachment'])
@@ -281,8 +285,8 @@
 
         timed_out = int(time.time()) - start >= client.build_timeout
 
-        if interface_status != status and timed_out:
-            message = ('Interface %s failed to reach %s status '
+        if interface_status not in status and timed_out:
+            message = ('Interface %s failed to reach either of %s statuses '
                        '(current %s) within the required time (%s s).' %
                        (port_id, status, interface_status,
                         client.build_timeout))
diff --git a/tempest/config.py b/tempest/config.py
index 6f609f1..fc3946f 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -420,6 +420,10 @@
                 default=False,
                 help='Enable VNC console. This configuration value should '
                      'be same as [nova.vnc]->vnc_enabled in nova.conf'),
+    cfg.StrOpt('vnc_server_header',
+               default='WebSockify',
+               help='Expected VNC server name (WebSockify, nginx, etc) '
+                    'in response header.'),
     cfg.BoolOpt('spice_console',
                 default=False,
                 help='Enable Spice console. This configuration value should '
@@ -904,6 +908,10 @@
                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.StrOpt('api_prefix',
+               default='',
+               help="Prefix for check capabilities. "
+                    "We need add prefix for testing radosgw by swift tests."),
 ]
 
 object_storage_feature_group = cfg.OptGroup(
diff --git a/tempest/lib/services/object_storage/capabilities_client.py b/tempest/lib/services/object_storage/capabilities_client.py
index d31bbc2..50844f1 100644
--- a/tempest/lib/services/object_storage/capabilities_client.py
+++ b/tempest/lib/services/object_storage/capabilities_client.py
@@ -20,10 +20,10 @@
 
 class CapabilitiesClient(rest_client.RestClient):
 
-    def list_capabilities(self):
+    def list_capabilities(self, api_prefix=''):
         self.skip_path()
         try:
-            resp, body = self.get('info')
+            resp, body = self.get('%sinfo' % api_prefix)
         finally:
             self.reset_path()
         body = json.loads(body)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 06b4b59..80c9235 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -771,8 +771,14 @@
         ports = self.os_admin.ports_client.list_ports(
             device_id=server['id'], fixed_ip=ip_addr)['ports']
         # A port can have more than one IP address in some cases.
-        # If the network is dual-stack (IPv4 + IPv6), this port is associated
-        # with 2 subnets
+        # If the network is dual-stack (IPv4 + IPv6), this port
+        # is associated with 2 subnets
+        port_map = [{"id": p["id"],
+                     "ip": fxip["ip_address"],
+                     "status": p["status"]}
+                    for p in ports
+                    for fxip in p["fixed_ips"]
+                    if netutils.is_valid_ipv4(fxip["ip_address"])]
         p_status = ['ACTIVE']
         # NOTE(vsaienko) With Ironic, instances live on separate hardware
         # servers. Neutron does not bind ports for Ironic instances, as a
@@ -780,22 +786,31 @@
         # TODO(vsaienko) remove once bug: #1599836 is resolved.
         if getattr(CONF.service_available, 'ironic', False):
             p_status.append('DOWN')
-        port_map = [(p["id"], fxip["ip_address"])
-                    for p in ports
-                    for fxip in p["fixed_ips"]
-                    if netutils.is_valid_ipv4(fxip["ip_address"])
-                    and p['status'] in p_status]
-        inactive = [p for p in ports if p['status'] != 'ACTIVE']
+        # TODO(pas-ha) add a new waiter that will wait for all/at least one
+        # port on instance at once, and use it here
+        for pm in port_map:
+            try:
+                port = waiters.wait_for_interface_status(self.interface_client,
+                                                         server['id'],
+                                                         pm["id"], p_status)
+                pm["status"] = port['port_state']
+            except lib_exc.TimeoutException:
+                # NOTE(pas-ha) as server might have several IPv4 ports
+                # we need at least one of them to be in appropriate state
+                # so just ignore timeouts and deal with it later
+                pass
+        port_map = [p for p in port_map if p["status"] in p_status]
+        inactive = [p for p in port_map if p["status"] != 'ACTIVE']
         if inactive:
-            LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
-
+            LOG.warning("Instance has port that are not ACTIVE: %s", inactive)
         self.assertNotEmpty(port_map,
                             "No IPv4 addresses found in: %s" % ports)
         self.assertEqual(len(port_map), 1,
                          "Found multiple IPv4 addresses: %s. "
                          "Unable to determine which port to target."
                          % port_map)
-        return port_map[0]
+
+        return port_map[0]["id"], port_map[0]["ip"]
 
     def _get_network_by_name(self, network_name):
         net = self.os_admin.networks_client.list_networks(
diff --git a/tox.ini b/tox.ini
index 21696eb..4431289 100644
--- a/tox.ini
+++ b/tox.ini
@@ -23,11 +23,12 @@
 passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH TEMPEST_CONFIG TEMPEST_CONFIG_DIR http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY ZUUL_CACHE_DIR REQUIREMENTS_PIP_LOCATION GENERATE_TEMPEST_PLUGIN_LIST
 usedevelop = True
 install_command =
-    {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
+    {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=83b1de74834fbcdbe749e270c29e76cdc28f85d5} {opts} {packages}
 whitelist_externals = *
 deps =
     -r{toxinidir}/requirements.txt
     -r{toxinidir}/test-requirements.txt
+    pycodestyle<=2.3.1
 commands =
     find . -type f -name "*.pyc" -delete
     stestr --test-path ./tempest/tests run {posargs}