Merge "Remove some deprecated identity options"
diff --git a/.zuul.yaml b/.zuul.yaml
index 0d2005b..462501e 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -613,8 +613,6 @@
             irrelevant-files: *tempest-irrelevant-files
         - neutron-tempest-dvr-ha-multinode-full:
             irrelevant-files: *tempest-irrelevant-files
-        - nova-cells-v1:
-            irrelevant-files: *tempest-irrelevant-files
         - nova-tempest-v2-api:
             irrelevant-files: *tempest-irrelevant-files
         - legacy-tempest-dsvm-lvm-multibackend:
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 4b1c145..b4f06e3 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -406,6 +406,14 @@
 
   .. _2.63: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id57
 
+  * `2.70`_
+
+  .. _2.70: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id63
+
+  * `2.71`_
+
+  .. _2.71: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id64
+
 * Volume
 
   * `3.3`_
diff --git a/releasenotes/notes/lib_api_microversion_fixture-f52308fc6b6b89f2.yaml b/releasenotes/notes/lib_api_microversion_fixture-f52308fc6b6b89f2.yaml
new file mode 100644
index 0000000..d707fc7
--- /dev/null
+++ b/releasenotes/notes/lib_api_microversion_fixture-f52308fc6b6b89f2.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    New library interface to set the API microversion on Service Clients.
+    ``APIMicroversionFixture,`` can be used to set the API microversion
+    on multiple services. This Fixture will take care of reseting the service
+    microversion to None once test is finished.
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 624a99e..e71e642 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -17,11 +17,11 @@
 
 from oslo_log import log as logging
 
-from tempest.api.compute import api_microversion_fixture
 from tempest.common import compute
 from tempest.common import waiters
 from tempest import config
 from tempest import exceptions
+from tempest.lib.common import api_microversion_fixture
 from tempest.lib.common import api_version_request
 from tempest.lib.common import api_version_utils
 from tempest.lib.common.utils import data_utils
@@ -470,7 +470,7 @@
     def setUp(self):
         super(BaseV2ComputeTest, self).setUp()
         self.useFixture(api_microversion_fixture.APIMicroversionFixture(
-            self.request_microversion))
+            compute_microversion=self.request_microversion))
 
     @classmethod
     def create_volume(cls, image_ref=None, **kwargs):
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 49d9742..d76a323 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -156,3 +156,53 @@
             cmd_why = 'sudo ls -lR /dev'
             LOG.info("Contents of /dev: %s", self.exec_command(cmd_why))
             raise
+
+    def nc_listen_host(self, port=80, protocol='tcp'):
+        """Creates persistent nc server listening on the given TCP / UDP port
+
+        :port: the port to start listening on.
+        :protocol: the protocol used by the server. TCP by default.
+        """
+        udp = '-u' if protocol.lower() == 'udp' else ''
+        cmd = "sudo nc %(udp)s -p %(port)s -lk -e echo foolish &" % {
+            'udp': udp, 'port': port}
+        return self.exec_command(cmd)
+
+    def nc_host(self, host, port=80, protocol='tcp', expected_response=None):
+        """Check connectivity to TCP / UDP port at host via nc
+
+        :host: an IP against which the connectivity will be tested.
+        :port: the port to check connectivity against.
+        :protocol: the protocol used by nc to send packets. TCP by default.
+        :expected_response: string representing the expected response
+            from server.
+        :raises SSHExecCommandFailed: if an expected response is given and it
+            does not match the actual server response.
+        """
+        udp = '-u' if protocol.lower() == 'udp' else ''
+        cmd = 'echo "bar" | nc -w 1 %(udp)s %(host)s %(port)s' % {
+            'udp': udp, 'host': host, 'port': port}
+        response = self.exec_command(cmd)
+
+        # sending an UDP packet will always succeed. we need to check
+        # the response.
+        if (expected_response is not None and
+                expected_response != response.strip()):
+            raise tempest.lib.exceptions.SSHExecCommandFailed(
+                command=cmd, exit_status=0, stdout=response, stderr='')
+        return response
+
+    def icmp_check(self, host, nic=None):
+        """Wrapper for icmp connectivity checks"""
+        return self.ping_host(host, nic=nic)
+
+    def udp_check(self, host, **kwargs):
+        """Wrapper for udp connectivity checks."""
+        kwargs.pop('nic', None)
+        return self.nc_host(host, protocol='udp', expected_response='foolish',
+                            **kwargs)
+
+    def tcp_check(self, host, **kwargs):
+        """Wrapper for tcp connectivity checks."""
+        kwargs.pop('nic', None)
+        return self.nc_host(host, **kwargs)
diff --git a/tempest/config.py b/tempest/config.py
index 8f8979c..6830148 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -1045,7 +1045,12 @@
                choices=["udhcpc", "dhclient", ""],
                help='DHCP client used by images to renew DCHP lease. '
                     'If left empty, update operation will be skipped. '
