Merge "Fixed wrong link in microversion_testing.rst"
diff --git a/releasenotes/notes/add-implied-roles-to-roles-client-library-edf96408ad9ba82e.yaml b/releasenotes/notes/add-implied-roles-to-roles-client-library-edf96408ad9ba82e.yaml
new file mode 100644
index 0000000..9116ef8
--- /dev/null
+++ b/releasenotes/notes/add-implied-roles-to-roles-client-library-edf96408ad9ba82e.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add the implied roles feature API to the roles_client library. This
+    feature enables the possibility to create inferences rules between
+    roles (a role being implied by another role).
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 85c7fba..9e1c0e9 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -18,6 +18,7 @@
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest.lib import decorators
+from tempest import test
 
 
 class ServersAdminTestJSON(base.BaseV2ComputeAdminTest):
@@ -91,6 +92,7 @@
         self.assertIn(self.s1_name, servers_name)
         self.assertIn(self.s2_name, servers_name)
 
+    @test.related_bug('1659811')
     @decorators.idempotent_id('7e5d6b8f-454a-4ba1-8ae2-da857af8338b')
     def test_list_servers_by_admin_with_specified_tenant(self):
         # In nova v2, tenant_id is ignored unless all_tenants is specified
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 6e6122b..7938604 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -90,8 +90,11 @@
         vendor = CONF.volume.vendor_name
         extra_specs = {"storage_protocol": proto,
                        "vendor_name": vendor}
-        body = self.create_volume_type(description=description, name=name,
-                                       extra_specs=extra_specs)
+        params = {'name': name,
+                  'description': description,
+                  'extra_specs': extra_specs,
+                  'os-volume-type-access:is_public': True}
+        body = self.create_volume_type(**params)
         self.assertIn('name', body)
         self.assertEqual(name, body['name'],
                          "The created volume_type name is not equal "
@@ -112,6 +115,12 @@
         self.assertEqual(extra_specs, fetched_volume_type['extra_specs'],
                          'The fetched Volume_type is different '
                          'from the created Volume_type')
+        self.assertEqual(description, fetched_volume_type['description'])
+        self.assertEqual(body['is_public'],
+                         fetched_volume_type['is_public'])
+        self.assertEqual(
+            body['os-volume-type-access:is_public'],
+            fetched_volume_type['os-volume-type-access:is_public'])
 
     @decorators.idempotent_id('7830abd0-ff99-4793-a265-405684a54d46')
     def test_volume_type_encryption_create_get_delete(self):
diff --git a/tempest/lib/services/identity/v3/roles_client.py b/tempest/lib/services/identity/v3/roles_client.py
index f1339dd..0df23ce 100644
--- a/tempest/lib/services/identity/v3/roles_client.py
+++ b/tempest/lib/services/identity/v3/roles_client.py
@@ -190,3 +190,40 @@
                                (domain_id, group_id, role_id))
         self.expected_success(204, resp.status)
         return rest_client.ResponseBody(resp)
+
+    def create_role_inference_rule(self, prior_role, implies_role):
+        """Create a role inference rule."""
+        resp, body = self.put('roles/%s/implies/%s' %
+                              (prior_role, implies_role), None)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_role_inference_rule(self, prior_role, implies_role):
+        """Get a role inference rule."""
+        resp, body = self.get('roles/%s/implies/%s' %
+                              (prior_role, implies_role))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_role_inferences_rules(self, prior_role):
+        """List the inferences rules from a role."""
+        resp, body = self.get('roles/%s/implies' % prior_role)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def check_role_inference_rule(self, prior_role, implies_role):
+        """Check a role inference rule."""
+        resp, body = self.head('roles/%s/implies/%s' %
+                               (prior_role, implies_role))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
+
+    def delete_role_inference_rule(self, prior_role, implies_role):
+        """Delete a role inference rule."""
+        resp, body = self.delete('roles/%s/implies/%s' %
+                                 (prior_role, implies_role))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 69b5250..c324261 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -321,8 +321,11 @@
         # We ping the external IP from the instance using its floating IP
         # which is always IPv4, so we must only test connectivity to
         # external IPv4 IPs if the external network is dualstack.
+        ext_ips = self.router['external_gateway_info']['external_fixed_ips']
+        subnet_ids = [sub['subnet_id'] for sub in ext_ips]
         v4_subnets = [s for s in self._list_subnets(
-            network_id=CONF.network.public_network_id) if s['ip_version'] == 4]
+            network_id=CONF.network.public_network_id)
+            if s['ip_version'] == 4 and s['id'] in subnet_ids]
         self.assertEqual(1, len(v4_subnets),
                          "Found %d IPv4 subnets" % len(v4_subnets))
 
