diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index ab59060da5..da74e092f5 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -1605,6 +1605,24 @@ is_admin_only: in: body required: true type: boolean +is_default_type: + description: | + Defines the share type created is default or not. If the returning + value is true, then it is the default share type, otherwise, it is + not default. + in: body + required: true + type: boolean + min_version: 2.46 +is_group_type_default: + description: | + Defines the share group type created is default or not. If the + returning value is true, then it is the default share group type, + otherwise, it is not default. + in: body + required: true + type: boolean + min_version: 2.46 is_public: description: | (Since API v2.8) The level of visibility for the diff --git a/api-ref/source/samples/share-group-type-create-response.json b/api-ref/source/samples/share-group-type-create-response.json index a4b8464aaf..cfb19b4392 100644 --- a/api-ref/source/samples/share-group-type-create-response.json +++ b/api-ref/source/samples/share-group-type-create-response.json @@ -4,6 +4,7 @@ "group_specs": {}, "share_types": ["ecd11f4c-d811-4471-b656-c755c77e02ba"], "id": "89861c2a-10bf-4013-bdd4-3d020466aee4", - "name": "test_group_type" + "name": "test_group_type", + "is_default": false } } diff --git a/api-ref/source/samples/share-group-types-default-list-response.json b/api-ref/source/samples/share-group-types-default-list-response.json index a4b8464aaf..b7f1075ae3 100644 --- a/api-ref/source/samples/share-group-types-default-list-response.json +++ b/api-ref/source/samples/share-group-types-default-list-response.json @@ -4,6 +4,7 @@ "group_specs": {}, "share_types": ["ecd11f4c-d811-4471-b656-c755c77e02ba"], "id": "89861c2a-10bf-4013-bdd4-3d020466aee4", - "name": "test_group_type" + "name": "test_group_type", + "is_default": true } } diff --git a/api-ref/source/samples/share-group-types-list-response.json b/api-ref/source/samples/share-group-types-list-response.json index 7a6d489536..e624678d40 100644 --- a/api-ref/source/samples/share-group-types-list-response.json +++ b/api-ref/source/samples/share-group-types-list-response.json @@ -5,7 +5,8 @@ "group_specs": {}, "share_types": ["ecd11f4c-d811-4471-b656-c755c77e02ba"], "id": "89861c2a-10bf-4013-bdd4-3d020466aee4", - "name": "test_group_type" + "name": "test_group_type", + "is_default": false } ] } diff --git a/api-ref/source/samples/share-type-create-response.json b/api-ref/source/samples/share-type-create-response.json index a5add19450..6f3cbb18ac 100644 --- a/api-ref/source/samples/share-type-create-response.json +++ b/api-ref/source/samples/share-type-create-response.json @@ -14,6 +14,7 @@ }, "id": "7fa1342b-de9d-4d89-bdc8-af67795c0e52", "name": "testing", + "is_default": false, "description": "share type description" }, "volume_type": { @@ -31,6 +32,7 @@ }, "id": "7fa1342b-de9d-4d89-bdc8-af67795c0e52", "name": "testing", + "is_default": false, "description": "share type description" } } diff --git a/api-ref/source/samples/share-type-show-response.json b/api-ref/source/samples/share-type-show-response.json index 840bbda6c2..a5a746e566 100644 --- a/api-ref/source/samples/share-type-show-response.json +++ b/api-ref/source/samples/share-type-show-response.json @@ -9,6 +9,7 @@ }, "id": "2780fc88-526b-464a-a72c-ecb83f0e3929", "name": "default-share-type", + "is_default": true, "description": "manila share type" }, "volume_type": { @@ -21,6 +22,7 @@ }, "id": "2780fc88-526b-464a-a72c-ecb83f0e3929", "name": "default-share-type", + "is_default": true, "description": "manila share type" } } diff --git a/api-ref/source/samples/share-types-default-list-response.json b/api-ref/source/samples/share-types-default-list-response.json index 3d2773b871..677b5d8719 100644 --- a/api-ref/source/samples/share-types-default-list-response.json +++ b/api-ref/source/samples/share-types-default-list-response.json @@ -9,6 +9,7 @@ }, "id": "420e6a31-3f3d-4ed7-9d11-59450372182a", "name": "default", + "is_default": true, "description": "share type description" }, "volume_type": { @@ -21,6 +22,7 @@ }, "id": "420e6a31-3f3d-4ed7-9d11-59450372182a", "name": "default", + "is_default": true, "description": "share type description" } } diff --git a/api-ref/source/samples/share-types-list-response.json b/api-ref/source/samples/share-types-list-response.json index ac745a1417..c3ac14a2ee 100644 --- a/api-ref/source/samples/share-types-list-response.json +++ b/api-ref/source/samples/share-types-list-response.json @@ -10,6 +10,7 @@ }, "id": "420e6a31-3f3d-4ed7-9d11-59450372182a", "name": "default", + "is_default": true, "description": "share type description" }, { @@ -27,6 +28,7 @@ }, "id": "7fa1342b-de9d-4d89-bdc8-af67795c0e52", "name": "testing", + "is_default": false, "description": "share type description" } ], @@ -41,6 +43,7 @@ }, "id": "420e6a31-3f3d-4ed7-9d11-59450372182a", "name": "default", + "is_default": true, "description": "share type description" }, { @@ -58,6 +61,7 @@ }, "id": "7fa1342b-de9d-4d89-bdc8-af67795c0e52", "name": "testing", + "is_default": false, "description": "share type description" } ] diff --git a/api-ref/source/share-group-types.inc b/api-ref/source/share-group-types.inc index 7baddb5130..7344b79920 100644 --- a/api-ref/source/share-group-types.inc +++ b/api-ref/source/share-group-types.inc @@ -65,6 +65,7 @@ Response parameters - share_types: share_types_1 - name: share_group_type_name - group_specs: group_specs_required + - is_default: is_group_type_default Response example ---------------- @@ -103,7 +104,7 @@ Request Response parameters ------------------- - + .. rest_parameters:: parameters.yaml - id: share_group_type_id_required @@ -111,6 +112,7 @@ Response parameters - share_types: share_types_1 - name: share_group_type_name - group_specs: group_specs_required + - is_default: is_group_type_default Response example ---------------- @@ -211,6 +213,7 @@ Response parameters - name: share_group_type_name - share_types: share_types_1 - is_public: share_group_type_is_public + - is_default: is_group_type_default Response example ---------------- diff --git a/api-ref/source/share-types.inc b/api-ref/source/share-types.inc index 457a440b07..eef8acc725 100644 --- a/api-ref/source/share-types.inc +++ b/api-ref/source/share-types.inc @@ -124,6 +124,7 @@ Response parameters - share_type_access:is_public: share_type_access:is_public - create_share_from_snapshot_support: create_share_from_snapshot_support - description: share_type_description + - is_default: is_default_type Response example ---------------- @@ -173,6 +174,7 @@ Response parameters - share_type_access:is_public: share_type_access:is_public - name: share_type_name - description: share_type_description + - is_default: is_default_type Response example ---------------- @@ -227,6 +229,7 @@ Response Parameters - share_type_access:is_public: share_type_access:is_public - name: share_type_name - description: share_type_description + - is_default: is_default_type Response Example ---------------- @@ -348,6 +351,7 @@ Response parameters - revert_to_snapshot_support: revert_to_snapshot_support - create_share_from_snapshot_support: create_share_from_snapshot_support - description: share_type_description + - is_default: is_default_type Response example ---------------- diff --git a/manila/api/openstack/api_version_request.py b/manila/api/openstack/api_version_request.py index 4a6fa8b720..656bc97b96 100644 --- a/manila/api/openstack/api_version_request.py +++ b/manila/api/openstack/api_version_request.py @@ -119,13 +119,15 @@ REST_API_VERSION_HISTORY = """ * 2.45 - Added access metadata for share access and also introduced the GET /share-access-rules API. The prior API to retrieve access rules will not work with API version >=2.45. + * 2.46 - Added 'is_default' field to 'share_type' and 'share_group_type' + objects. """ # The minimum and maximum versions of the API supported # The default api version request is defined to be the # minimum version of the API supported. _MIN_API_VERSION = "2.0" -_MAX_API_VERSION = "2.45" +_MAX_API_VERSION = "2.46" DEFAULT_API_VERSION = _MIN_API_VERSION diff --git a/manila/api/openstack/rest_api_version_history.rst b/manila/api/openstack/rest_api_version_history.rst index 0e014cb9bc..085c1c5f54 100644 --- a/manila/api/openstack/rest_api_version_history.rst +++ b/manila/api/openstack/rest_api_version_history.rst @@ -248,3 +248,8 @@ user documentation. Added access metadata for share access and also introduced the GET /share-access-rules API. The prior API to retrieve access rules will not work with API version >=2.45. + +2.46 +---- + Added 'is_default' field to 'share_type' and 'share_group_type' + objects. diff --git a/manila/api/views/share_group_types.py b/manila/api/views/share_group_types.py index 02f23033ca..89e476f727 100644 --- a/manila/api/views/share_group_types.py +++ b/manila/api/views/share_group_types.py @@ -9,13 +9,20 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +from oslo_config import cfg from manila.api import common +CONF = cfg.CONF + class ShareGroupTypeViewBuilder(common.ViewBuilder): _collection_name = 'share_group_types' + _detail_version_modifiers = [ + "add_is_default_attr", + ] + def show(self, request, share_group_type, brief=False): """Trim away extraneous share group type attributes.""" group_specs = share_group_type.get('group_specs', {}) @@ -37,3 +44,15 @@ class ShareGroupTypeViewBuilder(common.ViewBuilder): for share_group_type in share_group_types ] return {"share_group_types": share_group_types_list} + + @common.ViewBuilder.versioned_method("2.46") + def add_is_default_attr(self, context, + share_group_type_dict, + share_group_type): + is_default = False + type_name = share_group_type.get('name') + default_name = CONF.default_share_group_type + + if default_name is not None: + is_default = default_name == type_name + share_group_type_dict['is_default'] = is_default diff --git a/manila/api/views/types.py b/manila/api/views/types.py index 2c81ca06ab..5dbef70dc0 100644 --- a/manila/api/views/types.py +++ b/manila/api/views/types.py @@ -13,10 +13,14 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_config import cfg + from manila.api import common from manila.common import constants from manila.share import share_types +CONF = cfg.CONF + class ViewBuilder(common.ViewBuilder): @@ -27,6 +31,7 @@ class ViewBuilder(common.ViewBuilder): "add_is_public_attr_extension_like", "add_inferred_optional_extra_specs", "add_description_attr", + "add_is_default_attr" ] def show(self, request, share_type, brief=False): @@ -103,3 +108,13 @@ class ViewBuilder(common.ViewBuilder): @common.ViewBuilder.versioned_method("2.41") def add_description_attr(self, context, share_type_dict, share_type): share_type_dict['description'] = share_type.get('description') + + @common.ViewBuilder.versioned_method("2.46") + def add_is_default_attr(self, context, share_type_dict, share_type): + is_default = False + type_name = share_type.get('name') + default_name = CONF.default_share_type + + if default_name is not None: + is_default = default_name == type_name + share_type_dict['is_default'] = is_default diff --git a/manila/tests/api/v2/test_share_group_types.py b/manila/tests/api/v2/test_share_group_types.py index fb9ca83bca..e3705a37cd 100644 --- a/manila/tests/api/v2/test_share_group_types.py +++ b/manila/tests/api/v2/test_share_group_types.py @@ -15,6 +15,7 @@ import datetime import ddt import mock +from oslo_config import cfg import webob from manila.api.v2 import share_group_types as types @@ -24,6 +25,8 @@ from manila.share_group import share_group_types from manila import test from manila.tests.api import fakes +CONF = cfg.CONF + PROJ1_UUID = '11111111-1111-1111-1111-111111111111' PROJ2_UUID = '22222222-2222-2222-2222-222222222222' PROJ3_UUID = '33333333-3333-3333-3333-333333333333' @@ -53,6 +56,18 @@ GROUP_TYPE_2 = { 'share_types': [{'share_type_id': SHARE_TYPE_ID}], } +GROUP_TYPE_3 = { + 'id': '61fdcbed-db27-4cc0-8938-8b4f74c2ae59', + 'name': u'group type 3', + 'deleted': False, + 'created_at': datetime.datetime(2012, 1, 1, 1, 1, 1, 1), + 'updated_at': None, + 'deleted_at': None, + 'is_public': True, + 'group_specs': {}, + 'share_types': [], +} + def fake_request(url, admin=False, experimental=True, version='2.31', **kwargs): @@ -430,6 +445,98 @@ class ShareGroupTypesAPITest(test.TestCase): webob.exc.HTTPNotFound, self.controller._create, req, fake_body) + @ddt.data(('2.45', True), ('2.45', False), + ('2.46', True), ('2.46', False)) + @ddt.unpack + def test_share_group_types_create_with_is_default_key(self, + version, + admin): + # is_default is false + fake_type = copy.deepcopy(GROUP_TYPE_1) + fake_type['share_types'] = [{'share_type_id': SHARE_TYPE_ID}] + self.mock_object(share_group_types, 'create') + self.mock_object( + share_group_types, 'get_by_name', + mock.Mock(return_value=fake_type)) + req = fake_request('/v2/fake/share-group-types', + version=version, + admin=admin) + fake_body = {'share_group_type': { + 'name': GROUP_TYPE_1['name'], + 'share_types': [SHARE_TYPE_ID], + }} + res_dict = self.controller._create(req, fake_body) + if self.is_microversion_ge(version, '2.46'): + self.assertIn('is_default', res_dict['share_group_type']) + self.assertIs(False, res_dict['share_group_type']['is_default']) + else: + self.assertNotIn('is_default', res_dict['share_group_type']) + + # is_default is true + default_type_name = 'group type 3' + CONF.set_default('default_share_group_type', default_type_name) + + fake_type = copy.deepcopy(GROUP_TYPE_3) + fake_type['share_types'] = [{'share_type_id': SHARE_TYPE_ID}] + self.mock_object(share_group_types, 'create') + self.mock_object( + share_group_types, 'get_by_name', + mock.Mock(return_value=fake_type)) + req = fake_request('/v2/fake/share-group-types', + version=version, + admin=admin) + fake_body = {'share_group_type': { + 'name': GROUP_TYPE_3['name'], + 'share_types': [SHARE_TYPE_ID], + }} + res_dict = self.controller._create(req, fake_body) + if self.is_microversion_ge(version, '2.46'): + self.assertIn('is_default', res_dict['share_group_type']) + self.assertIs(True, res_dict['share_group_type']['is_default']) + else: + self.assertNotIn('is_default', res_dict['share_group_type']) + + @ddt.data(('2.45', True), ('2.45', False), + ('2.46', True), ('2.46', False)) + @ddt.unpack + def test_share_group_types_list_with_is_default_key(self, version, admin): + fake_types = { + GROUP_TYPE_1['name']: GROUP_TYPE_1, + GROUP_TYPE_2['name']: GROUP_TYPE_2, + } + self.mock_object( + share_group_types, 'get_all', + mock.Mock(return_value=fake_types)) + req = fake_request( + '/v2/fake/share-group-types?is_public=all', + version=version, + admin=admin) + res_dict = self.controller.index(req) + for res in res_dict['share_group_types']: + if self.is_microversion_ge(version, '2.46'): + self.assertIn('is_default', res) + self.assertIs(False, res['is_default']) + else: + self.assertNotIn('is_default', res) + self.assertEqual(2, len(res_dict['share_group_types'])) + + @ddt.data(('2.45', True), ('2.45', False), + ('2.46', True), ('2.46', False)) + @ddt.unpack + def test_shares_group_types_show_with_is_default_key(self, version, admin): + self.mock_object( + share_group_types, 'get', + mock.Mock(return_value=GROUP_TYPE_2)) + req = fake_request('/v2/fake/group-types/%s' % GROUP_TYPE_2['id'], + version=version, + admin=admin) + res_dict = self.controller.show(req, GROUP_TYPE_2['id']) + if self.is_microversion_ge(version, '2.46'): + self.assertIn('is_default', res_dict['share_group_type']) + self.assertIs(False, res_dict['share_group_type']['is_default']) + else: + self.assertNotIn('is_default', res_dict['share_group_type']) + class ShareGroupTypeAccessTest(test.TestCase): diff --git a/manila/tests/api/v2/test_share_types.py b/manila/tests/api/v2/test_share_types.py index b29146aced..6b7c3936c2 100644 --- a/manila/tests/api/v2/test_share_types.py +++ b/manila/tests/api/v2/test_share_types.py @@ -17,6 +17,7 @@ import datetime import ddt import mock +from oslo_config import cfg from oslo_utils import timeutils import webob @@ -32,6 +33,8 @@ from manila import test from manila.tests.api import fakes from manila.tests import fake_notifier +CONF = cfg.CONF + def stub_share_type(id): specs = { @@ -61,6 +64,34 @@ def return_share_types_get_all_types(context, search_opts=None): ) +def stub_default_name(): + return 'default_share_type' + + +def stub_default_share_type(id): + return dict( + id=id, + name=stub_default_name(), + description='description_%s' % str(id), + required_extra_specs={ + constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS: "true", + } + ) + + +def return_all_share_types(context, search_opts=None): + mock_value = dict( + share_type_1=stub_share_type(1), + share_type_2=stub_share_type(2), + share_type_3=stub_default_share_type(3) + ) + return mock_value + + +def return_default_share_type(context, search_opts=None): + return stub_default_share_type(3) + + def return_empty_share_types_get_all_types(context, search_opts=None): return {} @@ -491,6 +522,65 @@ class ShareTypesAPITest(test.TestCase): for d1, d2 in zip(expected, observed): self.assertEqual(d1['id'], d2['id']) + @ddt.data(('2.45', True), ('2.45', False), + ('2.46', True), ('2.46', False)) + @ddt.unpack + def test_share_types_create_with_is_default_key(self, version, admin): + req = fakes.HTTPRequest.blank('/v2/fake/types', + version=version, + use_admin_context=admin) + + body = make_create_body() + res_dict = self.controller.create(req, body) + if self.is_microversion_ge(version, '2.46'): + self.assertIn('is_default', res_dict['share_type']) + self.assertIs(False, res_dict['share_type']['is_default']) + else: + self.assertNotIn('is_default', res_dict['share_type']) + + @ddt.data(('2.45', True), ('2.45', False), + ('2.46', True), ('2.46', False)) + @ddt.unpack + def test_share_types_index_with_is_default_key(self, version, admin): + default_type_name = stub_default_name() + CONF.set_default("default_share_type", default_type_name) + self.mock_object(share_types, 'get_all_types', + return_all_share_types) + + req = fakes.HTTPRequest.blank('/v2/fake/types', + version=version, + use_admin_context=admin) + + res_dict = self.controller.index(req) + self.assertEqual(3, len(res_dict['share_types'])) + for res in res_dict['share_types']: + if self.is_microversion_ge(version, '2.46'): + self.assertIn('is_default', res) + expected = res['name'] == default_type_name + self.assertIs(res['is_default'], expected) + else: + self.assertNotIn('is_default', res) + + @ddt.data(('2.45', True), ('2.45', False), + ('2.46', True), ('2.46', False)) + @ddt.unpack + def test_share_types_default_with_is_default_key(self, version, admin): + default_type_name = stub_default_name() + CONF.set_default("default_share_type", default_type_name) + self.mock_object(share_types, 'get_default_share_type', + return_default_share_type) + + req = fakes.HTTPRequest.blank('/v2/fake/types/default_share_type', + version=version, + use_admin_context=admin) + + res_dict = self.controller.default(req) + if self.is_microversion_ge(version, '2.46'): + self.assertIn('is_default', res_dict['share_type']) + self.assertIs(True, res_dict['share_type']['is_default']) + else: + self.assertNotIn('is_default', res_dict['share_type']) + def generate_type(type_id, is_public): return { diff --git a/releasenotes/notes/add-is-default-e49727d276dd9bc3.yaml b/releasenotes/notes/add-is-default-e49727d276dd9bc3.yaml new file mode 100644 index 0000000000..9ecd0f18a9 --- /dev/null +++ b/releasenotes/notes/add-is-default-e49727d276dd9bc3.yaml @@ -0,0 +1,6 @@ +--- +features: + + - The share type and share group type APIs in API version 2.46 return + field "is_default" which is set to 'true' if the share type or the + share group type is the default as configured by the administrator.