diff --git a/doc/api_samples/os-server-groups/v2.13/server-groups-get-resp.json b/doc/api_samples/os-server-groups/v2.13/server-groups-get-resp.json new file mode 100644 index 000000000000..e4f7f7d70742 --- /dev/null +++ b/doc/api_samples/os-server-groups/v2.13/server-groups-get-resp.json @@ -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" + } +} diff --git a/doc/api_samples/os-server-groups/v2.13/server-groups-list-resp.json b/doc/api_samples/os-server-groups/v2.13/server-groups-list-resp.json new file mode 100644 index 000000000000..bc1dab68cab8 --- /dev/null +++ b/doc/api_samples/os-server-groups/v2.13/server-groups-list-resp.json @@ -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" + } + ] +} diff --git a/doc/api_samples/os-server-groups/v2.13/server-groups-post-req.json b/doc/api_samples/os-server-groups/v2.13/server-groups-post-req.json new file mode 100644 index 000000000000..83b3b8e5d703 --- /dev/null +++ b/doc/api_samples/os-server-groups/v2.13/server-groups-post-req.json @@ -0,0 +1,6 @@ +{ + "server_group": { + "name": "test", + "policies": ["anti-affinity"] + } +} diff --git a/doc/api_samples/os-server-groups/v2.13/server-groups-post-resp.json b/doc/api_samples/os-server-groups/v2.13/server-groups-post-resp.json new file mode 100644 index 000000000000..e4f7f7d70742 --- /dev/null +++ b/doc/api_samples/os-server-groups/v2.13/server-groups-post-resp.json @@ -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" + } +} diff --git a/doc/api_samples/versions/v21-version-get-resp.json b/doc/api_samples/versions/v21-version-get-resp.json index 8516b3ea6033..30d00dc1cefc 100644 --- a/doc/api_samples/versions/v21-version-get-resp.json +++ b/doc/api_samples/versions/v21-version-get-resp.json @@ -19,7 +19,7 @@ } ], "status": "CURRENT", - "version": "2.12", + "version": "2.13", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/doc/api_samples/versions/versions-get-resp.json b/doc/api_samples/versions/versions-get-resp.json index 198ea986e6b9..901d39238d7d 100644 --- a/doc/api_samples/versions/versions-get-resp.json +++ b/doc/api_samples/versions/versions-get-resp.json @@ -22,7 +22,7 @@ } ], "status": "CURRENT", - "version": "2.12", + "version": "2.13", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/nova/api/openstack/api_version_request.py b/nova/api/openstack/api_version_request.py index 1a752282a9e0..905984971e46 100644 --- a/nova/api/openstack/api_version_request.py +++ b/nova/api/openstack/api_version_request.py @@ -53,6 +53,7 @@ REST_API_VERSION_HISTORY = """REST API Version History: user. * 2.11 - Exposes forced_down attribute for os-services * 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 @@ -61,7 +62,7 @@ REST_API_VERSION_HISTORY = """REST API Version History: # Note(cyeoh): This only applies for the v2.1 API once microversions # support is fully merged. It does not affect the V2 API. _MIN_API_VERSION = "2.1" -_MAX_API_VERSION = "2.12" +_MAX_API_VERSION = "2.13" DEFAULT_API_VERSION = _MIN_API_VERSION diff --git a/nova/api/openstack/compute/server_groups.py b/nova/api/openstack/compute/server_groups.py index 549814ac42c7..72406ae83830 100644 --- a/nova/api/openstack/compute/server_groups.py +++ b/nova/api/openstack/compute/server_groups.py @@ -19,6 +19,7 @@ from oslo_log import log as logging import webob from webob import exc +from nova.api.openstack import api_version_request from nova.api.openstack import common from nova.api.openstack.compute.schemas import server_groups as schema from nova.api.openstack import extensions @@ -46,7 +47,7 @@ def _authorize_context(req): class ServerGroupController(wsgi.Controller): """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 # There is no 'uuid' key in server_group seen by clients. # In addition, clients see policies as a ["policy-name"] list; @@ -66,17 +67,25 @@ class ServerGroupController(wsgi.Controller): context, filters=filters) members = [instance.uuid for instance in instances] 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 @extensions.expected_errors(404) def show(self, req, id): """Return data about the given server group.""" + req_ver = req.api_version_request + context = _authorize_context(req) try: sg = objects.InstanceGroup.get_by_uuid(context, id) except nova.exception.InstanceGroupNotFound as e: 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) @extensions.expected_errors(404) @@ -113,6 +122,8 @@ class ServerGroupController(wsgi.Controller): @extensions.expected_errors(()) def index(self, req): """Returns a list of server groups.""" + req_ver = req.api_version_request + context = _authorize_context(req) project_id = context.project_id 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( context, project_id) 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] return {'server_groups': result} @@ -129,6 +140,8 @@ class ServerGroupController(wsgi.Controller): @validation.schema(schema.create) def create(self, req, body): """Creates a new server group.""" + req_ver = req.api_version_request + context = _authorize_context(req) quotas = objects.Quotas(context=context) @@ -152,8 +165,8 @@ class ServerGroupController(wsgi.Controller): raise exc.HTTPBadRequest(explanation=e) 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): diff --git a/nova/api/openstack/rest_api_version_history.rst b/nova/api/openstack/rest_api_version_history.rst index 73cfb83c2ad6..6f55fe0ac0a7 100644 --- a/nova/api/openstack/rest_api_version_history.rst +++ b/nova/api/openstack/rest_api_version_history.rst @@ -130,3 +130,10 @@ user documentation. Exposes VIF ``net-id`` attribute in ``os-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. + +2.13 +---- + + Add information ``project_id`` and ``user_id`` to ``os-server-groups`` + API response data. + diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-server-groups/v2.13/server-groups-get-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-server-groups/v2.13/server-groups-get-resp.json.tpl new file mode 100644 index 000000000000..1bdfe9e1b7e0 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-server-groups/v2.13/server-groups-get-resp.json.tpl @@ -0,0 +1,11 @@ +{ + "server_group": { + "id": "%(id)s", + "name": "%(name)s", + "policies": ["anti-affinity"], + "members": [], + "metadata": {}, + "project_id": "c7c9f4f175e247acb56c108fd724d667", + "user_id": "fake" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-server-groups/v2.13/server-groups-list-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-server-groups/v2.13/server-groups-list-resp.json.tpl new file mode 100644 index 000000000000..417b9a1a1b63 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-server-groups/v2.13/server-groups-list-resp.json.tpl @@ -0,0 +1,13 @@ +{ + "server_groups": [ + { + "id": "%(id)s", + "name": "test", + "policies": ["anti-affinity"], + "members": [], + "metadata": {}, + "project_id": "c7c9f4f175e247acb56c108fd724d667", + "user_id": "fake" + } + ] +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-server-groups/v2.13/server-groups-post-req.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-server-groups/v2.13/server-groups-post-req.json.tpl new file mode 100644 index 000000000000..1cc232832044 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-server-groups/v2.13/server-groups-post-req.json.tpl @@ -0,0 +1,6 @@ +{ + "server_group": { + "name": "%(name)s", + "policies": ["anti-affinity"] + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-server-groups/v2.13/server-groups-post-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-server-groups/v2.13/server-groups-post-resp.json.tpl new file mode 100644 index 000000000000..1bdfe9e1b7e0 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/os-server-groups/v2.13/server-groups-post-resp.json.tpl @@ -0,0 +1,11 @@ +{ + "server_group": { + "id": "%(id)s", + "name": "%(name)s", + "policies": ["anti-affinity"], + "members": [], + "metadata": {}, + "project_id": "c7c9f4f175e247acb56c108fd724d667", + "user_id": "fake" + } +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/versions/v21-version-get-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/versions/v21-version-get-resp.json.tpl index ad5183985065..1f018ae7c280 100644 --- a/nova/tests/functional/api_sample_tests/api_samples/versions/v21-version-get-resp.json.tpl +++ b/nova/tests/functional/api_sample_tests/api_samples/versions/v21-version-get-resp.json.tpl @@ -19,7 +19,7 @@ } ], "status": "CURRENT", - "version": "2.12", + "version": "2.13", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/nova/tests/functional/api_sample_tests/api_samples/versions/versions-get-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/versions/versions-get-resp.json.tpl index 198ea986e6b9..901d39238d7d 100644 --- a/nova/tests/functional/api_sample_tests/api_samples/versions/versions-get-resp.json.tpl +++ b/nova/tests/functional/api_sample_tests/api_samples/versions/versions-get-resp.json.tpl @@ -22,7 +22,7 @@ } ], "status": "CURRENT", - "version": "2.12", + "version": "2.13", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/nova/tests/functional/api_sample_tests/test_server_groups.py b/nova/tests/functional/api_sample_tests/test_server_groups.py index 89cd06bff34d..6133c65effa2 100644 --- a/nova/tests/functional/api_sample_tests/test_server_groups.py +++ b/nova/tests/functional/api_sample_tests/test_server_groups.py @@ -77,3 +77,9 @@ class ServerGroupsSampleJsonTest(api_sample_base.ApiSampleTestBaseV21): uuid = self._post_server_group() response = self._do_delete('os-server-groups/%s' % uuid) 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', {})] diff --git a/nova/tests/unit/api/openstack/compute/test_server_groups.py b/nova/tests/unit/api/openstack/compute/test_server_groups.py index e4408a45be38..3566f40b66d2 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_groups.py +++ b/nova/tests/unit/api/openstack/compute/test_server_groups.py @@ -16,6 +16,8 @@ from oslo_utils import uuidutils import webob +import mock + 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 import extensions @@ -127,6 +129,120 @@ class ServerGroupTestV21(test.TestCase): ig_uuid = self._create_instance_group(ctx, 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): ctx = context.RequestContext('fake_user', 'fake') (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) def test_list_server_group_by_tenant(self): - groups = [] - 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) + self._test_list_server_group_by_tenant(api_version='2.1') def test_list_server_group_all(self): - all_groups = [] - 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) + self._test_list_server_group_all(api_version='2.1') def test_delete_server_group_by_id(self): sg = server_group_template(id='123') @@ -373,3 +423,16 @@ class ServerGroupTestV2(ServerGroupTestV21): ext_mgr = extensions.ExtensionManager() ext_mgr.extensions = {} 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') diff --git a/nova/tests/unit/api/openstack/compute/test_versions.py b/nova/tests/unit/api/openstack/compute/test_versions.py index 7a85ecdcbfae..1928363b1d62 100644 --- a/nova/tests/unit/api/openstack/compute/test_versions.py +++ b/nova/tests/unit/api/openstack/compute/test_versions.py @@ -66,7 +66,7 @@ EXP_VERSIONS = { "v2.1": { "id": "v2.1", "status": "CURRENT", - "version": "2.12", + "version": "2.13", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z", "links": [ @@ -128,7 +128,7 @@ class VersionsTestV20(test.NoDBTestCase): { "id": "v2.1", "status": "CURRENT", - "version": "2.12", + "version": "2.13", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z", "links": [ diff --git a/releasenotes/notes/bp-add-project-and-user-id-a560d087656157d4.yaml b/releasenotes/notes/bp-add-project-and-user-id-a560d087656157d4.yaml new file mode 100644 index 000000000000..81b876513ca6 --- /dev/null +++ b/releasenotes/notes/bp-add-project-and-user-id-a560d087656157d4.yaml @@ -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.