diff --git a/tempest/tests/lib/services/identity/v3/test_roles_client.py b/tempest/tests/lib/services/identity/v3/test_roles_client.py
index 4f70b47..41cea85 100644
--- a/tempest/tests/lib/services/identity/v3/test_roles_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_roles_client.py
@@ -52,6 +52,65 @@
 
     FAKE_LIST_ROLES = {"roles": [FAKE_ROLE_INFO, FAKE_ROLE_INFO_2]}
 
+    FAKE_ROLE_INFERENCE_RULE = {
+        "role_inference": {
+            "prior_role": {
+                "id": FAKE_ROLE_ID,
+                "name": FAKE_ROLE_NAME,
+                "links": {
+                    "self": "http://example.com/identity/v3/roles/%s" % (
+                        FAKE_ROLE_ID)
+                }
+            },
+            "implies": {
+                "id": FAKE_ROLE_ID_2,
+                "name": FAKE_ROLE_NAME_2,
+                "links": {
+                    "self": "http://example.com/identity/v3/roles/%s" % (
+                        FAKE_ROLE_ID_2)
+                }
+            }
+        },
+        "links": {
+            "self": "http://example.com/identity/v3/roles/"
+                    "%s/implies/%s" % (FAKE_ROLE_ID, FAKE_ROLE_ID_2)
+        }
+    }
+
+    FAKE_LIST_ROLE_INFERENCES_RULES = {
+        "role_inference": {
+            "prior_role": {
+                "id": FAKE_ROLE_ID,
+                "name": FAKE_ROLE_NAME,
+                "links": {
+                    "self": "http://example.com/identity/v3/roles/%s" % (
+                        FAKE_ROLE_ID)
+                }
+            },
+            "implies": [
+                {
+                    "id": FAKE_ROLE_ID_2,
+                    "name": FAKE_ROLE_NAME_2,
+                    "links": {
+                        "self": "http://example.com/identity/v3/roles/%s" % (
+                            FAKE_ROLE_ID_2)
+                    }
+                },
+                {
+                    "id": "3",
+                    "name": "test3",
+                    "links": {
+                        "self": "http://example.com/identity/v3/roles/3"
+                    }
+                }
+            ]
+        },
+        "links": {
+            "self": "http://example.com/identity/v3/roles/"
+                    "%s/implies" % FAKE_ROLE_ID
+        }
+    }
+
     def setUp(self):
         super(TestRolesClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -172,6 +231,33 @@
             domain_id="b344506af7644f6794d9cb316600b020",
             group_id="123")
 
+    def _test_create_role_inference_rule(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_role_inference_rule,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_ROLE_INFERENCE_RULE,
+            bytes_body,
+            status=201,
+            prior_role=self.FAKE_ROLE_ID,
+            implies_role=self.FAKE_ROLE_ID_2)
+
+    def _test_show_role_inference_rule(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_role_inference_rule,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_ROLE_INFERENCE_RULE,
+            bytes_body,
+            prior_role=self.FAKE_ROLE_ID,
+            implies_role=self.FAKE_ROLE_ID_2)
+
+    def _test_list_role_inferences_rules(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_role_inferences_rules,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_ROLE_INFERENCES_RULES,
+            bytes_body,
+            prior_role=self.FAKE_ROLE_ID)
+
     def test_create_role_with_str_body(self):
         self._test_create_role()
 
@@ -319,3 +405,39 @@
             group_id="123",
             role_id="1234",
             status=204)
+
+    def test_create_role_inference_rule_with_str_body(self):
+        self._test_create_role_inference_rule()
+
+    def test_create_role_inference_rule_with_bytes_body(self):
+        self._test_create_role_inference_rule(bytes_body=True)
+
+    def test_show_role_inference_rule_with_str_body(self):
+        self._test_show_role_inference_rule()
+
+    def test_show_role_inference_rule_with_bytes_body(self):
+        self._test_show_role_inference_rule(bytes_body=True)
+
+    def test_list_role_inferences_rules_with_str_body(self):
+        self._test_list_role_inferences_rules()
+
+    def test_list_role_inferences_rules_with_bytes_body(self):
+        self._test_list_role_inferences_rules(bytes_body=True)
+
+    def test_check_role_inference_rule(self):
+        self.check_service_client_function(
+            self.client.check_role_inference_rule,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            status=204,
+            prior_role=self.FAKE_ROLE_ID,
+            implies_role=self.FAKE_ROLE_ID_2)
+
+    def test_delete_role_inference_rule(self):
+        self.check_service_client_function(
+            self.client.delete_role_inference_rule,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=204,
+            prior_role=self.FAKE_ROLE_ID,
+            implies_role=self.FAKE_ROLE_ID_2)