Add project-id and user-id when list server-groups

Currently, command "nova server-group-list" and
"nova server-group-get" doesn't return groups'
project id and user id information. It is really
hard to identify which group belong to which
project/user when admin user use this command
with option "--all-projects".

This patch add project-id and user-id to the list.
All os-server-groups APIs will contain the above
mentioned data in the response data.

DocImpact: This adds API microversion
APIImpact: Project id information will be returned for
os-servers-group API

Change-Id: I0405ed6271c33981578841cfade220758615b1fd
Implements: blueprint add-project-id-and-user-id
Partial-bug: #1481210
Depends-On: I167141676ef4f597a1c022c1fd5dc96fd55d02ad
This commit is contained in:
Kevin_Zheng 2015-08-04 19:54:57 +08:00
parent 3f8c69b2ef
commit 6c74a145bc
19 changed files with 260 additions and 80 deletions

View File

@ -0,0 +1,11 @@
{
"server_group": {
"id": "5bbcc3c4-1da2-4437-a48a-66f15b1b13f9",
"name": "test",
"policies": ["anti-affinity"],
"members": [],
"metadata": {},
"project_id": "c7c9f4f175e247acb56c108fd724d667",
"user_id": "fake"
}
}

View File

@ -0,0 +1,13 @@
{
"server_groups": [
{
"id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
"name": "test",
"policies": ["anti-affinity"],
"members": [],
"metadata": {},
"project_id": "c7c9f4f175e247acb56c108fd724d667",
"user_id": "fake"
}
]
}

View File

@ -0,0 +1,6 @@
{
"server_group": {
"name": "test",
"policies": ["anti-affinity"]
}
}

View File

@ -0,0 +1,11 @@
{
"server_group": {
"id": "5bbcc3c4-1da2-4437-a48a-66f15b1b13f9",
"name": "test",
"policies": ["anti-affinity"],
"members": [],
"metadata": {},
"project_id": "c7c9f4f175e247acb56c108fd724d667",
"user_id": "fake"
}
}

View File

@ -19,7 +19,7 @@
} }
], ],
"status": "CURRENT", "status": "CURRENT",
"version": "2.12", "version": "2.13",
"min_version": "2.1", "min_version": "2.1",
"updated": "2013-07-23T11:33:21Z" "updated": "2013-07-23T11:33:21Z"
} }

View File

@ -22,7 +22,7 @@
} }
], ],
"status": "CURRENT", "status": "CURRENT",
"version": "2.12", "version": "2.13",
"min_version": "2.1", "min_version": "2.1",
"updated": "2013-07-23T11:33:21Z" "updated": "2013-07-23T11:33:21Z"
} }

View File

@ -53,6 +53,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
user. user.
* 2.11 - Exposes forced_down attribute for os-services * 2.11 - Exposes forced_down attribute for os-services
* 2.12 - Exposes VIF net-id in os-virtual-interfaces * 2.12 - Exposes VIF net-id in os-virtual-interfaces
* 2.13 - Add project id and user id information for os-server-groups API
""" """
# The minimum and maximum versions of the API supported # The minimum and maximum versions of the API supported
@ -61,7 +62,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
# Note(cyeoh): This only applies for the v2.1 API once microversions # Note(cyeoh): This only applies for the v2.1 API once microversions
# support is fully merged. It does not affect the V2 API. # support is fully merged. It does not affect the V2 API.
_MIN_API_VERSION = "2.1" _MIN_API_VERSION = "2.1"
_MAX_API_VERSION = "2.12" _MAX_API_VERSION = "2.13"
DEFAULT_API_VERSION = _MIN_API_VERSION DEFAULT_API_VERSION = _MIN_API_VERSION

View File