-                    'Supported clients: "udhcpc", "dhclient"')
+                    'Supported clients: "udhcpc", "dhclient"'),
+    cfg.StrOpt('protocol',
+               default='icmp',
+               choices=('icmp', 'tcp', 'udp'),
+               help='The protocol used in security groups tests to check '
+                    'connectivity.'),
 ]
 
 
diff --git a/tempest/lib/api_schema/response/compute/v2_16/servers.py b/tempest/lib/api_schema/response/compute/v2_16/servers.py
index 72b84f5..fc81ff7 100644
--- a/tempest/lib/api_schema/response/compute/v2_16/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_16/servers.py
@@ -168,3 +168,6 @@
     servers.rebuild_server_with_admin_pass)
 show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
 get_remote_consoles = copy.deepcopy(servers.get_remote_consoles)
+attach_volume = copy.deepcopy(servers.attach_volume)
+show_volume_attachment = copy.deepcopy(servers.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
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 e3e8ad1..b6c3c14 100644
--- a/tempest/lib/api_schema/response/compute/v2_19/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_19/servers.py
@@ -58,3 +58,6 @@
 list_servers = copy.deepcopy(serversv216.list_servers)
 show_server_diagnostics = copy.deepcopy(serversv216.show_server_diagnostics)
 get_remote_consoles = copy.deepcopy(serversv216.get_remote_consoles)
+attach_volume = copy.deepcopy(serversv216.attach_volume)
+show_volume_attachment = copy.deepcopy(serversv216.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(serversv216.list_volume_attachments)
diff --git a/tempest/lib/api_schema/response/compute/v2_26/servers.py b/tempest/lib/api_schema/response/compute/v2_26/servers.py
index 8e62dc3..5a0f987 100644
--- a/tempest/lib/api_schema/response/compute/v2_26/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_26/servers.py
@@ -101,3 +101,6 @@
 list_servers = copy.deepcopy(servers219.list_servers)
 show_server_diagnostics = copy.deepcopy(servers219.show_server_diagnostics)
 get_remote_consoles = copy.deepcopy(servers219.get_remote_consoles)
+attach_volume = copy.deepcopy(servers219.attach_volume)
+show_volume_attachment = copy.deepcopy(servers219.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers219.list_volume_attachments)
diff --git a/tempest/lib/api_schema/response/compute/v2_3/servers.py b/tempest/lib/api_schema/response/compute/v2_3/servers.py
index 18fb352..1674c1b 100644
--- a/tempest/lib/api_schema/response/compute/v2_3/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_3/servers.py
@@ -173,3 +173,6 @@
 rebuild_server_with_admin_pass = copy.deepcopy(
     servers.rebuild_server_with_admin_pass)
 show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
+attach_volume = copy.deepcopy(servers.attach_volume)
+show_volume_attachment = copy.deepcopy(servers.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
diff --git a/tempest/lib/api_schema/response/compute/v2_47/servers.py b/tempest/lib/api_schema/response/compute/v2_47/servers.py
index 0fbacd3..d580f2c 100644
--- a/tempest/lib/api_schema/response/compute/v2_47/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_47/servers.py
@@ -66,3 +66,6 @@
 update_tag = copy.deepcopy(servers226.update_tag)
 delete_tag = copy.deepcopy(servers226.delete_tag)
 list_servers = copy.deepcopy(servers226.list_servers)
+attach_volume = copy.deepcopy(servers226.attach_volume)
+show_volume_attachment = copy.deepcopy(servers226.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers226.list_volume_attachments)
diff --git a/tempest/lib/api_schema/response/compute/v2_48/servers.py b/tempest/lib/api_schema/response/compute/v2_48/servers.py
index 84b5a2a..e2e45bc 100644
--- a/tempest/lib/api_schema/response/compute/v2_48/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_48/servers.py
@@ -129,3 +129,6 @@
 rebuild_server = copy.deepcopy(servers247.rebuild_server)
 rebuild_server_with_admin_pass = copy.deepcopy(
     servers247.rebuild_server_with_admin_pass)
+attach_volume = copy.deepcopy(servers247.attach_volume)
+show_volume_attachment = copy.deepcopy(servers247.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers247.list_volume_attachments)
diff --git a/tempest/lib/api_schema/response/compute/v2_54/servers.py b/tempest/lib/api_schema/response/compute/v2_54/servers.py
index 099e1b8..2c2bff0 100644
--- a/tempest/lib/api_schema/response/compute/v2_54/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_54/servers.py
@@ -55,3 +55,6 @@
 check_tag_existence = copy.deepcopy(servers248.check_tag_existence)
 update_tag = copy.deepcopy(servers248.update_tag)
 delete_tag = copy.deepcopy(servers248.delete_tag)
+attach_volume = copy.deepcopy(servers248.attach_volume)
+show_volume_attachment = copy.deepcopy(servers248.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers248.list_volume_attachments)
diff --git a/tempest/lib/api_schema/response/compute/v2_57/servers.py b/tempest/lib/api_schema/response/compute/v2_57/servers.py
index 0099a2b..aa57d25 100644
--- a/tempest/lib/api_schema/response/compute/v2_57/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_57/servers.py
@@ -59,3 +59,6 @@
 check_tag_existence = copy.deepcopy(servers254.check_tag_existence)
 update_tag = copy.deepcopy(servers254.update_tag)
 delete_tag = copy.deepcopy(servers254.delete_tag)
+attach_volume = copy.deepcopy(servers254.attach_volume)
+show_volume_attachment = copy.deepcopy(servers254.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers254.list_volume_attachments)
diff --git a/tempest/lib/api_schema/response/compute/v2_6/servers.py b/tempest/lib/api_schema/response/compute/v2_6/servers.py
index d5774de..922bf79 100644
--- a/tempest/lib/api_schema/response/compute/v2_6/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_6/servers.py
@@ -28,6 +28,9 @@
 rebuild_server_with_admin_pass = copy.deepcopy(
     servers.rebuild_server_with_admin_pass)
 show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
+attach_volume = copy.deepcopy(servers.attach_volume)
+show_volume_attachment = copy.deepcopy(servers.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
 
 # NOTE: The consolidated remote console API got introduced with v2.6
 # with bp/consolidate-console-api. See Nova commit 578bafeda
diff --git a/tempest/lib/api_schema/response/compute/v2_63/servers.py b/tempest/lib/api_schema/response/compute/v2_63/servers.py
index 3c3d41c..01910aa 100644
--- a/tempest/lib/api_schema/response/compute/v2_63/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_63/servers.py
@@ -73,3 +73,6 @@
 check_tag_existence = copy.deepcopy(servers257.check_tag_existence)
 update_tag = copy.deepcopy(servers257.update_tag)
 delete_tag = copy.deepcopy(servers257.delete_tag)
+attach_volume = copy.deepcopy(servers257.attach_volume)
+show_volume_attachment = copy.deepcopy(servers257.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers257.list_volume_attachments)
diff --git a/tempest/lib/api_schema/response/compute/v2_70/__init__.py b/tempest/lib/api_schema/response/compute/v2_70/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_70/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_70/servers.py b/tempest/lib/api_schema/response/compute/v2_70/servers.py
new file mode 100644
index 0000000..5ca4cc8
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_70/servers.py
@@ -0,0 +1,80 @@
+#    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 servers as servers2_1
+from tempest.lib.api_schema.response.compute.v2_63 import servers as servers263
+
+
+###########################################################################
+#
+# 2.70:
+#
+# Exposes virtual device tags for volume attachments and virtual interfaces
+# (ports). A tag parameter is added to the response body for the following
+# APIs:
+#
+# Volumes
+#
+# - GET /servers/{server_id}/os-volume_attachments (list)
+# - GET /servers/{server_id}/os-volume_attachments/{volume_id} (show)
+# - POST /servers/{server_id}/os-volume_attachments (attach)
+#
+# Ports
+#
+# - GET /servers/{server_id}/os-interface (list)
+# - GET /servers/{server_id}/os-interface/{port_id} (show)
+# - POST /servers/{server_id}/os-interface (attach)
+#
+###########################################################################
+
+attach_volume = copy.deepcopy(servers2_1.attach_volume)
+attach_volume['response_body']['properties']['volumeAttachment'][
+    'properties'].update({'tag': {'type': ['string', 'null']}})
+attach_volume['response_body']['properties']['volumeAttachment'][
+    'required'].append('tag')
+
+show_volume_attachment = copy.deepcopy(servers2_1.show_volume_attachment)
+show_volume_attachment['response_body']['properties']['volumeAttachment'][
+    'properties'].update({'tag': {'type': ['string', 'null']}})
+show_volume_attachment['response_body']['properties'][
+    'volumeAttachment']['required'].append('tag')
+
+list_volume_attachments = copy.deepcopy(servers2_1.list_volume_attachments)
+list_volume_attachments['response_body']['properties']['volumeAttachments'][
+    'items']['properties'].update({'tag': {'type': ['string', 'null']}})
+list_volume_attachments['response_body']['properties'][
+    'volumeAttachments']['items']['required'].append('tag')
+
+# TODO(mriedem): Handle the os-interface changes when there is a test that
+# needs them from this microversion onward.
+
+# NOTE(lajoskatona): 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.63 ***
+list_servers_detail = copy.deepcopy(servers263.list_servers_detail)
+rebuild_server = copy.deepcopy(servers263.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers263.rebuild_server_with_admin_pass)
+update_server = copy.deepcopy(servers263.update_server)
+get_server = copy.deepcopy(servers263.get_server)
+list_servers = copy.deepcopy(servers263.list_servers)
+show_server_diagnostics = copy.deepcopy(servers263.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers263.get_remote_consoles)
+list_tags = copy.deepcopy(servers263.list_tags)
+update_all_tags = copy.deepcopy(servers263.update_all_tags)
+delete_all_tags = copy.deepcopy(servers263.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers263.check_tag_existence)
+update_tag = copy.deepcopy(servers263.update_tag)
+delete_tag = copy.deepcopy(servers263.delete_tag)
diff --git a/tempest/lib/api_schema/response/compute/v2_71/__init__.py b/tempest/lib/api_schema/response/compute/v2_71/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_71/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_71/servers.py b/tempest/lib/api_schema/response/compute/v2_71/servers.py
new file mode 100644
index 0000000..0c526fb
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_71/servers.py
@@ -0,0 +1,81 @@
+#    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_70 import servers as servers270
+
+
+###########################################################################
+#
+# 2.71:
+#
+# The server_groups parameter will be in the response body of the following
+# APIs to list the server groups to which the server belongs:
+#
+# - GET /servers/{server_id} (show)
+# - PUT /servers/{server_id} (update)
+# - POST /servers/{server_id}/action (rebuild)
+#
+###########################################################################
+
+# The "server_groups" parameter will always be present and contain at most one
+# UUID entry.
+server_groups = {
+    'type': 'array',
+    'minItems': 0,
+    'maxItems': 1,
+    'items': {
+        'type': 'string',
+        'format': 'uuid'
+    }
+}
+
+rebuild_server = copy.deepcopy(servers270.rebuild_server)
+rebuild_server['response_body']['properties']['server'][
+    'properties'].update({'server_groups': server_groups})
+rebuild_server['response_body']['properties']['server'][
+    'required'].append('server_groups')
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers270.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'properties'].update({'server_groups': server_groups})
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'required'].append('server_groups')
+
+update_server = copy.deepcopy(servers270.update_server)
+update_server['response_body']['properties']['server'][
+    'properties'].update({'server_groups': server_groups})
+update_server['response_body']['properties']['server'][
+    'required'].append('server_groups')
+
+get_server = copy.deepcopy(servers270.get_server)
+get_server['response_body']['properties']['server'][
+    'properties'].update({'server_groups': server_groups})
+get_server['response_body']['properties']['server'][
+    'required'].append('server_groups')
+
+# NOTE(lajoskatona): 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.70 ***
+list_servers_details = copy.deepcopy(servers270.list_servers_detail)
+list_servers = copy.deepcopy(servers270.list_servers)
+show_server_diagnostics = copy.deepcopy(servers270.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers270.get_remote_consoles)
+list_tags = copy.deepcopy(servers270.list_tags)
+update_all_tags = copy.deepcopy(servers270.update_all_tags)
+delete_all_tags = copy.deepcopy(servers270.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers270.check_tag_existence)
+update_tag = copy.deepcopy(servers270.update_tag)
+delete_tag = copy.deepcopy(servers270.delete_tag)
diff --git a/tempest/lib/api_schema/response/compute/v2_8/servers.py b/tempest/lib/api_schema/response/compute/v2_8/servers.py
index df7847f..3dbab3f 100644
--- a/tempest/lib/api_schema/response/compute/v2_8/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_8/servers.py
@@ -35,3 +35,6 @@
 rebuild_server_with_admin_pass = copy.deepcopy(
     servers.rebuild_server_with_admin_pass)
 show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
+attach_volume = copy.deepcopy(servers.attach_volume)
+show_volume_attachment = copy.deepcopy(servers.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
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 55f8e75..ee0313d 100644
--- a/tempest/lib/api_schema/response/compute/v2_9/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_9/servers.py
@@ -54,3 +54,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)
+attach_volume = copy.deepcopy(servers.attach_volume)
+show_volume_attachment = copy.deepcopy(servers.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
diff --git a/tempest/lib/common/api_microversion_fixture.py b/tempest/lib/common/api_microversion_fixture.py
new file mode 100644
index 0000000..3837138
--- /dev/null
+++ b/tempest/lib/common/api_microversion_fixture.py
@@ -0,0 +1,82 @@
+# Copyright 2019 NEC 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 fixtures
+
+from tempest.lib.services.compute import base_compute_client
+from tempest.lib.services.placement import base_placement_client
+from tempest.lib.services.volume import base_client as base_volume_client
+
+
+class APIMicroversionFixture(fixtures.Fixture):
+    """API Microversion Fixture to set service microversion.
+
+    This class provides the fixture to set and reset the microversion
+    on service client. Service client has global variable to set the
+    microversion for that service API request.
+    For example: base_compute_client.COMPUTE_MICROVERSION
+    Global variable is always risky to set directly which can affect the
+    other test's API request also. This class provides a way to reset the
+    service microversion once test finish the API request.
+    This class can be used with useFixture: Example::
+
+        def setUp(self):
+            super(BaseV2ComputeTest, self).setUp()
+            self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+                compute_microversion=self.compute_request_microversion))
+
+    Or you can set microversion on multiple services together::
+
+        def setUp(self):
+            super(ScenarioTest, self).setUp()
+            self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+                compute_microversion=self.compute_request_microversion,
+                volume_microversion=self.volume_request_microversion))
+
+    Current supported services:
+    - Compute
+    - Volume
+    - Placement
+
+    :param str compute_microversion: microvesion to be set on compute
+                                     service clients
+    :param str volume_microversion: microvesion to be set on volume
+                                    service clients
+    :param str placement_microversion: microvesion to be set on placement
+                                       service clients
+    """
+
+    def __init__(self, compute_microversion=None, volume_microversion=None,
+                 placement_microversion=None):
+        self.compute_microversion = compute_microversion
+        self.volume_microversion = volume_microversion
+        self.placement_microversion = placement_microversion
+
+    def _setUp(self):
+        super(APIMicroversionFixture, self)._setUp()
+        if self.compute_microversion:
+            base_compute_client.COMPUTE_MICROVERSION = (
+                self.compute_microversion)
+        if self.volume_microversion:
+            base_volume_client.VOLUME_MICROVERSION = self.volume_microversion
+        if self.placement_microversion:
+            base_placement_client.PLACEMENT_MICROVERSION = (
+                self.placement_microversion)
+
+        self.addCleanup(self._reset_microversion)
+
+    def _reset_microversion(self):
+        base_compute_client.COMPUTE_MICROVERSION = None
+        base_volume_client.VOLUME_MICROVERSION = None
+        base_placement_client.PLACEMENT_MICROVERSION = None
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 18e08cc..f2270f8 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -33,6 +33,8 @@
 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_70 import servers as schemav270
+from tempest.lib.api_schema.response.compute.v2_71 import servers as schemav271
 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
@@ -55,7 +57,9 @@
         {'min': '2.48', 'max': '2.53', 'schema': schemav248},
         {'min': '2.54', 'max': '2.56', 'schema': schemav254},
         {'min': '2.57', 'max': '2.62', 'schema': schemav257},
-        {'min': '2.63', 'max': None, 'schema': schemav263}]
+        {'min': '2.63', 'max': '2.69', 'schema': schemav263},
+        {'min': '2.70', 'max': '2.70', 'schema': schemav270},
+        {'min': '2.71', 'max': None, 'schema': schemav271}]
 
     def __init__(self, auth_provider, service, region,
                  enable_instance_password=True, **kwargs):
@@ -426,6 +430,7 @@
         resp, body = self.post('servers/%s/os-volume_attachments' % server_id,
                                post_body)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.attach_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -460,6 +465,7 @@
         resp, body = self.get('servers/%s/os-volume_attachments/%s' % (
             server_id, volume_id))
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.show_volume_attachment, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -473,6 +479,7 @@
         resp, body = self.get('servers/%s/os-volume_attachments' % (
             server_id))
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.list_volume_attachments, resp, body)
         return rest_client.ResponseBody(resp, body)
 
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 86d37f1..aa06fd0 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -979,24 +979,33 @@
             raise
 
     def check_remote_connectivity(self, source, dest, should_succeed=True,
-                                  nic=None):
-        """assert ping server via source ssh connection
+                                  nic=None, protocol='icmp'):
+        """check server connectivity via source ssh connection
 
