Add project_id in group snapshots list and show API

Add ``project_id`` to response body of list group snapshots
with detail and show group snapshot detail APIs.

Merging the group and group snapshot response in same MV
was discussed in weekly meeting[1].

[1] http://eavesdrop.openstack.org/meetings/cinder/2019/cinder.2019-02-20-16.00.log.html#l-122
Change-Id: Ided66450b5d7de32551edbce249e94f6174da2eb
Implements: blueprint add-project-id-to-group-groupsnapshot-response
This commit is contained in:
whoami-rajat 2019-02-26 16:55:52 +05:30
parent 8b39e1cee6
commit 6dd3b9c51f
14 changed files with 130 additions and 27 deletions

View File

@ -80,6 +80,7 @@ Response Parameters
- status: status_group_snap - status: status_group_snap
- description: description_group_snap_req - description: description_group_snap_req
- group_type_id: group_type_id - group_type_id: group_type_id
- project_id: project_id_group_snapshot
Response Example Response Example
---------------- ----------------
@ -135,6 +136,7 @@ Response Parameters
- created_at: created_at - created_at: created_at
- group_id: group_id - group_id: group_id
- group_type_id: group_type_id - group_type_id: group_type_id
- project_id: project_id_group_snapshot
Response Example Response Example
---------------- ----------------

View File

@ -2216,7 +2216,14 @@ project_id_group:
description: | description: |
The UUID of the volume group project. The UUID of the volume group project.
in: body in: body
required: true required: false
type: string
min_version: 3.58
project_id_group_snapshot:
description: |
The UUID of the volume group snapshot project.
in: body
required: false
type: string type: string
min_version: 3.58 min_version: 3.58
project_id_host: project_id_host:

View File

@ -7,7 +7,8 @@
"created_at": "2015-09-16T09:28:52.000000", "created_at": "2015-09-16T09:28:52.000000",
"name": "my_group_snapshot1", "name": "my_group_snapshot1",
"description": "my first group snapshot", "description": "my first group snapshot",
"group_type_id": "0ef094a2-d9fd-4c79-acfd-ac60a0506b7d" "group_type_id": "0ef094a2-d9fd-4c79-acfd-ac60a0506b7d",
"project_id": "7ccf4863071f44aeb8f141f65780c51b"
}, },
{ {
"id": "aed36625-a6d7-4681-ba59-c7ba3d18c148", "id": "aed36625-a6d7-4681-ba59-c7ba3d18c148",
@ -16,7 +17,8 @@
"created_at": "2015-09-16T09:31:15.000000", "created_at": "2015-09-16T09:31:15.000000",
"name": "my_group_snapshot2", "name": "my_group_snapshot2",
"description": "Edited description", "description": "Edited description",
"group_type_id": "7270c56e-6354-4528-8e8b-f54dee2232c8" "group_type_id": "7270c56e-6354-4528-8e8b-f54dee2232c8",
"project_id": "7ccf4863071f44aeb8f141f65780c51b"
} }
] ]
} }

View File

@ -6,6 +6,7 @@
"created_at": "2015-09-16T09:28:52.000000", "created_at": "2015-09-16T09:28:52.000000",
"name": "my_group_snapshot1", "name": "my_group_snapshot1",
"description": "my first group snapshot", "description": "my first group snapshot",
"group_type_id": "7270c56e-6354-4528-8e8b-f54dee2232c8" "group_type_id": "7270c56e-6354-4528-8e8b-f54dee2232c8",
"project_id": "7ccf4863071f44aeb8f141f65780c51b"
} }
} }

View File

@ -155,7 +155,7 @@ BACKUP_PROJECT_USER_ID = '3.56'
TRANSFER_WITH_HISTORY = '3.57' TRANSFER_WITH_HISTORY = '3.57'
GROUP_PROJECT_ID = '3.58' GROUP_GROUPSNAPSHOT_PROJECT_ID = '3.58'
SUPPORT_TRANSFER_PAGINATION = '3.59' SUPPORT_TRANSFER_PAGINATION = '3.59'

