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:
parent
8b39e1cee6
commit
6dd3b9c51f
@ -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
|
||||||
----------------
|
----------------
|
||||||
|
@ -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:
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
|
|
||||||
|
@ -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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -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
|
||||||
----
|
----
|
||||||
|
@ -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."""
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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'
|
||||||
|
}
|
||||||
|
]),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
@ -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'])
|
||||||
|
@ -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".
|
@ -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".
|
|
Loading…
Reference in New Issue
Block a user