-        :param source: RemoteClient: an ssh connection from which to ping
-        :param dest: an IP to ping against
-        :param should_succeed: boolean: should ping succeed or not
-        :param nic: specific network interface to ping from
+        :param source: RemoteClient: an ssh connection from which to execute
+            the check
+        :param dest: an IP to check connectivity against
+        :param should_succeed: boolean should connection succeed or not
+        :param nic: specific network interface to test connectivity from
+        :param protocol: the protocol used to test connectivity with.
+        :returns: True, if the connection succeeded and it was expected to
+            succeed. False otherwise.
         """
-        def ping_remote():
+        method_name = '%s_check' % protocol
+        connectivity_checker = getattr(source, method_name)
+
+        def connect_remote():
             try:
-                source.ping_host(dest, nic=nic)
+                connectivity_checker(dest, nic=nic)
             except lib_exc.SSHExecCommandFailed:
-                LOG.warning('Failed to ping IP: %s via a ssh connection '
-                            'from: %s.', dest, source.ssh_client.host)
+                LOG.warning('Failed to check %(protocol)s connectivity for '
+                            'IP %(dest)s via a ssh connection from: %(src)s.',
+                            dict(protocol=protocol, dest=dest,
+                                 src=source.ssh_client.host))
                 return not should_succeed
             return should_succeed
 
-        result = test_utils.call_until_true(ping_remote,
+        result = test_utils.call_until_true(connect_remote,
                                             CONF.validation.ping_timeout, 1)
         if result:
             return
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index 438ee01..8de6614 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -166,9 +166,10 @@
         if self._sysconfig_network_scripts_dir_exists(ssh):
             try:
                 ssh.exec_command(
-                    'echo -e "DEVICE=%(nic)s\\nIPV6INIT=yes" | '
+                    'echo -e "DEVICE=%(nic)s\\nNAME=%(nic)s\\nIPV6INIT=yes" | '
                     'sudo tee /etc/sysconfig/network-scripts/ifcfg-%(nic)s; '
-                    'sudo /sbin/service network restart' % {'nic': nic})
+                    'sudo nmcli connection reload' % {'nic': nic})
+                ssh.exec_command('sudo nmcli connection up %s' % nic)
             except exceptions.SSHExecCommandFailed as e:
                 # NOTE(slaweq): Sometimes it can happen that this SSH command
                 # will fail because of some error from network manager in
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 2b7926a..9cbd831 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -395,24 +395,22 @@
             self.check_remote_connectivity(source=access_point_ssh,
                                            dest=self._get_server_ip(server))
 
-    def _test_cross_tenant_block(self, source_tenant, dest_tenant):
+    def _test_cross_tenant_block(self, source_tenant, dest_tenant, ruleset):
         # if public router isn't defined, then dest_tenant access is via
         # floating-ip
+        protocol = ruleset['protocol']
         access_point_ssh = self._connect_to_access_point(source_tenant)
         ip = self._get_server_ip(dest_tenant.access_point,
                                  floating=self.floating_ip_access)
         self.check_remote_connectivity(source=access_point_ssh, dest=ip,
-                                       should_succeed=False)
+                                       should_succeed=False, protocol=protocol)
 
-    def _test_cross_tenant_allow(self, source_tenant, dest_tenant):
+    def _test_cross_tenant_allow(self, source_tenant, dest_tenant, ruleset):
         """check for each direction:
 
         creating rule for tenant incoming traffic enables only 1way traffic
         """
-        ruleset = dict(
-            protocol='icmp',
-            direction='ingress'
-        )
+        protocol = ruleset['protocol']
         sec_group_rules_client = (
             dest_tenant.manager.security_group_rules_client)
         self._create_security_group_rule(
@@ -423,10 +421,10 @@
         access_point_ssh = self._connect_to_access_point(source_tenant)
         ip = self._get_server_ip(dest_tenant.access_point,
                                  floating=self.floating_ip_access)
-        self.check_remote_connectivity(access_point_ssh, ip)
+        self.check_remote_connectivity(access_point_ssh, ip, protocol=protocol)
 
         # test that reverse traffic is still blocked
-        self._test_cross_tenant_block(dest_tenant, source_tenant)
+        self._test_cross_tenant_block(dest_tenant, source_tenant, ruleset)
 
         # allow reverse traffic and check
         sec_group_rules_client = (
@@ -440,7 +438,8 @@
         access_point_ssh_2 = self._connect_to_access_point(dest_tenant)
         ip = self._get_server_ip(source_tenant.access_point,
                                  floating=self.floating_ip_access)
-        self.check_remote_connectivity(access_point_ssh_2, ip)
+        self.check_remote_connectivity(access_point_ssh_2, ip,
+                                       protocol=protocol)
 
     def _verify_mac_addr(self, tenant):
         """Verify that VM has the same ip, mac as listed in port"""
@@ -470,6 +469,17 @@
                 self._log_console_output(
                     servers=[tenant.access_point], client=client)
 
+    def _create_protocol_ruleset(self, protocol, port=80):
+        if protocol == 'icmp':
+            ruleset = dict(protocol='icmp',
+                           direction='ingress')
+        else:
+            ruleset = dict(protocol=protocol,
+                           port_range_min=port,
+                           port_range_max=port,
+                           direction='ingress')
+        return ruleset
+
     @decorators.idempotent_id('e79f879e-debb-440c-a7e4-efeda05b6848')
     @utils.services('compute', 'network')
     def test_cross_tenant_traffic(self):
@@ -484,8 +494,18 @@
             # cross tenant check
             source_tenant = self.primary_tenant
             dest_tenant = self.alt_tenant
-            self._test_cross_tenant_block(source_tenant, dest_tenant)
-            self._test_cross_tenant_allow(source_tenant, dest_tenant)
+
+            protocol = CONF.scenario.protocol
+            LOG.debug("Testing cross tenant traffic for %s protocol",
+                      protocol)
+            if protocol in ['udp', 'tcp']:
+                for tenant in [source_tenant, dest_tenant]:
+                    access_point = self._connect_to_access_point(tenant)
+                    access_point.nc_listen_host(protocol=protocol)
+
+            ruleset = self._create_protocol_ruleset(protocol)
+            self._test_cross_tenant_block(source_tenant, dest_tenant, ruleset)
+            self._test_cross_tenant_allow(source_tenant, dest_tenant, ruleset)
         except Exception:
             self._log_console_output_for_all_tenants()
             raise
diff --git a/tempest/tests/lib/test_api_microversion_fixture.py b/tempest/tests/lib/test_api_microversion_fixture.py
new file mode 100644
index 0000000..ad98ed0
--- /dev/null
+++ b/tempest/tests/lib/test_api_microversion_fixture.py
@@ -0,0 +1,58 @@
+# Copyright 2019 NEC Corporation.
+#
+#    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.
+
+from tempest.lib.common import api_microversion_fixture
+from tempest.lib.services.compute import base_compute_client
+from tempest.lib.services.placement import base_placement_client
+from tempest.lib.services.volume import base_client
+from tempest.tests import base
+
+
+class TestAPIMicroversionFixture(base.TestCase):
+    def setUp(self):
+        super(TestAPIMicroversionFixture, self).setUp()
+        # Verify that all the microversion are reset back to None
+        # by Fixture.
+        self.assertIsNone(base_compute_client.COMPUTE_MICROVERSION)
+        self.assertIsNone(base_client.VOLUME_MICROVERSION)
+        self.assertIsNone(base_placement_client.PLACEMENT_MICROVERSION)
+
+    def test_compute_microversion(self):
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            compute_microversion='2.10'))
+        self.assertEqual('2.10', base_compute_client.COMPUTE_MICROVERSION)
+        self.assertIsNone(base_client.VOLUME_MICROVERSION)
+        self.assertIsNone(base_placement_client.PLACEMENT_MICROVERSION)
+
+    def test_volume_microversion(self):
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            volume_microversion='3.10'))
+        self.assertIsNone(base_compute_client.COMPUTE_MICROVERSION)
+        self.assertEqual('3.10', base_client.VOLUME_MICROVERSION)
+        self.assertIsNone(base_placement_client.PLACEMENT_MICROVERSION)
+
+    def test_placement_microversion(self):
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            placement_microversion='1.10'))
+        self.assertIsNone(base_compute_client.COMPUTE_MICROVERSION)
+        self.assertIsNone(base_client.VOLUME_MICROVERSION)
+        self.assertEqual('1.10', base_placement_client.PLACEMENT_MICROVERSION)
+
+    def test_multiple_service_microversion(self):
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            compute_microversion='2.10', volume_microversion='3.10',
+            placement_microversion='1.10'))
+        self.assertEqual('2.10', base_compute_client.COMPUTE_MICROVERSION)
+        self.assertEqual('3.10', base_client.VOLUME_MICROVERSION)
+        self.assertEqual('1.10', base_placement_client.PLACEMENT_MICROVERSION)