Metadata for share export location

Adds set and unset commands for export location
properties within unified CLI

Depends-On: Icf096a5cbc650f02eca68d714c876eb854499b9b
Partially-implements: bp metadata-for-share-resources
Change-Id: I3878868c2359fba7aa8a49421a31a952a5776766
This commit is contained in:
Clifford 2023-07-26 07:07:25 +00:00 committed by Ashley Rodriguez
parent 0e74a7bcf2
commit 5ba65e94e4
7 changed files with 296 additions and 3 deletions

View File

@ -27,7 +27,7 @@ from manilaclient import utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
MAX_VERSION = '2.84' MAX_VERSION = '2.87'
MIN_VERSION = '2.0' MIN_VERSION = '2.0'
DEPRECATED_VERSION = '1.0' DEPRECATED_VERSION = '1.0'
_VERSIONED_METHOD_MAP = {} _VERSIONED_METHOD_MAP = {}

View File

@ -1179,8 +1179,15 @@ class ShareExportLocationShow(command.ShowOne):
share=share, share=share,
export_location=parsed_args.export_location 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): class ShareExportLocationList(command.Lister):
@ -1218,6 +1225,120 @@ class ShareExportLocationList(command.Lister):
(s, list_of_keys) for s in export_locations)) (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="<share>",
help=_('Name or ID of share')
)
parser.add_argument(
'export_location',
metavar="<export_location>",
help=_('ID of the export location')
)
parser.add_argument(
"--property",
metavar="<key=value>",
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="<share>",
help=_('Name or ID of share')
)
parser.add_argument(
'export_location',
metavar="<export_location>",
help=_('ID of the export location')
)
parser.add_argument(
"--property",
metavar="<key>",
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): class ShowShareProperties(command.ShowOne):
"""Show properties of a share""" """Show properties of a share"""
_description = _("Show share properties") _description = _("Show share properties")

View File

@ -330,6 +330,7 @@ class FakeShareExportLocation(object):
"id": "id-" + uuid.uuid4().hex, "id": "id-" + uuid.uuid4().hex,
"is_admin_only": False, "is_admin_only": False,
"preferred": False, "preferred": False,
"properties": {},
"updated_at": 'time-' + uuid.uuid4().hex, "updated_at": 'time-' + uuid.uuid4().hex,
} }

View File

@ -1910,6 +1910,148 @@ class TestShareExportLocationList(TestShare):
self.assertCountEqual(self.values, data) 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): class TestShowShareProperties(TestShare):
properties = { properties = {

View File

@ -27,9 +27,11 @@ class ShareExportLocation(base.Resource):
return self._info[key] return self._info[key]
class ShareExportLocationManager(base.ManagerWithFind): class ShareExportLocationManager(base.MetadataCapableManager):
"""Manage :class:`ShareExportLocation` resources.""" """Manage :class:`ShareExportLocation` resources."""
resource_class = ShareExportLocation resource_class = ShareExportLocation
resource_path = '/shares'
subresource_path = '/export_locations'
@api_versions.wraps("2.9") @api_versions.wraps("2.9")
def list(self, share, search_opts=None): 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" % { "/shares/%(share_id)s/export_locations/%(export_location_id)s" % {
"share_id": share_id, "share_id": share_id,
"export_location_id": export_location_id}, "export_location") "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)

View File

@ -0,0 +1,5 @@
---
features:
- |
Adds support to set and unset share export location properties
(only with the OpenStackClient).

View File

@ -52,6 +52,8 @@ openstack.share.v2 =
share_migration_show = manilaclient.osc.v2.share:ShareMigrationShow share_migration_show = manilaclient.osc.v2.share:ShareMigrationShow
share_export_location_show = manilaclient.osc.v2.share:ShareExportLocationShow share_export_location_show = manilaclient.osc.v2.share:ShareExportLocationShow
share_export_location_list = manilaclient.osc.v2.share:ShareExportLocationList 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_properties_show = manilaclient.osc.v2.share:ShowShareProperties
share_restore = manilaclient.osc.v2.share:RestoreShare share_restore = manilaclient.osc.v2.share:RestoreShare
share_revert = manilaclient.osc.v2.share:RevertShare share_revert = manilaclient.osc.v2.share:RevertShare