@ -19,6 +19,7 @@ from oslo_log import log as logging
import webob import webob
from webob import exc from webob import exc
from nova.api.openstack import api_version_request
from nova.api.openstack import common from nova.api.openstack import common
from nova.api.openstack.compute.schemas import server_groups as schema from nova.api.openstack.compute.schemas import server_groups as schema
from nova.api.openstack import extensions from nova.api.openstack import extensions
@ -46,7 +47,7 @@ def _authorize_context(req):
class ServerGroupController(wsgi.Controller): class ServerGroupController(wsgi.Controller):
"""The Server group API controller for the OpenStack API.""" """The Server group API controller for the OpenStack API."""
def _format_server_group(self, context, group): def _format_server_group(self, context, group, req_ver):
# the id field has its value as the uuid of the server group # the id field has its value as the uuid of the server group
# There is no 'uuid' key in server_group seen by clients. # There is no 'uuid' key in server_group seen by clients.
# In addition, clients see policies as a ["policy-name"] list; # In addition, clients see policies as a ["policy-name"] list;
@ -66,17 +67,25 @@ class ServerGroupController(wsgi.Controller):
context, filters=filters) context, filters=filters)
members = [instance.uuid for instance in instances] members = [instance.uuid for instance in instances]
server_group['members'] = members server_group['members'] = members
# Add project id information to the response data for
# API version v2.13
if req_ver >= api_version_request.APIVersionRequest("2.13"):
server_group['project_id'] = group.project_id
server_group['user_id'] = group.user_id
return server_group return server_group
@extensions.expected_errors(404) @extensions.expected_errors(404)
def show(self, req, id): def show(self, req, id):
"""Return data about the given server group.""" """Return data about the given server group."""
req_ver = req.api_version_request
context = _authorize_context(req) context = _authorize_context(req)
try: try:
sg = objects.InstanceGroup.get_by_uuid(context, id) sg = objects.InstanceGroup.get_by_uuid(context, id)
except nova.exception.InstanceGroupNotFound as e: except nova.exception.InstanceGroupNotFound as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message()) raise webob.exc.HTTPNotFound(explanation=e.format_message())
return {'server_group': self._format_server_group(context, sg)} return {'server_group': self._format_server_group(
context, sg, req_ver)}
@wsgi.response(204) @wsgi.response(204)
@extensions.expected_errors(404) @extensions.expected_errors(404)
@ -113,6 +122,8 @@ class ServerGroupController(wsgi.Controller):
@extensions.expected_errors(()) @extensions.expected_errors(())
def index(self, req): def index(self, req):
"""Returns a list of server groups.""" """Returns a list of server groups."""
req_ver = req.api_version_request
context = _authorize_context(req) context = _authorize_context(req)
project_id = context.project_id project_id = context.project_id
if 'all_projects' in req.GET and context.is_admin: if 'all_projects' in req.GET and context.is_admin:
@ -121,7 +132,7 @@ class ServerGroupController(wsgi.Controller):
sgs = objects.InstanceGroupList.get_by_project_id( sgs = objects.InstanceGroupList.get_by_project_id(
context, project_id) context, project_id)
limited_list = common.limited(sgs.objects, req) limited_list = common.limited(sgs.objects, req)
result = [self._format_server_group(context, group) result = [self._format_server_group(context, group, req_ver)
for group in limited_list] for group in limited_list]
return {'server_groups': result} return {'server_groups': result}
@ -129,6 +140,8 @@ class ServerGroupController(wsgi.Controller):
@validation.schema(schema.create) @validation.schema(schema.create)
def create(self, req, body): def create(self, req, body):
"""Creates a new server group.""" """Creates a new server group."""
req_ver = req.api_version_request
context = _authorize_context(req) context = _authorize_context(req)
quotas = objects.Quotas(context=context) quotas = objects.Quotas(context=context)
@ -152,8 +165,8 @@ class ServerGroupController(wsgi.Controller):
raise exc.HTTPBadRequest(explanation=e) raise exc.HTTPBadRequest(explanation=e)
quotas.commit() quotas.commit()
return {'server_group': self._format_server_group(context, sg,
return {'server_group': self._format_server_group(context, sg)} req_ver)}
class ServerGroups(extensions.V21APIExtensionBase): class ServerGroups(extensions.V21APIExtensionBase):

