Merge "Create default network for compute volume and negative metadata tests"
diff --git a/.zuul.yaml b/.zuul.yaml
index 70f582e..80d49d8 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -339,6 +339,13 @@
     nodeset: openstack-two-node-bionic
     # This job runs on Bionic from stable/stein on.
     branches: ^(?!stable/(ocata|pike|queens|rocky)).*$
+    vars:
+      devstack_localrc:
+        USE_PYTHON3: False
+    group-vars:
+      subnode:
+        devstack_localrc:
+          USE_PYTHON3: False
 
 - job:
     name: tempest-multinode-full
diff --git a/releasenotes/notes/add-subnet-id-config-option-fac3d6f12abfc171.yaml b/releasenotes/notes/add-subnet-id-config-option-fac3d6f12abfc171.yaml
new file mode 100644
index 0000000..a1bd4c5
--- /dev/null
+++ b/releasenotes/notes/add-subnet-id-config-option-fac3d6f12abfc171.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - A new config option 'subnet_id' is added to section
+    'network' to specify subnet which should be used for
+    allocation of IPs for VMs created during testing.
+    It should be used when the tested network contains more
+    than one subnet otherwise test of external connectivity
+    will fail. (Fixes bug #1856671)
diff --git a/releasenotes/notes/introduce-attachments-client-add-show-attachment-api-c3111f7e560a87b3.yaml b/releasenotes/notes/introduce-attachments-client-add-show-attachment-api-c3111f7e560a87b3.yaml
new file mode 100644
index 0000000..a058137
--- /dev/null
+++ b/releasenotes/notes/introduce-attachments-client-add-show-attachment-api-c3111f7e560a87b3.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    A new attachments client library has been introduced for the volume
+    service.
+
+    Initially only the show_attachment API is provided. This API requires a
+    minimum volume API microversion of ``3.27``.
diff --git a/tempest/clients.py b/tempest/clients.py
index 6aed92e..1db93a0 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -263,6 +263,8 @@
                 self.volume_v3.MessagesClient())
             self.volume_versions_client_latest = (
                 self.volume_v3.VersionsClient())
+            self.attachments_client_latest = (
+                self.volume_v3.AttachmentsClient())
 
             # TODO(gmann): Below alias for service clients have been
             # deprecated and will be removed in future. Start using the alias
diff --git a/tempest/config.py b/tempest/config.py
index 5a2d722..9685745 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -699,6 +699,11 @@
     cfg.StrOpt('floating_network_name',
                help="Default floating network name. Used to allocate floating "
                     "IPs when neutron is enabled."),
+    cfg.StrOpt('subnet_id',
+               default="",
+               help="Subnet id of subnet which is used for allocation of "
+                    "floating IPs. Specify when two or more subnets are "
+                    "present in network."),
     cfg.StrOpt('public_router_id',
                default="",
                help="Id of the public router that provides external "
diff --git a/tempest/lib/services/volume/v3/__init__.py b/tempest/lib/services/volume/v3/__init__.py
index a1b7de3..e2fa836 100644
--- a/tempest/lib/services/volume/v3/__init__.py
+++ b/tempest/lib/services/volume/v3/__init__.py
@@ -11,6 +11,7 @@
 # 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.services.volume.v3.attachments_client import AttachmentsClient
 from tempest.lib.services.volume.v3.availability_zone_client \
     import AvailabilityZoneClient
 from tempest.lib.services.volume.v3.backups_client import BackupsClient
@@ -43,12 +44,11 @@
 from tempest.lib.services.volume.v3.volume_manage_client import \
     VolumeManageClient
 from tempest.lib.services.volume.v3.volumes_client import VolumesClient
-
-__all__ = ['AvailabilityZoneClient', 'BackupsClient', 'BaseClient',
-           'CapabilitiesClient', 'EncryptionTypesClient', 'ExtensionsClient',
-           'GroupSnapshotsClient', 'GroupTypesClient', 'GroupsClient',
-           'HostsClient', 'LimitsClient', 'MessagesClient', 'QosSpecsClient',
-           'QuotaClassesClient', 'QuotasClient', 'SchedulerStatsClient',
-           'ServicesClient', 'SnapshotManageClient', 'SnapshotsClient',
-           'TransfersClient', 'TypesClient', 'VersionsClient',
-           'VolumeManageClient', 'VolumesClient']
+__all__ = ['AttachmentsClient', 'AvailabilityZoneClient', 'BackupsClient',
+           'BaseClient', 'CapabilitiesClient', 'EncryptionTypesClient',
+           'ExtensionsClient', 'GroupSnapshotsClient', 'GroupTypesClient',
+           'GroupsClient', 'HostsClient', 'LimitsClient', 'MessagesClient',
+           'QosSpecsClient', 'QuotaClassesClient', 'QuotasClient',
+           'SchedulerStatsClient', 'ServicesClient', 'SnapshotManageClient',
+           'SnapshotsClient', 'TransfersClient', 'TypesClient',
+           'VersionsClient', 'VolumeManageClient', 'VolumesClient']
diff --git a/tempest/lib/services/volume/v3/attachments_client.py b/tempest/lib/services/volume/v3/attachments_client.py
new file mode 100644
index 0000000..5e448f7
--- /dev/null
+++ b/tempest/lib/services/volume/v3/attachments_client.py
@@ -0,0 +1,28 @@
+#    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 oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+from tempest.lib.services.volume import base_client
+
+
+class AttachmentsClient(base_client.BaseClient):
+    """Client class to send CRUD attachment V3 API requests"""
+
+    def show_attachment(self, attachment_id):
+        """Show volume attachment."""
+        url = "attachments/%s" % (attachment_id)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index cb7acbf..99dd653 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -1008,13 +1008,18 @@
             port_id, ip4 = self._get_server_port_id_and_ip4(thing)
         else:
             ip4 = None
-        result = client.create_floatingip(
-            floating_network_id=external_network_id,
-            port_id=port_id,
-            tenant_id=thing['tenant_id'],
-            fixed_ip_address=ip4
-        )
+
+        kwargs = {
+            'floating_network_id': external_network_id,
+            'port_id': port_id,
+            'tenant_id': thing['tenant_id'],
+            'fixed_ip_address': ip4,
+        }
+        if CONF.network.subnet_id:
+            kwargs['subnet_id'] = CONF.network.subnet_id
+        result = client.create_floatingip(**kwargs)
         floating_ip = result['floatingip']
+
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         client.delete_floatingip,
                         floating_ip['id'])
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index f46c7e8..d8584ec 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -346,10 +346,19 @@
                 network_id=CONF.network.public_network_id)['subnets']
             if s['ip_version'] == 4
         ]
