Accept and return project_id for API calls
Update the API to accept project_id in requests and return
project_id in responses.
For now, the API treats tenant_id and project_id equivalently.
It accepts either or both in requests.
It returns both in responses, depending on filters.
We include an extension to indicate that support for project_id
is enabled in the API.
Completes: blueprint keystone-v3
APIImpact: Describe how the Networking API supports Keystone V3.
Co-Authored-By: Henry Gessau <HenryG@gessau.net>
Co-Authored-By: Akihiro Motoki <amotoki@gmail.com>
Change-Id: I8775aa8a477191ef21e7c3c6da31d098befefc3c
diff --git a/neutron/tests/tempest/api/test_extensions.py b/neutron/tests/tempest/api/test_extensions.py
index 28029d8..b1b09e8 100644
--- a/neutron/tests/tempest/api/test_extensions.py
+++ b/neutron/tests/tempest/api/test_extensions.py
@@ -34,3 +34,7 @@
@test.idempotent_id('19db409e-a23f-445d-8bc8-ca3d64c84706')
def test_list_extensions_pagination(self):
self._test_list_extensions_includes('pagination')
+
+ @test.idempotent_id('155b7bc2-e358-4dd8-bf3e-1774c084567f')
+ def test_list_extensions_project_id(self):
+ self._test_list_extensions_includes('project-id')
diff --git a/neutron/tests/tempest/api/test_networks.py b/neutron/tests/tempest/api/test_networks.py
index 2c2991a..a6d8262 100644
--- a/neutron/tests/tempest/api/test_networks.py
+++ b/neutron/tests/tempest/api/test_networks.py
@@ -46,6 +46,10 @@
fields.append('mtu')
for key in fields:
self.assertEqual(network[key], self.network[key])
+ project_id = self.client.tenant_id
+ self.assertEqual(project_id, network['tenant_id'])
+ if test.is_extension_enabled('project-id', 'network'):
+ self.assertEqual(project_id, network['project_id'])
@test.idempotent_id('867819bb-c4b6-45f7-acf9-90edcf70aa5e')
def test_show_network_fields(self):
@@ -59,6 +63,27 @@
self.assertEqual(sorted(network.keys()), sorted(fields))
for field_name in fields:
self.assertEqual(network[field_name], self.network[field_name])
+ self.assertNotIn('tenant_id', network)
+ self.assertNotIn('project_id', network)
+
+ @test.idempotent_id('26f2b7a5-2cd1-4f3a-b11f-ad259b099b11')
+ @test.requires_ext(extension="project-id", service="network")
+ def test_show_network_fields_keystone_v3(self):
+
+ def _check_show_network_fields(fields, expect_project_id,
+ expect_tenant_id):
+ params = {}
+ if fields:
+ params['fields'] = fields
+ body = self.client.show_network(self.network['id'], **params)
+ network = body['network']
+ self.assertEqual(expect_project_id, 'project_id' in network)
+ self.assertEqual(expect_tenant_id, 'tenant_id' in network)
+
+ _check_show_network_fields(None, True, True)
+ _check_show_network_fields(['tenant_id'], False, True)
+ _check_show_network_fields(['project_id'], True, False)
+ _check_show_network_fields(['project_id', 'tenant_id'], True, True)
@test.idempotent_id('c72c1c0c-2193-4aca-ccc4-b1442640bbbb')
@test.requires_ext(extension="standard-attr-description",
@@ -75,6 +100,28 @@
body = self.client.list_networks(id=net_id)['networks'][0]
self.assertEqual('d2', body['description'])
+ @test.idempotent_id('0cc0552f-afaf-4231-b7a7-c2a1774616da')
+ @test.requires_ext(extension="project-id", service="network")
+ def test_create_network_keystone_v3(self):
+ project_id = self.client.tenant_id
+
+ name = 'created-with-project_id'
+ body = self.client.create_network_keystone_v3(name, project_id)
+ new_net = body['network']
+ self.assertEqual(name, new_net['name'])
+ self.assertEqual(project_id, new_net['project_id'])
+ self.assertEqual(project_id, new_net['tenant_id'])
+
+ body = self.client.list_networks(id=new_net['id'])['networks'][0]
+ self.assertEqual(name, body['name'])
+
+ new_name = 'create-with-project_id-2'
+ body = self.client.update_network(new_net['id'], name=new_name)
+ new_net = body['network']
+ self.assertEqual(new_name, new_net['name'])
+ self.assertEqual(project_id, new_net['project_id'])
+ self.assertEqual(project_id, new_net['tenant_id'])
+
@test.idempotent_id('6ae6d24f-9194-4869-9c85-c313cb20e080')
def test_list_networks_fields(self):
# Verify specific fields of the networks
@@ -87,6 +134,26 @@
for network in networks:
self.assertEqual(sorted(network.keys()), sorted(fields))
+ @test.idempotent_id('a23186b9-aa6f-4b08-b877-35ca3b9cd54c')
+ @test.requires_ext(extension="project-id", service="network")
+ def test_list_networks_fields_keystone_v3(self):
+ def _check_list_networks_fields(fields, expect_project_id,
+ expect_tenant_id):
+ params = {}
+ if fields:
+ params['fields'] = fields
+ body = self.client.list_networks(**params)
+ networks = body['networks']
+ self.assertNotEmpty(networks, "Network list returned is empty")
+ for network in networks:
+ self.assertEqual(expect_project_id, 'project_id' in network)
+ self.assertEqual(expect_tenant_id, 'tenant_id' in network)
+
+ _check_list_networks_fields(None, True, True)
+ _check_list_networks_fields(['tenant_id'], False, True)
+ _check_list_networks_fields(['project_id'], True, False)
+ _check_list_networks_fields(['project_id', 'tenant_id'], True, True)
+
class NetworksSearchCriteriaTest(base.BaseSearchCriteriaTest):
diff --git a/neutron/tests/tempest/api/test_qos.py b/neutron/tests/tempest/api/test_qos.py
index a2a9941..b9572ca 100644
--- a/neutron/tests/tempest/api/test_qos.py
+++ b/neutron/tests/tempest/api/test_qos.py
@@ -554,6 +554,7 @@
self.client2.show_qos_policy(qos_pol['id'])
rbac_pol = {'target_tenant': '*',
'tenant_id': self.admin_client.tenant_id,
+ 'project_id': self.admin_client.tenant_id,
'object_type': 'qos_policy',
'object_id': qos_pol['id'],
'action': 'access_as_shared'}
diff --git a/neutron/tests/tempest/services/network/json/network_client.py b/neutron/tests/tempest/services/network/json/network_client.py
index bcb55d7..4828059 100644
--- a/neutron/tests/tempest/services/network/json/network_client.py
+++ b/neutron/tests/tempest/services/network/json/network_client.py
@@ -872,6 +872,19 @@
body = jsonutils.loads(body)
return service_client.ResponseBody(resp, body)
+ def create_network_keystone_v3(self, name, project_id):
+ uri = '%s/networks' % self.uri_prefix
+ post_data = {
+ 'network': {
+ 'name': name,
+ 'project_id': project_id
+ }
+ }
+ resp, body = self.post(uri, self.serialize(post_data))
+ body = self.deserialize_single(body)
+ self.expected_success(201, resp.status)
+ return service_client.ResponseBody(resp, body)
+
def list_extensions(self, **filters):
uri = self.get_uri("extensions")
if filters: