diff --git a/manilaclient/api_versions.py b/manilaclient/api_versions.py index e303dc7ef..39a11f7ef 100644 --- a/manilaclient/api_versions.py +++ b/manilaclient/api_versions.py @@ -27,7 +27,7 @@ from manilaclient import utils LOG = logging.getLogger(__name__) -MAX_VERSION = '2.84' +MAX_VERSION = '2.87' MIN_VERSION = '2.0' DEPRECATED_VERSION = '1.0' _VERSIONED_METHOD_MAP = {} diff --git a/manilaclient/osc/v2/share.py b/manilaclient/osc/v2/share.py index ce677f2f9..57f5e6359 100644 --- a/manilaclient/osc/v2/share.py +++ b/manilaclient/osc/v2/share.py @@ -1179,8 +1179,15 @@ class ShareExportLocationShow(command.ShowOne): share=share, export_location=parsed_args.export_location ) + data = export_location._info + data.update( + { + 'properties': + format_columns.DictColumn(data.pop('metadata', {})), + }, + ) - return self.dict2columns(export_location._info) + return self.dict2columns(data) class ShareExportLocationList(command.Lister): @@ -1218,6 +1225,120 @@ class ShareExportLocationList(command.Lister): (s, list_of_keys) for s in export_locations)) +class ShareExportLocationSet(command.Command): + """Set an export location property.""" + + _description = _("Set an export location property.") + + def get_parser(self, prog_name): + parser = super(ShareExportLocationSet, self).get_parser( + prog_name) + + parser.add_argument( + 'share', + metavar="", + help=_('Name or ID of share') + ) + parser.add_argument( + 'export_location', + metavar="", + help=_('ID of the export location') + ) + parser.add_argument( + "--property", + metavar="", + default={}, + action=parseractions.KeyValueAction, + help=_("Set a property to this export location " + "(repeat option to set multiple properties). " + "Available only for microversion >= 2.87."), + ) + return parser + + def take_action(self, parsed_args): + share_client = self.app.client_manager.share + share_id = apiutils.find_resource( + share_client.shares, + parsed_args.share).id + + if (parsed_args.property and + share_client.api_version < api_versions.APIVersion("2.87")): + raise exceptions.CommandError( + "Property can be specified only with manila API " + "version >= 2.87.") + + if parsed_args.property: + try: + share_client.share_export_locations.set_metadata( + share_id, + parsed_args.property, + subresource=parsed_args.export_location) + except Exception as e: + raise exceptions.CommandError(_( + "Failed to set export location property " + "'%(properties)s': %(e)s") % + {'properties': parsed_args.property, 'e': e} + ) + + +class ShareExportLocationUnset(command.Command): + """Unset a share export location property""" + _description = _("Unset a share export location property") + + def get_parser(self, prog_name): + parser = super(ShareExportLocationUnset, self).get_parser( + prog_name) + + parser.add_argument( + 'share', + metavar="", + help=_('Name or ID of share') + ) + parser.add_argument( + 'export_location', + metavar="", + help=_('ID of the export location') + ) + parser.add_argument( + "--property", + metavar="", + action='append', + help=_("Remove a property from export location " + "(repeat option to remove multiple properties). " + "Available only for microversion >= 2.87."), + ) + return parser + + def take_action(self, parsed_args): + share_client = self.app.client_manager.share + share_id = apiutils.find_resource( + share_client.shares, + parsed_args.share).id + + if (parsed_args.property and + share_client.api_version < api_versions.APIVersion("2.87")): + raise exceptions.CommandError( + "Property can be specified only with manila API " + "version >= 2.87.") + + if parsed_args.property: + result = 0 + for key in parsed_args.property: + try: + share_client.share_export_locations.delete_metadata( + share_id, [key], + subresource=parsed_args.export_location) + except Exception as e: + result += 1 + LOG.error("Failed to unset export location property " + "'%(key)s': %(e)s", {'key': key, 'e': e}) + if result > 0: + total = len(parsed_args.property) + raise exceptions.CommandError( + f"{result} of {total} export location properties failed " + f"to be unset.") + + class ShowShareProperties(command.ShowOne): """Show properties of a share""" _description = _("Show share properties") diff --git a/manilaclient/tests/unit/osc/v2/fakes.py b/manilaclient/tests/unit/osc/v2/fakes.py index 1b0225614..15bc79e1d 100644 --- a/manilaclient/tests/unit/osc/v2/fakes.py +++ b/manilaclient/tests/unit/osc/v2/fakes.py @@ -330,6 +330,7 @@ class FakeShareExportLocation(object): "id": "id-" + uuid.uuid4().hex, "is_admin_only": False, "preferred": False, + "properties": {}, "updated_at": 'time-' + uuid.uuid4().hex, } diff --git a/manilaclient/tests/unit/osc/v2/test_share.py b/manilaclient/tests/unit/osc/v2/test_share.py index 080479b05..e05d7a564 100644 --- a/manilaclient/tests/unit/osc/v2/test_share.py +++ b/manilaclient/tests/unit/osc/v2/test_share.py @@ -1910,6 +1910,148 @@ class TestShareExportLocationList(TestShare): self.assertCountEqual(self.values, data) +class TestExportLocationSet(TestShare): + + def setUp(self): + super(TestExportLocationSet, self).setUp() + + self._share = manila_fakes.FakeShare.create_one_share( + methods={"set_metadata": None} + ) + self.shares_mock.get.return_value = self._share + + self._export_location = ( + manila_fakes.FakeShareExportLocation.create_one_export_location( + {'fake_share_instance_id': self._share.id})) + + self.export_locations_mock.get.return_value = ( + self._export_location + ) + + self.cmd = osc_shares.ShareExportLocationSet(self.app, None) + + def test_share_set_export_location_property(self): + self.app.client_manager.share.api_version = api_versions.APIVersion( + '2.87' + ) + arglist = [ + self._share.id, + self._export_location.id, + '--property', 'Bobcat=manila', + + ] + verifylist = [ + ('share', self._share.id), + ('export_location', self._export_location.id), + ('property', {'Bobcat': 'manila'}), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + self.export_locations_mock.set_metadata.assert_called_once_with( + self._share.id, {'Bobcat': 'manila'}, + subresource=self._export_location.id) + + def test_share_set_export_location_property_exception(self): + self.app.client_manager.share.api_version = api_versions.APIVersion( + '2.87' + ) + arglist = [ + self._share.id, + self._export_location.id, + '--property', 'key=', + ] + verifylist = [ + ('share', self._share.id), + ('export_location', self._export_location.id), + ('property', {'key': ''}), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.export_locations_mock.set_metadata.assert_called_once_with( + self._share.id, {'key': ''}, + subresource=self._export_location.id) + + self.export_locations_mock.set_metadata.side_effect = ( + exceptions.BadRequest) + self.assertRaises( + osc_exceptions.CommandError, self.cmd.take_action, + parsed_args) + + +class TestExportLocationUnset(TestShare): + + def setUp(self): + super(TestExportLocationUnset, self).setUp() + + self._share = manila_fakes.FakeShare.create_one_share( + methods={"set_metadata": None} + ) + self.shares_mock.get.return_value = self._share + + self._export_location = ( + manila_fakes.FakeShareExportLocation.create_one_export_location( + {'fake_share_instance_id': self._share.id})) + + self.export_locations_mock.get.return_value = ( + self._export_location + ) + + self.cmd = osc_shares.ShareExportLocationUnset(self.app, None) + + def test_share_unset_export_location_property(self): + self.app.client_manager.share.api_version = api_versions.APIVersion( + '2.87' + ) + arglist = [ + self._share.id, + self._export_location.id, + '--property', 'Bobcat', + + ] + verifylist = [ + ('share', self._share.id), + ('export_location', self._export_location.id), + ('property', ['Bobcat']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + self.export_locations_mock.delete_metadata.assert_called_once_with( + self._share.id, ['Bobcat'], + subresource=self._export_location.id) + + def test_share_unset_export_location_property_exception(self): + self.app.client_manager.share.api_version = api_versions.APIVersion( + '2.87' + ) + arglist = [ + self._share.id, + self._export_location.id, + '--property', 'key', + ] + verifylist = [ + ('share', self._share.id), + ('export_location', self._export_location.id), + ('property', ['key']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.export_locations_mock.delete_metadata.assert_has_calls([ + mock.call(self._share.id, ['key'], + subresource=self._export_location.id)]) + + self.export_locations_mock.delete_metadata.side_effect = ( + exceptions.NotFound) + self.assertRaises( + osc_exceptions.CommandError, self.cmd.take_action, + parsed_args) + + class TestShowShareProperties(TestShare): properties = { diff --git a/manilaclient/v2/share_export_locations.py b/manilaclient/v2/share_export_locations.py index a59b24bbf..ed716aa9a 100644 --- a/manilaclient/v2/share_export_locations.py +++ b/manilaclient/v2/share_export_locations.py @@ -27,9 +27,11 @@ class ShareExportLocation(base.Resource): return self._info[key] -class ShareExportLocationManager(base.ManagerWithFind): +class ShareExportLocationManager(base.MetadataCapableManager): """Manage :class:`ShareExportLocation` resources.""" resource_class = ShareExportLocation + resource_path = '/shares' + subresource_path = '/export_locations' @api_versions.wraps("2.9") def list(self, share, search_opts=None): @@ -47,3 +49,23 @@ class ShareExportLocationManager(base.ManagerWithFind): "/shares/%(share_id)s/export_locations/%(export_location_id)s" % { "share_id": share_id, "export_location_id": export_location_id}, "export_location") + + @api_versions.wraps('2.87') + def get_metadata(self, share, share_export_location): + return super(ShareExportLocationManager, self).get_metadata( + share, subresource=share_export_location) + + @api_versions.wraps('2.87') + def set_metadata(self, resource, metadata, subresource=None): + return super(ShareExportLocationManager, self).set_metadata( + resource, metadata, subresource=subresource) + + @api_versions.wraps('2.87') + def delete_metadata(self, resource, keys, subresource=None): + return super(ShareExportLocationManager, self).delete_metadata( + resource, keys, subresource=subresource) + + @api_versions.wraps('2.87') + def update_all_metadata(self, resource, metadata, subresource=None): + return super(ShareExportLocationManager, self).update_all_metadata( + resource, metadata, subresource=subresource) diff --git a/releasenotes/notes/add_export_location_metadata-e023dccf4c28bd76.yaml b/releasenotes/notes/add_export_location_metadata-e023dccf4c28bd76.yaml new file mode 100644 index 000000000..6f36828fa --- /dev/null +++ b/releasenotes/notes/add_export_location_metadata-e023dccf4c28bd76.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Adds support to set and unset share export location properties + (only with the OpenStackClient). diff --git a/setup.cfg b/setup.cfg index 12b79568f..a22325ae3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -52,6 +52,8 @@ openstack.share.v2 = share_migration_show = manilaclient.osc.v2.share:ShareMigrationShow share_export_location_show = manilaclient.osc.v2.share:ShareExportLocationShow share_export_location_list = manilaclient.osc.v2.share:ShareExportLocationList + share_export_location_set = manilaclient.osc.v2.share:ShareExportLocationSet + share_export_location_unset = manilaclient.osc.v2.share:ShareExportLocationUnset share_properties_show = manilaclient.osc.v2.share:ShowShareProperties share_restore = manilaclient.osc.v2.share:RestoreShare share_revert = manilaclient.osc.v2.share:RevertShare