Change-Id: I920dc4922056a807b4d25728560fb4f58cbf14c2 Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
574 lines
19 KiB
Python
574 lines
19 KiB
Python
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import logging
|
|
|
|
from osc_lib.cli import parseractions
|
|
from osc_lib.command import command
|
|
from osc_lib import exceptions
|
|
from osc_lib import utils as oscutils
|
|
|
|
from manilaclient import api_versions
|
|
from manilaclient.common._i18n import _
|
|
from manilaclient.common.apiclient import utils as apiutils
|
|
from manilaclient.osc import utils
|
|
|
|
ACCESS_RULE_ATTRIBUTES = [
|
|
'id',
|
|
'share_id',
|
|
'access_level',
|
|
'access_to',
|
|
'access_type',
|
|
'state',
|
|
'access_key',
|
|
'created_at',
|
|
'updated_at',
|
|
'properties',
|
|
]
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class ShareAccessAllow(command.ShowOne):
|
|
"""Create a new share access rule."""
|
|
|
|
_description = _("Create new share access rule")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super().get_parser(prog_name)
|
|
parser.add_argument(
|
|
'share',
|
|
metavar="<share>",
|
|
help=_('Name or ID of the NAS share to modify.'),
|
|
)
|
|
parser.add_argument(
|
|
'access_type',
|
|
metavar="<access_type>",
|
|
help=_(
|
|
'Access rule type (only "ip", "user" (user or group), '
|
|
'"cert" or "cephx" are supported).'
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
'access_to',
|
|
metavar="<access_to>",
|
|
help=_('Value that defines access.'),
|
|
)
|
|
# metadata --> properties in osc
|
|
parser.add_argument(
|
|
'--properties',
|
|
type=str,
|
|
nargs='*',
|
|
metavar='<key=value>',
|
|
help=_(
|
|
'Space separated list of key=value pairs of properties. '
|
|
'OPTIONAL: Default=None. '
|
|
'Available only for API microversion >= 2.45.'
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
'--access-level',
|
|
metavar="<access_level>",
|
|
type=str,
|
|
default=None,
|
|
choices=['rw', 'ro'],
|
|
help=_(
|
|
'Share access level ("rw" and "ro" access levels '
|
|
'are supported). Defaults to rw.'
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--wait",
|
|
action='store_true',
|
|
help=_("Wait for share access rule creation."),
|
|
)
|
|
parser.add_argument(
|
|
"--lock-visibility",
|
|
action='store_true',
|
|
default=False,
|
|
help=_(
|
|
"Whether the sensitive fields of the access rule redacted "
|
|
"to other users. Only available with API version >= 2.82."
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--lock-deletion",
|
|
action='store_true',
|
|
default=False,
|
|
help=_(
|
|
"When enabled, a 'delete' lock will be placed against the "
|
|
"rule and the rule cannot be deleted while the lock "
|
|
"exists. Only available with API version >= 2.82."
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
'--lock-reason',
|
|
metavar="<lock_reason>",
|
|
type=str,
|
|
default=None,
|
|
help=_(
|
|
"Reason for locking the access rule. Should only be "
|
|
"provided alongside a deletion or visibility lock. "
|
|
"Only available with API version >= 2.82."
|
|
),
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
share_client = self.app.client_manager.share
|
|
|
|
share = apiutils.find_resource(share_client.shares, parsed_args.share)
|
|
lock_kwargs = {}
|
|
if parsed_args.lock_visibility:
|
|
lock_kwargs['lock_visibility'] = parsed_args.lock_visibility
|
|
if parsed_args.lock_deletion:
|
|
lock_kwargs['lock_deletion'] = parsed_args.lock_deletion
|
|
if parsed_args.lock_reason:
|
|
lock_kwargs['lock_reason'] = parsed_args.lock_reason
|
|
|
|
if lock_kwargs and share_client.api_version < api_versions.APIVersion(
|
|
"2.82"
|
|
):
|
|
raise exceptions.CommandError(
|
|
'Restricted access rules are only available starting '
|
|
'from API version 2.82.'
|
|
)
|
|
|
|
if lock_kwargs.get('lock_reason', None) and not (
|
|
lock_kwargs.get('lock_visibility', None)
|
|
or lock_kwargs.get('lock_deletion', None)
|
|
):
|
|
raise exceptions.CommandError(
|
|
'Lock reason can only be set while locking the deletion or '
|
|
'visibility.'
|
|
)
|
|
|
|
properties = {}
|
|
if parsed_args.properties:
|
|
if share_client.api_version >= api_versions.APIVersion("2.45"):
|
|
properties = utils.extract_properties(parsed_args.properties)
|
|
else:
|
|
raise exceptions.CommandError(
|
|
"Adding properties to access rules is supported only "
|
|
"with API microversion 2.45 and beyond"
|
|
)
|
|
try:
|
|
share_access_rule = share.allow(
|
|
access_type=parsed_args.access_type,
|
|
access=parsed_args.access_to,
|
|
access_level=parsed_args.access_level,
|
|
metadata=properties,
|
|
**lock_kwargs,
|
|
)
|
|
if parsed_args.wait:
|
|
if not oscutils.wait_for_status(
|
|
status_f=share_client.share_access_rules.get,
|
|
res_id=share_access_rule['id'],
|
|
status_field='state',
|
|
):
|
|
LOG.error(_("ERROR: Share access rule is in error state."))
|
|
|
|
share_access_rule = oscutils.find_resource(
|
|
share_client.share_access_rules, share_access_rule['id']
|
|
)._info
|
|
share_access_rule.update(
|
|
{
|
|
'properties': utils.format_properties(
|
|
share_access_rule.pop('metadata', {})
|
|
)
|
|
}
|
|
)
|
|
return (
|
|
ACCESS_RULE_ATTRIBUTES,
|
|
oscutils.get_dict_properties(
|
|
share_access_rule, ACCESS_RULE_ATTRIBUTES
|
|
),
|
|
)
|
|
except Exception as e:
|
|
raise exceptions.CommandError(
|
|
f"Failed to create access to share '{share}': {e}"
|
|
)
|
|
|
|
|
|
class ShareAccessDeny(command.Command):
|
|
"""Delete a share access rule."""
|
|
|
|
_description = _("Delete a share access rule")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super().get_parser(prog_name)
|
|
parser.add_argument(
|
|
'share',
|
|
metavar="<share>",
|
|
help=_('Name or ID of the NAS share to modify.'),
|
|
)
|
|
parser.add_argument(
|
|
'id',
|
|
metavar="<id>",
|
|
help=_('ID of the access rule to be deleted.'),
|
|
)
|
|
parser.add_argument(
|
|
"--wait",
|
|
action='store_true',
|
|
default=False,
|
|
help=_("Wait for share access rule deletion"),
|
|
)
|
|
parser.add_argument(
|
|
"--unrestrict",
|
|
action='store_true',
|
|
default=False,
|
|
help=_(
|
|
"Seek access rule deletion despite restrictions. Only "
|
|
"available with API version >= 2.82."
|
|
),
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
share_client = self.app.client_manager.share
|
|
|
|
share = apiutils.find_resource(share_client.shares, parsed_args.share)
|
|
kwargs = {}
|
|
if parsed_args.unrestrict:
|
|
if share_client.api_version < api_versions.APIVersion("2.82"):
|
|
raise exceptions.CommandError(
|
|
'Restricted access rules are only available starting from '
|
|
'API version 2.82.'
|
|
)
|
|
kwargs['unrestrict'] = True
|
|
|
|
try:
|
|
share.deny(parsed_args.id, **kwargs)
|
|
if parsed_args.wait:
|
|
if not oscutils.wait_for_delete(
|
|
manager=share_client.share_access_rules,
|
|
res_id=parsed_args.id,
|
|
):
|
|
raise Exception('timed out while waiting for deletion')
|
|
except Exception as e:
|
|
msg = _(
|
|
"Failed to delete share access rule %(id)s for share "
|
|
"%(share)s: %(e)s"
|
|
)
|
|
raise exceptions.CommandError(
|
|
msg
|
|
% {'id': parsed_args.id, 'share': parsed_args.share, 'e': e}
|
|
)
|
|
|
|
|
|
class ListShareAccess(command.Lister):
|
|
"""List share access rules."""
|
|
|
|
_description = _("List share access rule")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super().get_parser(prog_name)
|
|
parser.add_argument(
|
|
'share', metavar="<share>", help=_('Name or ID of the share.')
|
|
)
|
|
|
|
# metadata --> properties in osc
|
|
parser.add_argument(
|
|
'--properties',
|
|
type=str,
|
|
nargs='*',
|
|
metavar='<key=value>',
|
|
default=None,
|
|
help=_(
|
|
'Filters results by properties (key=value). '
|
|
'OPTIONAL: Default=None. '
|
|
'Available only for API microversion >= 2.45'
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--access-type",
|
|
metavar="<access_type>",
|
|
default=None,
|
|
help=_("Filter access rules by the access type."),
|
|
)
|
|
parser.add_argument(
|
|
"--access-key",
|
|
metavar="<access_key>",
|
|
default=None,
|
|
help=_("Filter access rules by the access key."),
|
|
)
|
|
parser.add_argument(
|
|
"--access-to",
|
|
metavar="<access_to>",
|
|
default=None,
|
|
help=_("Filter access rules by the access to field."),
|
|
)
|
|
parser.add_argument(
|
|
"--access-level",
|
|
metavar="<access_level>",
|
|
default=None,
|
|
help=_("Filter access rules by the access level."),
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
share_client = self.app.client_manager.share
|
|
|
|
share = apiutils.find_resource(share_client.shares, parsed_args.share)
|
|
access_type = parsed_args.access_type
|
|
access_key = parsed_args.access_key
|
|
access_to = parsed_args.access_to
|
|
access_level = parsed_args.access_level
|
|
|
|
extended_filter_keys = {
|
|
'access_type': access_type,
|
|
'access_key': access_key,
|
|
'access_to': access_to,
|
|
'access_level': access_level,
|
|
}
|
|
|
|
if any(
|
|
extended_filter_keys.values()
|
|
) and share_client.api_version < api_versions.APIVersion("2.82"):
|
|
raise exceptions.CommandError(
|
|
'Filtering access rules by access_type, access_key, access_to '
|
|
'and access_level is available starting from API version '
|
|
'2.82.'
|
|
)
|
|
|
|
search_opts = {}
|
|
if share_client.api_version >= api_versions.APIVersion("2.82"):
|
|
for filter_key, filter_value in extended_filter_keys.items():
|
|
if filter_value:
|
|
search_opts[filter_key] = filter_value
|
|
|
|
if share_client.api_version >= api_versions.APIVersion("2.45"):
|
|
if parsed_args.properties:
|
|
search_opts = {
|
|
'metadata': utils.extract_properties(
|
|
parsed_args.properties
|
|
)
|
|
}
|
|
access_list = share_client.share_access_rules.access_list(
|
|
share, search_opts
|
|
)
|
|
elif parsed_args.properties:
|
|
raise exceptions.CommandError(
|
|
"Filtering access rules by properties is supported only "
|
|
"with API microversion 2.45 and beyond."
|
|
)
|
|
else:
|
|
access_list = share.access_list()
|
|
|
|
list_of_keys = [
|
|
'ID',
|
|
'Access Type',
|
|
'Access To',
|
|
'Access Level',
|
|
'State',
|
|
]
|
|
|
|
if share_client.api_version >= api_versions.APIVersion("2.21"):
|
|
list_of_keys.append('Access Key')
|
|
|
|
if share_client.api_version >= api_versions.APIVersion("2.33"):
|
|
list_of_keys.append('Created At')
|
|
list_of_keys.append('Updated At')
|
|
|
|
values = (
|
|
oscutils.get_item_properties(a, list_of_keys) for a in access_list
|
|
)
|
|
|
|
return (list_of_keys, values)
|
|
|
|
|
|
class ShowShareAccess(command.ShowOne):
|
|
"""Display a share access rule."""
|
|
|
|
_description = _(
|
|
"Display a share access rule. "
|
|
"Available for API microversion 2.45 and higher"
|
|
)
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super().get_parser(prog_name)
|
|
parser.add_argument(
|
|
'access_id',
|
|
metavar="<access_id>",
|
|
help=_('ID of the NAS share access rule.'),
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
share_client = self.app.client_manager.share
|
|
|
|
if share_client.api_version >= api_versions.APIVersion("2.45"):
|
|
access_rule = share_client.share_access_rules.get(
|
|
parsed_args.access_id
|
|
)
|
|
access_rule._info.update(
|
|
{
|
|
'properties': utils.format_properties(
|
|
access_rule._info.pop('metadata', {})
|
|
)
|
|
}
|
|
)
|
|
return (
|
|
ACCESS_RULE_ATTRIBUTES,
|
|
oscutils.get_dict_properties(
|
|
access_rule._info, ACCESS_RULE_ATTRIBUTES
|
|
),
|
|
)
|
|
else:
|
|
raise exceptions.CommandError(
|
|
"Displaying share access rule details is only available "
|
|
"with API microversion 2.45 and higher."
|
|
)
|
|
|
|
|
|
class SetShareAccess(command.Command):
|
|
"""Set properties to share access rule."""
|
|
|
|
_description = _(
|
|
"Set properties to share access rule. "
|
|
"Available for API microversion 2.45 and higher"
|
|
)
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super().get_parser(prog_name)
|
|
parser.add_argument(
|
|
'access_id',
|
|
metavar="<access_id>",
|
|
help=_('ID of the NAS share access rule.'),
|
|
)
|
|
# metadata --> properties in osc
|
|
parser.add_argument(
|
|
'--property',
|
|
metavar='<key=value>',
|
|
default={},
|
|
action=parseractions.KeyValueAction,
|
|
help=_(
|
|
'Set a property to this share access rule. '
|
|
'(Repeat option to set multiple properties) '
|
|
'Available only for API microversion >= 2.45.'
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--access-level",
|
|
metavar="<access_level>",
|
|
default=None,
|
|
choices=['rw', 'ro'],
|
|
help=_(
|
|
'Share access level ("rw" and "ro" access levels '
|
|
'are supported) to set.'
|
|
),
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
share_client = self.app.client_manager.share
|
|
|
|
if (
|
|
parsed_args.property
|
|
and share_client.api_version < api_versions.APIVersion("2.45")
|
|
):
|
|
raise exceptions.CommandError(
|
|
"Setting properties to access rule is supported only "
|
|
"with API microversion 2.45 and higher"
|
|
)
|
|
|
|
if (
|
|
parsed_args.access_level
|
|
and share_client.api_version < api_versions.APIVersion("2.88")
|
|
):
|
|
raise exceptions.CommandError(
|
|
"Setting access level to access rule is supported only "
|
|
"with API microversion 2.88 and higher"
|
|
)
|
|
|
|
access_rule = share_client.share_access_rules.get(
|
|
parsed_args.access_id
|
|
)
|
|
if parsed_args.property:
|
|
try:
|
|
share_client.share_access_rules.set_metadata(
|
|
access_rule, parsed_args.property
|
|
)
|
|
except Exception as e:
|
|
raise exceptions.CommandError(
|
|
"Failed to set properties to share access rule with ID "
|
|
f"'{access_rule.id}': {e}"
|
|
)
|
|
|
|
if parsed_args.access_level:
|
|
try:
|
|
share_client.share_access_rules.set_access_level(
|
|
access_rule, parsed_args.access_level
|
|
)
|
|
except Exception as e:
|
|
raise exceptions.CommandError(
|
|
"Failed to set access level to share access rule with ID "
|
|
f"'{access_rule.id}': {e}"
|
|
)
|
|
|
|
|
|
class UnsetShareAccess(command.Command):
|
|
"""Unset properties of share access rule."""
|
|
|
|
_description = _(
|
|
"Unset properties of share access rule. "
|
|
"Available for API microversion 2.45 and higher"
|
|
)
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super().get_parser(prog_name)
|
|
parser.add_argument(
|
|
'access_id',
|
|
metavar="<access_id>",
|
|
help=_('ID of the NAS share access rule.'),
|
|
)
|
|
# metadata --> properties in osc
|
|
parser.add_argument(
|
|
'--property',
|
|
metavar='<key>',
|
|
action='append',
|
|
help=_(
|
|
'Remove property from share access rule. '
|
|
'(Repeat option to remove multiple properties) '
|
|
'Available only for API microversion >= 2.45.'
|
|
),
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
share_client = self.app.client_manager.share
|
|
|
|
if share_client.api_version >= api_versions.APIVersion("2.45"):
|
|
access_rule = share_client.share_access_rules.get(
|
|
parsed_args.access_id
|
|
)
|
|
if parsed_args.property:
|
|
try:
|
|
share_client.share_access_rules.unset_metadata(
|
|
access_rule, parsed_args.property
|
|
)
|
|
except Exception as e:
|
|
raise exceptions.CommandError(
|
|
"Failed to unset properties for share access rule "
|
|
f"with ID '{access_rule.id}': {e}"
|
|
)
|
|
else:
|
|
raise exceptions.CommandError(
|
|
"Please specify '--property <key>' to unset a property. "
|
|
)
|
|
|
|
else:
|
|
raise exceptions.CommandError(
|
|
"Option to unset properties of access rule is available only "
|
|
"for API microversion 2.45 and higher"
|
|
)
|