View File

@ -130,3 +130,10 @@ user documentation.
Exposes VIF ``net-id`` attribute in ``os-virtual-interfaces``. Exposes VIF ``net-id`` attribute in ``os-virtual-interfaces``.
User will be able to get Virtual Interfaces ``net-id`` in Virtual Interfaces User will be able to get Virtual Interfaces ``net-id`` in Virtual Interfaces
list and can determine in which network a Virtual Interface is plugged into. list and can determine in which network a Virtual Interface is plugged into.
2.13
----
Add information ``project_id`` and ``user_id`` to ``os-server-groups``
API response data.

View File

@ -0,0 +1,11 @@
{
"server_group": {
"id": "%(id)s",
"name": "%(name)s",
"policies": ["anti-affinity"],
"members": [],
"metadata": {},
"project_id": "c7c9f4f175e247acb56c108fd724d667",
"user_id": "fake"
}
}

View File

@ -0,0 +1,13 @@
{
"server_groups": [
{
"id": "%(id)s",
"name": "test",
"policies": ["anti-affinity"],
"members": [],
"metadata": {},
"project_id": "c7c9f4f175e247acb56c108fd724d667",
"user_id": "fake"
}
]
}

View File

@ -0,0 +1,6 @@
{
"server_group": {
"name": "%(name)s",
"policies": ["anti-affinity"]
}
}

View File

@ -0,0 +1,11 @@
{
"server_group": {
"id": "%(id)s",
"name": "%(name)s",
"policies": ["anti-affinity"],
"members": [],
"metadata": {},
"project_id": "c7c9f4f175e247acb56c108fd724d667",
"user_id": "fake"
}
}

View File

@ -19,7 +19,7 @@
} }
], ],
"status": "CURRENT", "status": "CURRENT",
"version": "2.12", "version": "2.13",
"min_version": "2.1", "min_version": "2.1",
"updated": "2013-07-23T11:33:21Z" "updated": "2013-07-23T11:33:21Z"
} }

View File

@ -22,7 +22,7 @@
} }
], ],
"status": "CURRENT", "status": "CURRENT",
"version": "2.12", "version": "2.13",
"min_version": "2.1", "min_version": "2.1",
"updated": "2013-07-23T11:33:21Z" "updated": "2013-07-23T11:33:21Z"
} }

View File

@ -77,3 +77,9 @@ class ServerGroupsSampleJsonTest(api_sample_base.ApiSampleTestBaseV21):
uuid = self._post_server_group() uuid = self._post_server_group()
response = self._do_delete('os-server-groups/%s' % uuid) response = self._do_delete('os-server-groups/%s' % uuid)
self.assertEqual(204, response.status_code) self.assertEqual(204, response.status_code)
class ServerGroupsV213SampleJsonTest(api_sample_base.ApiSampleTestBaseV21):
extension_name = "os-server-groups"
request_api_version = '2.13'
scenarios = [('v2_13', {})]

View File