View File

@ -132,7 +132,8 @@ REST_API_VERSION_HISTORY = """
* 3.57 - Add 'source_project_id', 'destination_project_id', 'accepted' to * 3.57 - Add 'source_project_id', 'destination_project_id', 'accepted' to
transfer. transfer.
* 3.58 - Add ``project_id`` attribute to response body of list groups with * 3.58 - Add ``project_id`` attribute to response body of list groups with
detail and show group detail APIs. detail, list group snapshots with detail, show group detail and
show group snapshot detail APIs.
* 3.59 - Support volume transfer pagination. * 3.59 - Support volume transfer pagination.
""" """

View File

@ -455,8 +455,9 @@ related api (create/show/list detail transfer APIs) responses.
3.58 3.58
---- ----
Add ``project_id`` attribute to response body of list groups with detail and Add ``project_id`` attribute to response body of list groups with detail,
show group detail APIs. list group snapshots with detail, show group detail and show group snapshot
detail APIs.
3.59 3.59
---- ----

View File

@ -14,6 +14,8 @@
# under the License. # under the License.
from cinder.api import common from cinder.api import common
from cinder.api import microversions as mv
from cinder.policies import group_snapshots as policy
class ViewBuilder(common.ViewBuilder): class ViewBuilder(common.ViewBuilder):
@ -47,7 +49,7 @@ class ViewBuilder(common.ViewBuilder):
def detail(self, request, group_snapshot): def detail(self, request, group_snapshot):
"""Detailed view of a single group_snapshot.""" """Detailed view of a single group_snapshot."""
return { group_snapshot_ref = {
'group_snapshot': { 'group_snapshot': {
'id': group_snapshot.id, 'id': group_snapshot.id,
'group_id': group_snapshot.group_id, 'group_id': group_snapshot.group_id,
@ -58,6 +60,16 @@ class ViewBuilder(common.ViewBuilder):
'description': group_snapshot.description 'description': group_snapshot.description
} }
} }
req_version = request.api_version_request
context = request.environ['cinder.context']
if req_version.matches(mv.GROUP_GROUPSNAPSHOT_PROJECT_ID, None):
if context.authorize(policy.GROUP_SNAPSHOT_ATTRIBUTES_POLICY,
fatal=False):
group_snapshot_ref['group_snapshot']['project_id'] = (
group_snapshot.project_id)
return group_snapshot_ref
def _list_view(self, func, request, group_snapshots): def _list_view(self, func, request, group_snapshots):
"""Provide a view for a list of group_snapshots.""" """Provide a view for a list of group_snapshots."""

View File

@ -80,7 +80,7 @@ class ViewBuilder(common.ViewBuilder):
if req_version.matches(mv.GROUP_REPLICATION, None): if req_version.matches(mv.GROUP_REPLICATION, None):
group_ref['group']['replication_status'] = group.replication_status group_ref['group']['replication_status'] = group.replication_status
if req_version.matches(mv.GROUP_PROJECT_ID, None): if req_version.matches(mv.GROUP_GROUPSNAPSHOT_PROJECT_ID, None):
if context.authorize(policy.GROUP_ATTRIBUTES_POLICY, fatal=False): if context.authorize(policy.GROUP_ATTRIBUTES_POLICY, fatal=False):
group_ref['group']['project_id'] = group.project_id group_ref['group']['project_id'] = group.project_id

View File

@ -23,6 +23,7 @@ DELETE_POLICY = 'group:delete_group_snapshot'
UPDATE_POLICY = 'group:update_group_snapshot' UPDATE_POLICY = 'group:update_group_snapshot'
GET_POLICY = 'group:get_group_snapshot' GET_POLICY = 'group:get_group_snapshot'
GET_ALL_POLICY = 'group:get_all_group_snapshots' GET_ALL_POLICY = 'group:get_all_group_snapshots'
GROUP_SNAPSHOT_ATTRIBUTES_POLICY = 'group:group_snapshot_project_attribute'
group_snapshots_policies = [ group_snapshots_policies = [
@ -80,6 +81,21 @@ group_snapshots_policies = [
'path': '/group_snapshots/{group_snapshot_id}' 'path': '/group_snapshots/{group_snapshot_id}'
} }
]), ]),
policy.DocumentedRuleDefault(
name=GROUP_SNAPSHOT_ATTRIBUTES_POLICY,
check_str=base.RULE_ADMIN_API,
description="List group snapshots or show group "
"snapshot with project attributes.",
operations=[
{
'method': 'GET',
'path': '/group_snapshots/{group_snapshot_id}'
},
{
'method': 'GET',
'path': '/group_snapshots/detail'
}
]),
] ]

View File

@ -297,6 +297,43 @@ class GroupSnapshotsAPITestCase(test.TestCase):
self.controller.show, self.controller.show,
req, fake.WILL_NOT_BE_FOUND_ID) req, fake.WILL_NOT_BE_FOUND_ID)
def test_show_group_snapshot_with_project_id(self):
group_snapshot = utils.create_group_snapshot(
self.context, group_id=self.group.id)
req = fakes.HTTPRequest.blank(
'/v3/%s/group_snapshots/%s' % (fake.PROJECT_ID,
group_snapshot.id),
version=mv.GROUP_GROUPSNAPSHOT_PROJECT_ID,
use_admin_context=True)
res_dict = self.controller.show(req, group_snapshot.id)
self.assertEqual(1, len(res_dict))
self.assertEqual('test_group_snapshot',
res_dict['group_snapshot']['name'])
self.assertEqual(fake.PROJECT_ID,
res_dict['group_snapshot']['project_id'])
group_snapshot.destroy()
def test_show_group_snapshot_without_project_id(self):
group_snapshot = utils.create_group_snapshot(
self.context, group_id=self.group.id)
# using mv.TRANSFER_WITH_HISTORY (3.57) to test the
# project_id field is not in response before mv 3.58
req = fakes.HTTPRequest.blank(
'/v3/%s/group_snapshots/%s' % (fake.PROJECT_ID,
group_snapshot.id),
version=mv.TRANSFER_WITH_HISTORY,
use_admin_context=True)
res_dict = self.controller.show(req, group_snapshot.id)
self.assertEqual(1, len(res_dict))
self.assertEqual('test_group_snapshot',
res_dict['group_snapshot']['name'])
self.assertNotIn('project_id', res_dict['group_snapshot'])
group_snapshot.destroy()
@ddt.data(True, False) @ddt.data(True, False)
def test_list_group_snapshots_json(self, is_detail): def test_list_group_snapshots_json(self, is_detail):
if is_detail: if is_detail:
@ -324,6 +361,29 @@ class GroupSnapshotsAPITestCase(test.TestCase):
self.assertNotIn('description', self.assertNotIn('description',
res_dict['group_snapshots'][2 - index].keys()) res_dict['group_snapshots'][2 - index].keys())
@ddt.data(True, False)
def test_list_group_snapshots_with_project_id(self, is_detail):
if is_detail:
request_url = '/v3/%s/group_snapshots/detail'
else:
request_url = '/v3/%s/group_snapshots'
req = fakes.HTTPRequest.blank(
request_url % fake.PROJECT_ID,
version=mv.GROUP_GROUPSNAPSHOT_PROJECT_ID,
use_admin_context=True)
if is_detail:
res_dict = self.controller.detail(req)
else:
res_dict = self.controller.index(req)
self.assertEqual(1, len(res_dict))
self.assertEqual(3, len(res_dict['group_snapshots']))
for group in res_dict['group_snapshots']:
if is_detail:
self.assertIsNotNone(group['project_id'])
else:
self.assertNotIn('project_id', group)
@mock.patch('cinder.db.volume_type_get') @mock.patch('cinder.db.volume_type_get')
@mock.patch('cinder.quota.VolumeTypeQuotaEngine.reserve') @mock.patch('cinder.quota.VolumeTypeQuotaEngine.reserve')
def test_create_group_snapshot_json(self, mock_quota, mock_vol_type): def test_create_group_snapshot_json(self, mock_quota, mock_vol_type):

View File

@ -1423,10 +1423,10 @@ class GroupsAPITestCase(test.TestCase):
def test_show_group_with_project_id(self): def test_show_group_with_project_id(self):
# If the microversion >= 3.58 and "is_admin=True", "project_id" should # If the microversion >= 3.58 and "is_admin=True", "project_id" should
# be contained in the response body. # be contained in the response body.
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s' % req = fakes.HTTPRequest.blank(
(fake.PROJECT_ID, self.group1.id), '/v3/%s/groups/%s' % (fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_PROJECT_ID, version=mv.GROUP_GROUPSNAPSHOT_PROJECT_ID,
use_admin_context=True) use_admin_context=True)
res_dict = self.controller.show(req, self.group1.id) res_dict = self.controller.show(req, self.group1.id)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
self.assertEqual(fake.PROJECT_ID, self.assertEqual(fake.PROJECT_ID,
@ -1437,7 +1437,7 @@ class GroupsAPITestCase(test.TestCase):
req = fakes.HTTPRequest.blank( req = fakes.HTTPRequest.blank(
'/v3/%s/groups/%s' % '/v3/%s/groups/%s' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=mv.get_prior_version(mv.GROUP_PROJECT_ID), version=mv.get_prior_version(mv.GROUP_GROUPSNAPSHOT_PROJECT_ID),
use_admin_context=True) use_admin_context=True)
res_dict = self.controller.show(req, self.group1.id) res_dict = self.controller.show(req, self.group1.id)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
@ -1450,10 +1450,10 @@ class GroupsAPITestCase(test.TestCase):
self.group2.group_type_id = fake.GROUP_TYPE2_ID self.group2.group_type_id = fake.GROUP_TYPE2_ID
self.group2.save() self.group2.save()
req = fakes.HTTPRequest.blank(('/v3/%s/groups/detail' req = fakes.HTTPRequest.blank(
% self.ctxt.project_id), '/v3/%s/groups/detail' % self.ctxt.project_id,
version=mv.GROUP_PROJECT_ID, version=mv.GROUP_GROUPSNAPSHOT_PROJECT_ID,
use_admin_context=True) use_admin_context=True)
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
@ -1465,9 +1465,9 @@ class GroupsAPITestCase(test.TestCase):
def test_show_group_without_project_id(self): def test_show_group_without_project_id(self):
# If the microversion >= 3.58 and "is_admin=False", "project_id" should # If the microversion >= 3.58 and "is_admin=False", "project_id" should
# not be contained in the response body. # not be contained in the response body.
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s' % req = fakes.HTTPRequest.blank(
(fake.PROJECT_ID, self.group3.id), '/v3/%s/groups/%s' % (fake.PROJECT_ID, self.group3.id),
version=mv.GROUP_PROJECT_ID) version=mv.GROUP_GROUPSNAPSHOT_PROJECT_ID)
res_dict = self.controller.show(req, self.group1.id) res_dict = self.controller.show(req, self.group1.id)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
self.assertNotIn('project_id', res_dict['group']) self.assertNotIn('project_id', res_dict['group'])

View File

@ -0,0 +1,6 @@
---
features:
- |
Added ``project_id`` attribute to response body of list groups with detail,
list group snapshots with detail, show group detail and show group snapshot
detail APIs since microversion "3.58".

View File

@ -1,5 +0,0 @@
---
features:
- |
Added ``project_id`` attribute to response body of list groups with detail
and show group detail APIs since microversion "3.58".