-        self.assertEqual(1, len(v4_subnets),
-                         "Found %d IPv4 subnets" % len(v4_subnets))
 
-        external_ips = [v4_subnets[0]['gateway_ip']]
+        if len(v4_subnets) > 1:
+            self.assertTrue(
+                CONF.network.subnet_id,
+                "Found %d subnets. Specify subnet using configuration "
+                "option [network].subnet_id."
+                % len(v4_subnets))
+            subnet = self.os_admin.subnets_client.show_subnet(
+                CONF.network.subnet_id)['subnet']
+            external_ips = [subnet['gateway_ip']]
+        else:
+            external_ips = [v4_subnets[0]['gateway_ip']]
+
         self._check_server_connectivity(self.floating_ip_tuple.floating_ip,
                                         external_ips)
 
diff --git a/tempest/tests/lib/services/image/v2/test_resource_types_client.py b/tempest/tests/lib/services/image/v2/test_resource_types_client.py
index 741b4eb..74e1c36 100644
--- a/tempest/tests/lib/services/image/v2/test_resource_types_client.py
+++ b/tempest/tests/lib/services/image/v2/test_resource_types_client.py
@@ -67,3 +67,12 @@
 
     def test_list_resource_types_with_bytes_body(self):
         self._test_list_resource_types(bytes_body=True)
+
+    def test_delete_resource_type_association(self):
+        self.check_service_client_function(
+            self.client.delete_resource_type_association,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, status=204,
+            namespace_id="OS::Compute::Hypervisor",
+            resource_name="OS::Glance::Image",
+            )
diff --git a/tempest/tests/lib/services/volume/v3/test_attachments_client.py b/tempest/tests/lib/services/volume/v3/test_attachments_client.py
new file mode 100644
index 0000000..52c94e5
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_attachments_client.py
@@ -0,0 +1,46 @@
+#    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.services.volume.v3 import attachments_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+from oslo_utils.fixture import uuidsentinel as uuids
+
+
+class TestAttachmentsClient(base.BaseServiceTest):
+
+    FAKE_ATTACHMENT_INFO = {
+        "attachment": {
+            "status": "attaching",
+            "detached_at": "2015-09-16T09:28:52.000000",
+            "connection_info": {},
+            "attached_at": "2015-09-16T09:28:52.000000",
+            "attach_mode": "ro",
+            "instance": uuids.instance_id,
+            "volume_id": uuids.volume_id,
+            "id": uuids.id,
+        }
+    }
+
+    def setUp(self):
+        super(TestAttachmentsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = attachments_client.AttachmentsClient(fake_auth,
+                                                           'volume',
+                                                           'regionOne')
+
+    def test_show_attachment(self):
+        self.check_service_client_function(
+            self.client.show_attachment,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_ATTACHMENT_INFO, attachment_id=uuids.id)