@ -16,6 +16,8 @@
from oslo_utils import uuidutils from oslo_utils import uuidutils
import webob import webob
import mock
from nova.api.openstack.compute.legacy_v2.contrib import server_groups from nova.api.openstack.compute.legacy_v2.contrib import server_groups
from nova.api.openstack.compute import server_groups as sg_v21 from nova.api.openstack.compute import server_groups as sg_v21
from nova.api.openstack import extensions from nova.api.openstack import extensions
@ -127,6 +129,120 @@ class ServerGroupTestV21(test.TestCase):
ig_uuid = self._create_instance_group(ctx, members) ig_uuid = self._create_instance_group(ctx, members)
return (ig_uuid, instances, members) return (ig_uuid, instances, members)
@mock.patch.object(nova.db, 'instance_group_get_all_by_project_id')
@mock.patch.object(nova.db, 'instance_group_get_all')
def _test_list_server_group_all(self,
mock_get_all,
mock_get_by_project,
api_version='2.1'):
policies = ['anti-affinity']
members = []
metadata = {} # always empty
names = ['default-x', 'test']
p_id = 'project_id'
u_id = 'user_id'
if api_version >= '2.13':
sg1 = server_group_resp_template(id=str(1345),
name=names[0],
policies=policies,
members=members,
metadata=metadata,
project_id=p_id,
user_id=u_id)
sg2 = server_group_resp_template(id=str(891),
name=names[1],
policies=policies,
members=members,
metadata=metadata,
project_id=p_id,
user_id=u_id)
else:
sg1 = server_group_resp_template(id=str(1345),
name=names[0],
policies=policies,
members=members,
metadata=metadata)
sg2 = server_group_resp_template(id=str(891),
name=names[1],
policies=policies,
members=members,
metadata=metadata)
tenant_groups = [sg2]
all_groups = [sg1, sg2]
all = {'server_groups': all_groups}
tenant_specific = {'server_groups': tenant_groups}
def return_all_server_groups():
return [server_group_db(sg) for sg in all_groups]
mock_get_all.return_value = return_all_server_groups()
def return_tenant_server_groups():
return [server_group_db(sg) for sg in tenant_groups]
mock_get_by_project.return_value = return_tenant_server_groups()
path = '/os-server-groups?all_projects=True'
req = fakes.HTTPRequest.blank(path, use_admin_context=True,
version=api_version)
res_dict = self.controller.index(req)
self.assertEqual(all, res_dict)
req = fakes.HTTPRequest.blank(path,
version=api_version)
res_dict = self.controller.index(req)
self.assertEqual(tenant_specific, res_dict)
@mock.patch.object(nova.db, 'instance_group_get_all_by_project_id')
def _test_list_server_group_by_tenant(self, mock_get_by_project,
api_version='2.1'):
policies = ['anti-affinity']
members = []
metadata = {} # always empty
names = ['default-x', 'test']
p_id = 'project_id'
u_id = 'user_id'
if api_version >= '2.13':
sg1 = server_group_resp_template(id=str(1345),
name=names[0],
policies=policies,
members=members,
metadata=metadata,
project_id=p_id,
user_id=u_id)
sg2 = server_group_resp_template(id=str(891),
name=names[1],
policies=policies,
members=members,
metadata=metadata,
project_id=p_id,
user_id=u_id)
else:
sg1 = server_group_resp_template(id=str(1345),
name=names[0],
policies=policies,
members=members,
metadata=metadata)
sg2 = server_group_resp_template(id=str(891),
name=names[1],
policies=policies,
members=members,
metadata=metadata)
groups = [sg1, sg2]
expected = {'server_groups': groups}
def return_server_groups():
return [server_group_db(sg) for sg in groups]
return_get_by_project = return_server_groups()
mock_get_by_project.return_value = return_get_by_project
path = '/os-server-groups'
self.req = fakes.HTTPRequest.blank(path,
version=api_version)
res_dict = self.controller.index(self.req)
self.assertEqual(expected, res_dict)
def test_display_members(self): def test_display_members(self):
ctx = context.RequestContext('fake_user', 'fake') ctx = context.RequestContext('fake_user', 'fake')
(ig_uuid, instances, members) = self._create_groups_and_instances(ctx) (ig_uuid, instances, members) = self._create_groups_and_instances(ctx)
@ -262,76 +378,10 @@ class ServerGroupTestV21(test.TestCase):
self.controller.create, self.req, body=body) self.controller.create, self.req, body=body)
def test_list_server_group_by_tenant(self): def test_list_server_group_by_tenant(self):
groups = [] self._test_list_server_group_by_tenant(api_version='2.1')
policies = ['anti-affinity']
members = []
metadata = {} # always empty
names = ['default-x', 'test']
sg1 = server_group_resp_template(id=str(1345),
name=names[0],
policies=policies,
members=members,
metadata=metadata)
sg2 = server_group_resp_template(id=str(891),
name=names[1],
policies=policies,
members=members,
metadata=metadata)
groups = [sg1, sg2]
expected = {'server_groups': groups}
def return_server_groups(context, project_id):
return [server_group_db(sg) for sg in groups]
self.stubs.Set(nova.db, 'instance_group_get_all_by_project_id',
return_server_groups)
res_dict = self.controller.index(self.req)
self.assertEqual(res_dict, expected)
def test_list_server_group_all(self): def test_list_server_group_all(self):
all_groups = [] self._test_list_server_group_all(api_version='2.1')
tenant_groups = []
policies = ['anti-affinity']
members = []
metadata = {} # always empty
names = ['default-x', 'test']
sg1 = server_group_resp_template(id=str(1345),
name=names[0],
policies=[],
members=members,
metadata=metadata)
sg2 = server_group_resp_template(id=str(891),
name=names[1],
policies=policies,
members=members,
metadata={})
tenant_groups = [sg2]
all_groups = [sg1, sg2]
all = {'server_groups': all_groups}
tenant_specific = {'server_groups': tenant_groups}
def return_all_server_groups(context):
return [server_group_db(sg) for sg in all_groups]
self.stubs.Set(nova.db, 'instance_group_get_all',
return_all_server_groups)
def return_tenant_server_groups(context, project_id):
return [server_group_db(sg) for sg in tenant_groups]
self.stubs.Set(nova.db, 'instance_group_get_all_by_project_id',
return_tenant_server_groups)
path = '/os-server-groups?all_projects=True'
req = fakes.HTTPRequest.blank(path, use_admin_context=True)
res_dict = self.controller.index(req)
self.assertEqual(res_dict, all)
req = fakes.HTTPRequest.blank(path)
res_dict = self.controller.index(req)
self.assertEqual(res_dict, tenant_specific)
def test_delete_server_group_by_id(self): def test_delete_server_group_by_id(self):
sg = server_group_template(id='123') sg = server_group_template(id='123')
@ -373,3 +423,16 @@ class ServerGroupTestV2(ServerGroupTestV21):
ext_mgr = extensions.ExtensionManager() ext_mgr = extensions.ExtensionManager()
ext_mgr.extensions = {} ext_mgr.extensions = {}
self.controller = server_groups.ServerGroupController(ext_mgr) self.controller = server_groups.ServerGroupController(ext_mgr)
class ServerGroupTestV213(ServerGroupTestV21):
wsgi_api_version = '2.13'
def _setup_controller(self):
self.controller = sg_v21.ServerGroupController()
def test_list_server_group_all(self):
self._test_list_server_group_all(api_version='2.13')
def test_list_server_group_by_tenant(self):
self._test_list_server_group_by_tenant(api_version='2.13')

View File

@ -66,7 +66,7 @@ EXP_VERSIONS = {
"v2.1": { "v2.1": {
"id": "v2.1", "id": "v2.1",
"status": "CURRENT", "status": "CURRENT",
"version": "2.12", "version": "2.13",
"min_version": "2.1", "min_version": "2.1",
"updated": "2013-07-23T11:33:21Z", "updated": "2013-07-23T11:33:21Z",
"links": [ "links": [
@ -128,7 +128,7 @@ class VersionsTestV20(test.NoDBTestCase):
{ {
"id": "v2.1", "id": "v2.1",
"status": "CURRENT", "status": "CURRENT",
"version": "2.12", "version": "2.13",
"min_version": "2.1", "min_version": "2.1",
"updated": "2013-07-23T11:33:21Z", "updated": "2013-07-23T11:33:21Z",
"links": [ "links": [

View File

@ -0,0 +1,8 @@
---
features:
- |
Project-id and user-id are now also returned in
the return data of os-server-groups APIs. In order
to use this new feature, user have to contain the
header of request microversion v2.13 in the API
request.