Merge "Fix comments in rest_client"
diff --git a/.gitignore b/.gitignore
index 7cb052f..06a2281 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,9 +3,8 @@
 *.pyc
 __pycache__/
 etc/accounts.yaml
-etc/tempest.conf
+etc/*.conf
 etc/tempest.conf.sample
-etc/logging.conf
 include/swift_objects/swift_small
 include/swift_objects/swift_medium
 include/swift_objects/swift_large
diff --git a/.zuul.yaml b/.zuul.yaml
index 52c11c9..ee0ae9c 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -275,6 +275,8 @@
         TEMPEST_VOLUME_TYPE: volumev2
 
 - project:
+    templates:
+      - openstack-python36-jobs
     check:
       jobs:
         - devstack-tempest:
diff --git a/README.rst b/README.rst
index 2243536..c93e19f 100644
--- a/README.rst
+++ b/README.rst
@@ -95,10 +95,12 @@
    command. Tempest is expecting a ``tempest.conf`` file in etc/ so if only a
    sample exists you must rename or copy it to tempest.conf before making
    any changes to it otherwise Tempest will not know how to load it. For
-   details on configuring Tempest refer to the :ref:`tempest-configuration`.
+   details on configuring Tempest refer to the
+   `Tempest Configuration <https://docs.openstack.org/tempest/latest/configuration.html#tempest-configuration>`_
 
 #. Once the configuration is done you're now ready to run Tempest. This can
-   be done using the :ref:`tempest_run` command. This can be done by either
+   be done using the `Tempest Run <https://docs.openstack.org/tempest/latest/run.html#tempest-run>`_
+   command. This can be done by either
    running::
 
     $ tempest run
@@ -129,7 +131,8 @@
 stable interface and there are no guarantees on the Python API unless otherwise
 stated.
 
-For more details refer to the library documentation here: :ref:`library`
+For more details refer to the `library documentation
+<https://docs.openstack.org/tempest/latest/library.html#library>`_
 
 Release Versioning
 ------------------
@@ -165,8 +168,10 @@
 -------------
 
 Detailed configuration of Tempest is beyond the scope of this
-document see :ref:`tempest-configuration` for more details on configuring
-Tempest. The ``etc/tempest.conf.sample`` attempts to be a self-documenting
+document, see `Tempest Configuration Documentation
+<https://docs.openstack.org/tempest/latest/configuration.html#tempest-configuration>`_
+for more details on configuring Tempest.
+The ``etc/tempest.conf.sample`` attempts to be a self-documenting
 version of the configuration.
 
 You can generate a new sample tempest.conf file, run the following
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 6677d0a..983fa24 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -306,6 +306,10 @@
 
   .. _2.6: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id5
 
+  * `2.8`_
+
+  .. _2.8: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id7
+
   * `2.9`_
 
   .. _2.9: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id8
@@ -334,6 +338,10 @@
 
   .. _2.26: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id23
 
+* `2.28`_

+

+  .. _2.28: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id25
+
   * `2.32`_
 
   .. _2.32: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id29
diff --git a/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml b/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml
index 0da2ddc..d3a6851 100644
--- a/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml
+++ b/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml
@@ -2,9 +2,9 @@
 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'.
+    to ``compute-feature-enabled`` section. It allows the expected
+    VNC server name in the response header to be specified. For example,
+    obvious at hand names are 'WebSockify', 'nginx'.
 fixes:
   - |
     Fix VNC server response header issue when it is behind reverse proxy
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 2518703..3be014f 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   v19.0.0
    v18.0.0
    v17.2.0
    v17.1.0
diff --git a/releasenotes/source/v19.0.0.rst b/releasenotes/source/v19.0.0.rst
new file mode 100644
index 0000000..bcffe5d
--- /dev/null
+++ b/releasenotes/source/v19.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v19.0.0 Release Notes
+=====================
+
+.. release-notes:: 19.0.0 Release Notes
+   :version: 19.0.0
diff --git a/tempest/api/compute/admin/test_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
index b23c59f..14f51e9 100644
--- a/tempest/api/compute/admin/test_hypervisor.py
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -104,6 +104,19 @@
             "None of the hypervisors had a valid uptime: %s" % hypers)
 
 
+class HypervisorAdminV228Test(HypervisorAdminTestBase):
+    min_microversion = '2.28'
+
+    @decorators.idempotent_id('d46bab64-0fbe-4eb8-9133-e6ee56188cc5')
+    def test_get_list_hypervisor_details(self):
+        # NOTE(zhufl): This test tests the hypervisor APIs response schema
+        # for 2.28 microversion. No specific assert or behaviour verification
+        # is needed.
+        hypers = self._list_hypervisors()
+        self.assertNotEmpty(hypers, "No hypervisors found.")
+        self.client.show_hypervisor(hypers[0]['id'])
+
+
 class HypervisorAdminUnderV252Test(HypervisorAdminTestBase):
     max_microversion = '2.52'
 
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 7509ac6..0636ee4 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -29,11 +29,11 @@
 CONF = config.CONF
 
 
-class AttachInterfacesTestJSON(base.BaseV2ComputeTest):
+class AttachInterfacesTestBase(base.BaseV2ComputeTest):
 
     @classmethod
     def skip_checks(cls):
-        super(AttachInterfacesTestJSON, cls).skip_checks()
+        super(AttachInterfacesTestBase, cls).skip_checks()
         if not CONF.service_available.neutron:
             raise cls.skipException("Neutron is required")
         if not CONF.compute_feature_enabled.interface_attach:
@@ -43,14 +43,26 @@
     def setup_credentials(cls):
         # This test class requires network and subnet
         cls.set_network_resources(network=True, subnet=True)
-        super(AttachInterfacesTestJSON, cls).setup_credentials()
+        super(AttachInterfacesTestBase, cls).setup_credentials()
 
     @classmethod
     def setup_clients(cls):
-        super(AttachInterfacesTestJSON, cls).setup_clients()
+        super(AttachInterfacesTestBase, cls).setup_clients()
         cls.subnets_client = cls.os_primary.subnets_client
         cls.ports_client = cls.os_primary.ports_client
 
+    def _create_server_get_interfaces(self):
+        server = self.create_test_server(wait_until='ACTIVE')
+        ifs = (self.interfaces_client.list_interfaces(server['id'])
+               ['interfaceAttachments'])
+        body = waiters.wait_for_interface_status(
+            self.interfaces_client, server['id'], ifs[0]['port_id'], 'ACTIVE')
+        ifs[0]['port_state'] = body['port_state']
+        return server, ifs
+
+
+class AttachInterfacesTestJSON(AttachInterfacesTestBase):
+
     def wait_for_port_detach(self, port_id):
         """Waits for the port's device_id to be unset.
 
@@ -92,15 +104,6 @@
         if mac_addr:
             self.assertEqual(iface['mac_addr'], mac_addr)
 
-    def _create_server_get_interfaces(self):
-        server = self.create_test_server(wait_until='ACTIVE')
-        ifs = (self.interfaces_client.list_interfaces(server['id'])
-               ['interfaceAttachments'])
-        body = waiters.wait_for_interface_status(
-            self.interfaces_client, server['id'], ifs[0]['port_id'], 'ACTIVE')
-        ifs[0]['port_state'] = body['port_state']
-        return server, ifs
-
     def _test_create_interface(self, server):
         iface = (self.interfaces_client.create_interface(server['id'])
                  ['interfaceAttachment'])
@@ -254,30 +257,6 @@
         _ifs = self._test_delete_interface(server, ifs)
         self.assertEqual(len(ifs) - 1, len(_ifs))
 
-    @decorators.attr(type='smoke')
-    @decorators.idempotent_id('c7e0e60b-ee45-43d0-abeb-8596fd42a2f9')
-    @utils.services('network')
-    def test_add_remove_fixed_ip(self):
-        # Add and Remove the fixed IP to server.
-        server, ifs = self._create_server_get_interfaces()
-        interface_count = len(ifs)
-        self.assertGreater(interface_count, 0)
-        network_id = ifs[0]['net_id']
-        self.servers_client.add_fixed_ip(server['id'], networkId=network_id)
-        # Remove the fixed IP from server.
-        server_detail = self.os_primary.servers_client.show_server(
-            server['id'])['server']
-        # Get the Fixed IP from server.
-        fixed_ip = None
-        for ip_set in server_detail['addresses']:
-            for ip in server_detail['addresses'][ip_set]:
-                if ip['OS-EXT-IPS:type'] == 'fixed':
-                    fixed_ip = ip['addr']
-                    break
-            if fixed_ip is not None:
-                break
-        self.servers_client.remove_fixed_ip(server['id'], address=fixed_ip)
-
     @decorators.idempotent_id('2f3a0127-95c7-4977-92d2-bc5aec602fb4')
     def test_reassign_port_between_servers(self):
         """Tests the following:
@@ -314,3 +293,31 @@
             # API so we have to poll the port until the device_id is unset.
             self.interfaces_client.delete_interface(server['id'], port_id)
             self.wait_for_port_detach(port_id)
+
+
+class AttachInterfacesUnderV243Test(AttachInterfacesTestBase):
+    max_microversion = '2.43'
+
+    @decorators.attr(type='smoke')
+    @decorators.idempotent_id('c7e0e60b-ee45-43d0-abeb-8596fd42a2f9')
+    @utils.services('network')
+    def test_add_remove_fixed_ip(self):
+        # Add and Remove the fixed IP to server.
+        server, ifs = self._create_server_get_interfaces()
+        interface_count = len(ifs)
+        self.assertGreater(interface_count, 0)
+        network_id = ifs[0]['net_id']
+        self.servers_client.add_fixed_ip(server['id'], networkId=network_id)
+        # Remove the fixed IP from server.
+        server_detail = self.os_primary.servers_client.show_server(
+            server['id'])['server']
+        # Get the Fixed IP from server.
+        fixed_ip = None
+        for ip_set in server_detail['addresses']:
+            for ip in server_detail['addresses'][ip_set]:
+                if ip['OS-EXT-IPS:type'] == 'fixed':
+                    fixed_ip = ip['addr']
+                    break
+            if fixed_ip is not None:
+                break
+        self.servers_client.remove_fixed_ip(server['id'], address=fixed_ip)
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index a72675e..2c40cb1 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -15,7 +15,7 @@
 import os
 import re
 
-import pep8
+import pycodestyle
 
 
 PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron',
@@ -69,7 +69,7 @@
 
 def no_setup_teardown_class_for_tests(physical_line, filename):
 
-    if pep8.noqa(physical_line):
+    if pycodestyle.noqa(physical_line):
         return
 
     if 'tempest/test.py' in filename or 'tempest/lib/' in filename:
@@ -164,7 +164,7 @@
     if not METHOD.match(physical_line):
         return False
 
-    if pep8.noqa(physical_line):
+    if pycodestyle.noqa(physical_line):
         return False
 
     return True
@@ -287,7 +287,7 @@
     if 'tempest/api/' not in filename:
         return
 
-    if pep8.noqa(physical_line):
+    if pycodestyle.noqa(physical_line):
         return
 
     if not re.match(r'class .*Test.*\(.*Admin.*\):', logical_line):
diff --git a/tempest/lib/api_schema/response/compute/v2_28/__init__.py b/tempest/lib/api_schema/response/compute/v2_28/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_28/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_28/hypervisors.py b/tempest/lib/api_schema/response/compute/v2_28/hypervisors.py
new file mode 100644
index 0000000..8ea9ff8
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_28/hypervisors.py
@@ -0,0 +1,40 @@
+# Copyright 2018 ZTE Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 \
+    import hypervisors as hypervisorsv21
+
+# hypervisor.cpu_info change from string to JSON object.
+hypervisor_detail = copy.deepcopy(hypervisorsv21.hypervisor_detail)
+hypervisor_detail['properties'].update({'cpu_info': {'type': 'object'}})
+
+list_hypervisors_detail = copy.deepcopy(hypervisorsv21.list_hypervisors_detail)
+list_hypervisors_detail['response_body']['properties']['hypervisors'].update(
+    {'items': hypervisor_detail})
+
+get_hypervisor = copy.deepcopy(hypervisorsv21.get_hypervisor)
+get_hypervisor['response_body']['properties'].update(
+    {'hypervisor': hypervisor_detail})
+
+# NOTE(zhufl): 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.
+# ****** Schemas unchanged since microversion 2.1 ***
+get_hypervisor_statistics = \
+    copy.deepcopy(hypervisorsv21.get_hypervisor_statistics)
+list_search_hypervisors = copy.deepcopy(hypervisorsv21.list_search_hypervisors)
+get_hypervisor_uptime = copy.deepcopy(hypervisorsv21.get_hypervisor_uptime)
+get_hypervisors_servers = copy.deepcopy(hypervisorsv21.get_hypervisors_servers)
diff --git a/tempest/lib/api_schema/response/compute/v2_8/__init__.py b/tempest/lib/api_schema/response/compute/v2_8/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_8/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_8/servers.py b/tempest/lib/api_schema/response/compute/v2_8/servers.py
new file mode 100644
index 0000000..df7847f
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_8/servers.py
@@ -0,0 +1,37 @@
+# Copyright 2018 AT&T Corporation.  All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_6 import servers
+
+# 2.8: Add 'mks' protocol and 'webmks' type for remote consoles.
+get_remote_consoles = copy.deepcopy(servers.get_remote_consoles)
+get_remote_consoles['response_body']['properties']['remote_console'][
+    'properties']['protocol']['enum'].append('mks')
+get_remote_consoles['response_body']['properties']['remote_console'][
+    'properties']['type']['enum'].append('webmks')
+
+# NOTE: 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.
+# ****** Schemas unchanged since microversion 2.6 ******
+list_servers = copy.deepcopy(servers.list_servers)
+get_server = copy.deepcopy(servers.get_server)
+list_servers_detail = copy.deepcopy(servers.list_servers_detail)
+update_server = copy.deepcopy(servers.update_server)
+rebuild_server = copy.deepcopy(servers.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers.rebuild_server_with_admin_pass)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
diff --git a/tempest/lib/api_schema/response/compute/v2_9/servers.py b/tempest/lib/api_schema/response/compute/v2_9/servers.py
index f412839..55f8e75 100644
--- a/tempest/lib/api_schema/response/compute/v2_9/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_9/servers.py
@@ -14,16 +14,7 @@
 
 import copy
 
-from tempest.lib.api_schema.response.compute.v2_1 import servers as servers_21
-from tempest.lib.api_schema.response.compute.v2_6 import servers
-
-# NOTE: 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.
-# ****** Schemas unchanged since microversion 2.6 ******
-list_servers = copy.deepcopy(servers.list_servers)
-show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
-get_remote_consoles = copy.deepcopy(servers.get_remote_consoles)
+from tempest.lib.api_schema.response.compute.v2_8 import servers
 
 get_server = copy.deepcopy(servers.get_server)
 get_server['response_body']['properties']['server'][
@@ -37,21 +28,29 @@
 list_servers_detail['response_body']['properties']['servers']['items'][
     'required'].append('locked')
 
-update_server = copy.deepcopy(servers_21.update_server)
+update_server = copy.deepcopy(servers.update_server)
 update_server['response_body']['properties']['server'][
     'properties'].update({'locked': {'type': 'boolean'}})
 update_server['response_body']['properties']['server'][
     'required'].append('locked')
 
-rebuild_server = copy.deepcopy(servers_21.rebuild_server)
+rebuild_server = copy.deepcopy(servers.rebuild_server)
 rebuild_server['response_body']['properties']['server'][
     'properties'].update({'locked': {'type': 'boolean'}})
 rebuild_server['response_body']['properties']['server'][
     'required'].append('locked')
 
 rebuild_server_with_admin_pass = copy.deepcopy(
-    servers_21.rebuild_server_with_admin_pass)
+    servers.rebuild_server_with_admin_pass)
 rebuild_server_with_admin_pass['response_body']['properties']['server'][
     'properties'].update({'locked': {'type': 'boolean'}})
 rebuild_server_with_admin_pass['response_body']['properties']['server'][
     'required'].append('locked')
+
+# NOTE: 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.
+# ****** Schemas unchanged since microversion 2.8 ******
+list_servers = copy.deepcopy(servers.list_servers)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers.get_remote_consoles)
diff --git a/tempest/lib/services/compute/hypervisor_client.py b/tempest/lib/services/compute/hypervisor_client.py
index 23c304e..1cbfcc3 100644
--- a/tempest/lib/services/compute/hypervisor_client.py
+++ b/tempest/lib/services/compute/hypervisor_client.py
@@ -15,16 +15,24 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.lib.api_schema.response.compute.v2_1 import hypervisors as schema
+from tempest.lib.api_schema.response.compute.v2_1 \
+    import hypervisors as schemav21
+from tempest.lib.api_schema.response.compute.v2_28 \
+    import hypervisors as schemav228
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
 
 
 class HypervisorClient(base_compute_client.BaseComputeClient):
 
+    schema_versions_info = [
+        {'min': None, 'max': '2.27', 'schema': schemav21},
+        {'min': '2.28', 'max': None, 'schema': schemav228}]
+
     def list_hypervisors(self, detail=False):
         """List hypervisors information."""
         url = 'os-hypervisors'
+        schema = self.get_schema(self.schema_versions_info)
         _schema = schema.list_search_hypervisors
         if detail:
             url += '/detail'
@@ -39,6 +47,7 @@
         """Display the details of the specified hypervisor."""
         resp, body = self.get('os-hypervisors/%s' % hypervisor_id)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.get_hypervisor, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -46,6 +55,7 @@
         """List instances belonging to the specified hypervisor."""
         resp, body = self.get('os-hypervisors/%s/servers' % hypervisor_name)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.get_hypervisors_servers, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -53,6 +63,7 @@
         """Get hypervisor statistics over all compute nodes."""
         resp, body = self.get('os-hypervisors/statistics')
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.get_hypervisor_statistics, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -60,6 +71,7 @@
         """Display the uptime of the specified hypervisor."""
         resp, body = self.get('os-hypervisors/%s/uptime' % hypervisor_id)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.get_hypervisor_uptime, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -69,5 +81,6 @@
         """Search specified hypervisor."""
         resp, body = self.get('os-hypervisors/%s/search' % hypervisor_name)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.list_search_hypervisors, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 0314356..9eed4b3 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -33,6 +33,7 @@
 from tempest.lib.api_schema.response.compute.v2_57 import servers as schemav257
 from tempest.lib.api_schema.response.compute.v2_6 import servers as schemav26
 from tempest.lib.api_schema.response.compute.v2_63 import servers as schemav263
+from tempest.lib.api_schema.response.compute.v2_8 import servers as schemav28
 from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
@@ -44,7 +45,8 @@
     schema_versions_info = [
         {'min': None, 'max': '2.2', 'schema': schema},
         {'min': '2.3', 'max': '2.5', 'schema': schemav23},
-        {'min': '2.6', 'max': '2.8', 'schema': schemav26},
+        {'min': '2.6', 'max': '2.7', 'schema': schemav26},
+        {'min': '2.8', 'max': '2.8', 'schema': schemav28},
         {'min': '2.9', 'max': '2.15', 'schema': schemav29},
         {'min': '2.16', 'max': '2.18', 'schema': schemav216},
         {'min': '2.19', 'max': '2.25', 'schema': schemav219},
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 79c2d14..810480b 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -11,6 +11,7 @@
 #    under the License.
 
 from oslo_log import log as logging
+from oslo_serialization import jsonutils
 import testtools
 
 from tempest.common import utils
@@ -49,7 +50,8 @@
                                      source_type,
                                      keypair=None,
                                      security_group=None,
-                                     delete_on_termination=False):
+                                     delete_on_termination=False,
+                                     name=None):
         create_kwargs = dict()
         if keypair:
             create_kwargs['key_name'] = keypair['name']
@@ -60,6 +62,8 @@
             source_id,
             source_type,
             delete_on_termination=delete_on_termination))
+        if name:
+            create_kwargs['name'] = name
 
         return self.create_server(image_id='', **create_kwargs)
 
@@ -67,6 +71,10 @@
         self.servers_client.delete_server(server['id'])
         waiters.wait_for_server_termination(self.servers_client, server['id'])
 
+    def _delete_snapshot(self, snapshot_id):
+        self.snapshots_client.delete_snapshot(snapshot_id)
+        self.snapshots_client.wait_for_resource_deletion(snapshot_id)
+
     @decorators.idempotent_id('557cd2c2-4eb8-4dce-98be-f86765ff311b')
     # Note: This test is being skipped based on 'public_network_id'.
     # It is being used in create_floating_ip() method which gets called
@@ -202,28 +210,29 @@
     def test_image_defined_boot_from_volume(self):
         # create an instance from image-backed volume
         volume_origin = self._create_volume_from_image()
-        instance = self._boot_instance_from_resource(
+        name = data_utils.rand_name(self.__class__.__name__ +
+                                    '-volume-backed-server')
+        instance1 = self._boot_instance_from_resource(
             source_id=volume_origin['id'],
             source_type='volume',
-            delete_on_termination=True)
+            delete_on_termination=True,
+            name=name)
         # Create a snapshot image from the volume-backed server.
         # The compute service will have the block service create a snapshot of
         # the root volume and store its metadata in the image.
-        image = self.create_server_snapshot(instance)
-
-        # Delete the first server which will also delete the first image-backed
-        # volume.
-        self._delete_server(instance)
+        image = self.create_server_snapshot(instance1)
 
         # Create a server from the image snapshot which has an
         # "image-defined block device mapping (BDM)" in it, i.e. the metadata
         # about the volume snapshot. The compute service will use this to
         # create a volume from the volume snapshot and use that as the root
         # disk for the server.
-        instance = self.create_server(image_id=image['id'])
+        name = data_utils.rand_name(self.__class__.__name__ +
+                                    '-image-snapshot-server')
+        instance2 = self.create_server(image_id=image['id'], name=name)
 
         # Verify the server was created from the image-defined BDM.
-        volume_attachments = instance['os-extended-volumes:volumes_attached']
+        volume_attachments = instance2['os-extended-volumes:volumes_attached']
         self.assertEqual(1, len(volume_attachments),
                          "No volume attachment found.")
         created_volume = self.volumes_client.show_volume(
@@ -232,7 +241,7 @@
         self.assertEqual(1, len(created_volume['attachments']),
                          "No server attachment found for volume: %s" %
                          created_volume)
-        self.assertEqual(instance['id'],
+        self.assertEqual(instance2['id'],
                          created_volume['attachments'][0]['server_id'])
         self.assertEqual(volume_attachments[0]['id'],
                          created_volume['attachments'][0]['volume_id'])
@@ -242,11 +251,30 @@
 
         # Delete the second server which should also delete the second volume
         # created from the volume snapshot.
-        self._delete_server(instance)
+        self._delete_server(instance2)
 
         # Assert that the underlying volume is gone.
         self.volumes_client.wait_for_resource_deletion(created_volume['id'])
 
+        # Delete the volume snapshot. We must do this before deleting the first
+        # server created in this test because the snapshot depends on the first
+        # instance's underlying volume (volume_origin).
+        # In glance v2, the image properties are flattened and in glance v1,
+        # the image properties are under the 'properties' key.
+        bdms = image.get('block_device_mapping')
+        if not bdms:
+            bdms = image['properties']['block_device_mapping']
+        bdms = jsonutils.loads(bdms)
+        snapshot_id = bdms[0]['snapshot_id']
+        self._delete_snapshot(snapshot_id)
+
+        # Now, delete the first server which will also delete the first
+        # image-backed volume.
+        self._delete_server(instance1)
+
+        # Assert that the underlying volume is gone.
+        self.volumes_client.wait_for_resource_deletion(volume_origin['id'])
+
     @decorators.idempotent_id('cb78919a-e553-4bab-b73b-10cf4d2eb125')
     @testtools.skipUnless(CONF.compute_feature_enabled.attach_encrypted_volume,
                           'Encrypted volume attach is not supported')
diff --git a/test-requirements.txt b/test-requirements.txt
index e33f207..196387c 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,7 +1,7 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
+hacking>=1.1.0,<1.2.0 # Apache-2.0
 mock>=2.0.0 # BSD
 coverage!=4.4,>=4.0 # Apache-2.0
 oslotest>=3.2.0 # Apache-2.0
diff --git a/tox.ini b/tox.ini
index befa991..d61a7fe 100644
--- a/tox.ini
+++ b/tox.ini
@@ -186,7 +186,8 @@
 # E125 is a won't fix until https://github.com/jcrocholl/pep8/issues/126 is resolved.  For further detail see https://review.openstack.org/#/c/36788/
 # E123 skipped because it is ignored by default in the default pep8
 # E129 skipped because it is too limiting when combined with other rules
-ignore = E125,E123,E129
+# W504 skipped because it is overeager and unnecessary
+ignore = E125,E123,E129,W504
 show-source = True
 exclude = .git,.venv,.tox,dist,doc,*egg,build
 enable-extensions = H106,H203,H904