Python bindings for Manila
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

6045 lines
188 KiB

# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# 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.
from operator import xor
import os
import re
import sys
import time
from oslo_utils import strutils
import six
from manilaclient import api_versions
from manilaclient.common.apiclient import utils as apiclient_utils
from manilaclient.common import cliutils
from manilaclient.common import constants
from manilaclient import exceptions
def _wait_for_resource_status(cs,
resource,
expected_status,
resource_type='share',
status_attr='status',
poll_timeout=900,
poll_interval=2):
"""Waiter for resource status changes
:param cs: command shell control
:param expected_status: a string or a list of strings containing expected
states to wait for
:param resource_type: 'share', 'snapshot', 'share_replica', 'share_group',
or 'share_group_snapshot'
:param status_attr: 'status', 'task_state', 'access_rules_status' or any
other status field that is expected to have the "expected_status"
:param poll_timeout: how long to wait for in seconds
:param poll_interval: how often to try in seconds
"""
find_resource = {
'share': _find_share,
'snapshot': _find_share_snapshot,
'share_replica': _find_share_replica,
'share_group': _find_share_group,
'share_group_snapshot': _find_share_group_snapshot,
}
print_resource = {
'share': _print_share,
'snapshot': _print_share_snapshot,
'share_replica': _print_share_replica,
'share_group': _print_share_group,
'share_group_snapshot': _print_share_group_snapshot,
}
expected_status = expected_status or ('available', )
if not isinstance(expected_status, (list, tuple, set)):
expected_status = (expected_status, )
time_elapsed = 0
timeout_message = ("%(resource_type)s %(resource)s did not reach "
"%(expected_states)s within %(seconds)d seconds.")
error_message = ("%(resource_type)s %(resource)s has reached a failed "
"state.")
deleted_message = ("%(resource_type)s %(resource)s has been successfully "
"deleted.")
message_payload = {
'resource_type': resource_type.capitalize(),
'resource': resource.id,
}
not_found_regex = "no %s .* exists" % resource_type
while True:
if time_elapsed > poll_timeout:
print_resource[resource_type](cs, resource)
message_payload.update({'expected_states': expected_status,
'seconds': poll_timeout})
raise exceptions.TimeoutException(
message=timeout_message % message_payload)
try:
resource = find_resource[resource_type](cs, resource.id)
except exceptions.CommandError as e:
if (re.search(not_found_regex, str(e), flags=re.IGNORECASE)
and 'deleted' in expected_status):
print(deleted_message % message_payload)
break
else:
raise e
if getattr(resource, status_attr) in expected_status:
break
elif 'error' in getattr(resource, status_attr):
print_resource[resource_type](cs, resource)
raise exceptions.ResourceInErrorState(
message=error_message % message_payload)
time.sleep(poll_interval)
time_elapsed += poll_interval
return resource
def _find_share(cs, share):
"""Get a share by ID."""
return apiclient_utils.find_resource(cs.shares, share)
@api_versions.wraps("1.0", "2.8")
def _print_share(cs, share):
info = share._info.copy()
info.pop('links', None)
# NOTE(vponomaryov): remove deprecated single field 'export_location' and
# leave only list field 'export_locations'. Also, transform the latter to
# text with new line separators to make it pretty in CLI.
# It will look like following:
# +-------------------+--------------------------------------------+
# | Property | Value |
# +-------------------+--------------------------------------------+
# | status | available |
# | export_locations | 1.2.3.4:/f/o/o |
# | | 5.6.7.8:/b/a/r |
# | | 9.10.11.12:/q/u/u/z |
# | id | d778d2ee-b6bb-4c5f-9f5d-6f3057d549b1 |
# | size | 1 |
# | share_proto | NFS |
# +-------------------+--------------------------------------------+
if info.get('export_locations'):
info.pop('export_location', None)
info['export_locations'] = "\n".join(info['export_locations'])
# No need to print both volume_type and share_type to CLI
if 'volume_type' in info and 'share_type' in info:
info.pop('volume_type', None)
cliutils.print_dict(info)
@api_versions.wraps("2.9") # noqa
def _print_share(cs, share): # noqa
info = share._info.copy()
info.pop('links', None)
# NOTE(vponomaryov): remove deprecated single field 'export_location' and
# leave only list field 'export_locations'. Also, transform the latter to
# text with new line separators to make it pretty in CLI.
# It will look like following:
# +-------------------+--------------------------------------------+
# | Property | Value |
# +-------------------+--------------------------------------------+
# | status | available |
# | export_locations | |
# | | uuid = FOO-UUID |
# | | path = 5.6.7.8:/foo/export/location/path |
# | | |
# | | uuid = BAR-UUID |
# | | path = 5.6.7.8:/bar/export/location/path |
# | | |
# | id | d778d2ee-b6bb-4c5f-9f5d-6f3057d549b1 |
# | size | 1 |
# | share_proto | NFS |
# +-------------------+--------------------------------------------+
if info.get('export_locations'):
info['export_locations'] = (
cliutils.transform_export_locations_to_string_view(
info['export_locations']))
# No need to print both volume_type and share_type to CLI
if 'volume_type' in info and 'share_type' in info:
info.pop('volume_type', None)
cliutils.print_dict(info)
def _wait_for_share_status(cs, share, expected_status='available'):
return _wait_for_resource_status(
cs, share, expected_status, resource_type='share')
def _find_share_instance(cs, instance):
"""Get a share instance by ID."""
return apiclient_utils.find_resource(cs.share_instances, instance)
def _print_type_show(stype, default_share_type=None):
if hasattr(stype, 'is_default'):
is_default = 'YES' if stype.is_default else 'NO'
elif default_share_type:
is_default = 'YES' if stype.id == default_share_type.id else 'NO'
else:
is_default = 'NO'
stype_dict = {
'id': stype.id,
'name': stype.name,
'visibility': _is_share_type_public(stype),
'is_default': is_default,
'description': stype.description,
'required_extra_specs': _print_type_required_extra_specs(stype),
'optional_extra_specs': _print_type_optional_extra_specs(stype),
}
cliutils.print_dict(stype_dict)
@api_versions.wraps("1.0", "2.8")
def _print_share_instance(cs, instance):
info = instance._info.copy()
info.pop('links', None)
cliutils.print_dict(info)
@api_versions.wraps("2.9") # noqa
def _print_share_instance(cs, instance): # noqa
info = instance._info.copy()
info.pop('links', None)
if info.get('export_locations'):
info['export_locations'] = (
cliutils.transform_export_locations_to_string_view(
info['export_locations']))
cliutils.print_dict(info)
def _find_share_replica(cs, replica):
"""Get a replica by ID."""
return apiclient_utils.find_resource(cs.share_replicas, replica)
@api_versions.wraps("2.11", "2.46")
def _print_share_replica(cs, replica):
info = replica._info.copy()
info.pop('links', None)
cliutils.print_dict(info)
@api_versions.wraps("2.47") # noqa
def _print_share_replica(cs, replica): # noqa
info = replica._info.copy()
info.pop('links', None)
if info.get('export_locations'):
info['export_locations'] = (
cliutils.transform_export_locations_to_string_view(
info['export_locations']))
cliutils.print_dict(info)
@api_versions.wraps("2.31")
def _find_share_group(cs, share_group):
"""Get a share group ID."""
return apiclient_utils.find_resource(cs.share_groups, share_group)
def _print_share_group(cs, share_group):
info = share_group._info.copy()
info.pop('links', None)
if info.get('share_types'):
info['share_types'] = "\n".join(info['share_types'])
cliutils.print_dict(info)
@api_versions.wraps("2.31")
def _find_share_group_snapshot(cs, share_group_snapshot):
"""Get a share group snapshot by name or ID."""
return apiclient_utils.find_resource(
cs.share_group_snapshots, share_group_snapshot)
def _print_share_group_snapshot(cs, share_group_snapshot):
info = share_group_snapshot._info.copy()
info.pop('links', None)
info.pop('members', None)
cliutils.print_dict(info)
def _print_share_group_snapshot_members(cs, share_group_snapshot):
info = share_group_snapshot._info.copy()
cliutils.print_dict(info.get('members', {}))
def _find_share_snapshot(cs, snapshot):
"""Get a snapshot by ID."""
return apiclient_utils.find_resource(cs.share_snapshots, snapshot)
def _print_share_snapshot(cs, snapshot):
info = snapshot._info.copy()
info.pop('links', None)
if info.get('export_locations'):
info['export_locations'] = (
cliutils.transform_export_locations_to_string_view(
info['export_locations']))
cliutils.print_dict(info)
def _quota_set_pretty_show(quotas):
"""Convert quotas object to dict and display."""
new_quotas = {}
for quota_k, quota_v in sorted(quotas.to_dict().items()):
if isinstance(quota_v, dict):
quota_v = '\n'.join(
['%s = %s' % (k, v) for k, v in sorted(quota_v.items())])
new_quotas[quota_k] = quota_v
cliutils.print_dict(new_quotas)
def _find_share_snapshot_instance(cs, snapshot_instance):
"""Get a share snapshot instance by ID."""
return apiclient_utils.find_resource(
cs.share_snapshot_instances, snapshot_instance)
def _find_share_network(cs, share_network):
"""Get a share network by ID or name."""
return apiclient_utils.find_resource(cs.share_networks, share_network)
def _find_security_service(cs, security_service):
"""Get a security service by ID or name."""
return apiclient_utils.find_resource(cs.security_services,
security_service)
def _find_share_server(cs, share_server):
"""Get a share server by ID."""
return apiclient_utils.find_resource(cs.share_servers, share_server)
def _find_message(cs, message):
"""Get a message by ID."""
return apiclient_utils.find_resource(cs.messages, message)
def _translate_keys(collection, convert):
for item in collection:
keys = item.__dict__
for from_key, to_key in convert:
if from_key in keys and to_key not in keys:
setattr(item, to_key, item._info[from_key])
def _extract_metadata(args):
return _extract_key_value_options(args, 'metadata')
def _extract_extra_specs(args):
return _extract_key_value_options(args, 'extra_specs')
def _extract_group_specs(args):
return _extract_key_value_options(args, 'group_specs')
def _extract_key_value_options(args, option_name):
result_dict = {}
duplicate_options = []
options = getattr(args, option_name, None)
if options:
for option in options:
# unset doesn't require a val, so we have the if/else
if '=' in option:
(key, value) = option.split('=', 1)
else:
key = option
value = None
if key not in result_dict:
result_dict[key] = value
else:
duplicate_options.append(key)
if len(duplicate_options) > 0:
duplicate_str = ', '.join(duplicate_options)
msg = "Following options were duplicated: %s" % duplicate_str
raise exceptions.CommandError(msg)
return result_dict
def _split_columns(columns, title=True):
if title:
list_of_keys = list(map(lambda x: x.strip().title(),
columns.split(",")))
else:
list_of_keys = list(map(lambda x: x.strip().lower(),
columns.split(",")))
return list_of_keys
@api_versions.wraps("2.0")
def do_api_version(cs, args):
"""Display the API version information."""
columns = ['ID', 'Status', 'Version', 'Min_version']
column_labels = ['ID', 'Status', 'Version', 'Minimum Version']
response = cs.services.server_api_version()
cliutils.print_list(response, columns, field_labels=column_labels)
def do_endpoints(cs, args):
"""Discover endpoints that get returned from the authenticate services."""
catalog = cs.keystone_client.service_catalog.catalog
for e in catalog.get('serviceCatalog', catalog.get('catalog')):
cliutils.print_dict(e['endpoints'][0], e['name'])
def do_credentials(cs, args):
"""Show user credentials returned from auth."""
catalog = cs.keystone_client.service_catalog.catalog
cliutils.print_dict(catalog['user'], "User Credentials")
if not catalog['version'] == 'v3':
data = catalog['token']
else:
data = {
'issued_at': catalog['issued_at'],
'expires': catalog['expires_at'],
'id': catalog['auth_token'],
'audit_ids': catalog['audit_ids'],
'tenant': catalog['project'],
}
cliutils.print_dict(data, "Token")
_quota_resources = [
'shares',
'snapshots',
'gigabytes',
'snapshot_gigabytes',
'share_networks',
'share_replicas',
'replica_gigabytes'
]
def _quota_class_update(manager, identifier, args):
updates = {}
for resource in _quota_resources:
val = getattr(args, resource, None)
if val is not None:
updates[resource] = val
if updates:
manager.update(identifier, **updates)
@cliutils.arg(
'--tenant-id', '--tenant',
'--project', '--project-id',
action='single_alias',
dest='project_id',
metavar='<project-id>',
default=None,
help='ID of project to list the quotas for.')
@cliutils.arg(
'--user-id',
metavar='<user-id>',
default=None,
help="ID of user to list the quotas for. Optional. "
"Mutually exclusive with '--share-type'.")
@cliutils.arg(
'--share-type',
'--share_type',
metavar='<share-type>',
type=str,
default=None,
action='single_alias',
help="UUID or name of a share type to set the quotas for. Optional. "
"Mutually exclusive with '--user-id'. "
"Available only for microversion >= 2.39")
@cliutils.arg(
'--detail',
action='store_true',
help='Optional flag to indicate whether to show quota in detail. '
'Default false, available only for microversion >= 2.25.')
@api_versions.wraps("1.0")
def do_quota_show(cs, args):
"""List the quotas for a project, user or share type."""
project_id = args.project_id or cs.keystone_client.project_id
kwargs = {
"tenant_id": project_id,
"user_id": args.user_id,
"detail": args.detail,
}
if args.share_type is not None:
if cs.api_version < api_versions.APIVersion("2.39"):
raise exceptions.CommandError(
"'share type' quotas are available only starting with "
"'2.39' API microversion.")
kwargs["share_type"] = args.share_type
_quota_set_pretty_show(cs.quotas.get(**kwargs))
@cliutils.arg(
'--tenant-id', '--tenant',
'--project', '--project-id',
action='single_alias',
dest='project_id',
metavar='<project-id>',
default=None,
help='ID of the project to list the default quotas for.')
def do_quota_defaults(cs, args):
"""List the default quotas for a project."""
project = args.project_id or cs.keystone_client.project_id
_quota_set_pretty_show(cs.quotas.defaults(project))
@cliutils.arg(
'project_id',
metavar='<project-id>',
help='UUID of project to set the quotas for.')
@cliutils.arg(
'--user-id',
metavar='<user-id>',
default=None,
help="ID of a user to set the quotas for. Optional. "
"Mutually exclusive with '--share-type'.")
@cliutils.arg(
'--shares',
metavar='<shares>',
type=int,
default=None,
help='New value for the "shares" quota.')
@cliutils.arg(
'--snapshots',
metavar='<snapshots>',
type=int,
default=None,
help='New value for the "snapshots" quota.')
@cliutils.arg(
'--gigabytes',
metavar='<gigabytes>',
type=int,
default=None,
help='New value for the "gigabytes" quota.')
@cliutils.arg(
'--snapshot-gigabytes',
'--snapshot_gigabytes', # alias
metavar='<snapshot_gigabytes>',
type=int,
default=None,
action='single_alias',
help='New value for the "snapshot_gigabytes" quota.')
@cliutils.arg(
'--share-networks',
'--share_networks',
metavar='<share-networks>',
type=int,
default=None,
action='single_alias',
help='New value for the "share_networks" quota.')
@cliutils.arg(
'--share-groups', '--share_groups', '--groups',
metavar='<share_groups>',
type=int,
default=None,
action='single_alias',
help='New value for the "share_groups" quota.')
@cliutils.arg(
'--share-group-snapshots', '--share_group_snapshots',
'--group-snapshots', '--group_snapshots',
metavar='<share_group_snapshots>',
type=int,
default=None,
action='single_alias',
help='New value for the "share_group_snapshots" quota.')
@cliutils.arg(
'--share-type',
'--share_type',
metavar='<share-type>',
type=str,
default=None,
action='single_alias',
help="UUID or name of a share type to set the quotas for. Optional. "
"Mutually exclusive with '--user-id'. "
"Available only for microversion >= 2.39")
@cliutils.arg(
'--share-replicas',
'--share_replicas',
'--replicas',
metavar='<share-replicas>',
type=int,
default=None,
help='New value for the "share_replicas" quota. Available only for '
'microversion >= 2.53')
@cliutils.arg(
'--replica-gigabytes',
'--replica_gigabytes',
metavar='<replica-gigabytes>',
type=int,
default=None,
help='New value for the "replica_gigabytes" quota. Available only for '
'microversion >= 2.53')
@cliutils.arg(
'--force',
dest='force',
action="store_true",
default=None,
help='Whether force update the quota even if the already used '
'and reserved exceeds the new quota.')
@api_versions.wraps("1.0")
def do_quota_update(cs, args):
"""Update the quotas for a project/user and/or share type (Admin only)."""
kwargs = {
"tenant_id": args.project_id,
"user_id": args.user_id,
"shares": args.shares,
"gigabytes": args.gigabytes,
"snapshots": args.snapshots,
"snapshot_gigabytes": args.snapshot_gigabytes,
"share_networks": args.share_networks,
"force": args.force,
}
if args.share_type is not None:
if cs.api_version < api_versions.APIVersion("2.39"):
raise exceptions.CommandError(
"'share type' quotas are available only starting with "
"'2.39' API microversion.")
kwargs["share_type"] = args.share_type
if args.share_groups is not None or args.share_group_snapshots is not None:
if cs.api_version < api_versions.APIVersion("2.40"):
raise exceptions.CommandError(
"'share group' quotas are available only starting with "
"'2.40' API microversion.")
elif args.share_type is not None:
raise exceptions.CommandError(
"Share type quotas cannot be used to constrain share groups.")
kwargs["share_groups"] = args.share_groups
kwargs["share_group_snapshots"] = args.share_group_snapshots
if args.share_replicas is not None or args.replica_gigabytes is not None:
if cs.api_version < api_versions.APIVersion("2.53"):
raise exceptions.CommandError(
"'share replica' quotas are available only starting with "
"'2.53' API microversion.")
kwargs["share_replicas"] = args.share_replicas
kwargs["replica_gigabytes"] = args.replica_gigabytes
cs.quotas.update(**kwargs)
@cliutils.arg(
'--tenant-id', '--tenant',
'--project', '--project-id',
action='single_alias',
dest='project_id',
metavar='<project-id>',
help='ID of the project to delete quota for.')
@cliutils.arg(
'--user-id',
metavar='<user-id>',
help="ID of user to delete quota for. Optional."
"Mutually exclusive with '--share-type'.")
@cliutils.arg(
'--share-type',
'--share_type',
metavar='<share-type>',
type=str,
default=None,
action='single_alias',
help="UUID or name of a share type to set the quotas for. Optional. "
"Mutually exclusive with '--user-id'. "
"Available only for microversion >= 2.39")
@api_versions.wraps("1.0")
def do_quota_delete(cs, args):
"""Delete quota for a project, or project/user or project/share-type.
The quota will revert back to default (Admin only).
"""
project_id = args.project_id or cs.keystone_client.project_id
kwargs = {
"tenant_id": project_id,
"user_id": args.user_id,
}
if args.share_type is not None:
if cs.api_version < api_versions.APIVersion("2.39"):
raise exceptions.CommandError(
"'share type' quotas are available only starting with "
"'2.39' API microversion.")
kwargs["share_type"] = args.share_type
cs.quotas.delete(**kwargs)
@cliutils.arg(
'class_name',
metavar='<class>',
help='Name of quota class to list the quotas for.')
def do_quota_class_show(cs, args):
"""List the quotas for a quota class."""
_quota_set_pretty_show(cs.quota_classes.get(args.class_name))
@cliutils.arg(
'class_name',
metavar='<class-name>',
help='Name of quota class to set the quotas for.')
@cliutils.arg(
'--shares',
metavar='<shares>',
type=int,
default=None,
help='New value for the "shares" quota.')
@cliutils.arg(
'--snapshots',
metavar='<snapshots>',
type=int,
default=None,
help='New value for the "snapshots" quota.')
@cliutils.arg(
'--gigabytes',
metavar='<gigabytes>',
type=int,
default=None,
help='New value for the "gigabytes" quota.')
@cliutils.arg(
'--snapshot-gigabytes',
'--snapshot_gigabytes', # alias
metavar='<snapshot_gigabytes>',
type=int,
default=None,
action='single_alias',
help='New value for the "snapshot_gigabytes" quota.')
@cliutils.arg(
'--share-networks',
'--share_networks', # alias
metavar='<share-networks>',
type=int,
default=None,
action='single_alias',
help='New value for the "share_networks" quota.')
@cliutils.arg(
'--share-replicas',
'--share_replicas', # alias
'--replicas', # alias
metavar='<share-replicas>',
type=int,
default=None,
action='single_alias',
help='New value for the "share_replicas" quota. Available only for '
'microversion >= 2.53')
@cliutils.arg(
'--replica-gigabytes',
'--replica_gigabytes', # alias
metavar='<replica-gigabytes>',
type=int,
default=None,
action='single_alias',
help='New value for the "replica_gigabytes" quota. Available only for '
'microversion >= 2.53')
def do_quota_class_update(cs, args):
"""Update the quotas for a quota class (Admin only)."""
if args.share_replicas is not None or args.replica_gigabytes is not None:
if cs.api_version < api_versions.APIVersion("2.53"):
raise exceptions.CommandError(
"'share replica' quotas are available only starting with "
"'2.53' API microversion.")
_quota_class_update(cs.quota_classes, args.class_name, args)
def do_absolute_limits(cs, args):
"""Print a list of absolute limits for a user."""
limits = cs.limits.get().absolute
columns = ['Name', 'Value']
cliutils.print_list(limits, columns)
@cliutils.arg(
'--columns',
metavar='<columns>',
type=str,
default=None,
help='Comma separated list of columns to be displayed '
'example --columns "verb,uri,value".')
def do_rate_limits(cs, args):
"""Print a list of rate limits for a user."""
limits = cs.limits.get().rate
columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available']
if args.columns is not None:
columns = _split_columns(columns=args.columns)
cliutils.print_list(limits, columns)
@cliutils.arg(
'share_protocol',
metavar='<share_protocol>',
type=str,
help='Share protocol (NFS, CIFS, CephFS, GlusterFS, HDFS or MAPRFS).')
@cliutils.arg(
'size',
metavar='<size>',
type=int,
help='Share size in GiB.')
@cliutils.arg(
'--snapshot-id',
'--snapshot_id',
metavar='<snapshot-id>',
action='single_alias',
help='Optional snapshot ID to create the share from. (Default=None)',
default=None)
@cliutils.arg(
'--name',
metavar='<name>',
help='Optional share name. (Default=None)',
default=None)
@cliutils.arg(
'--metadata',
type=str,
nargs='*',
metavar='<key=value>',
help='Metadata key=value pairs (Optional, Default=None).',
default=None)
@cliutils.arg(
'--share-network',
'--share_network',
metavar='<network-info>',
action='single_alias',
help='Optional network info ID or name.',
default=None)
@cliutils.arg(
'--description',
metavar='<description>',
help='Optional share description. (Default=None)',
default=None)
@cliutils.arg(
'--share-type', '--share_type', '--volume-type', '--volume_type',
metavar='<share-type>',
default=None,
action='single_alias',
help='Optional share type. Use of optional volume type is deprecated. '
'(Default=None)')
@cliutils.arg(
'--public',
dest='public',
action='store_true',
default=False,
help="Level of visibility for share. Defines whether other projects are "
"able to see it or not. (Default=False)")
@cliutils.arg(
'--availability-zone', '--availability_zone', '--az',
metavar='<availability-zone>',
default=None,
action='single_alias',
help='Availability zone in which share should be created.')
@cliutils.arg(
'--share-group', '--share_group', '--group',
metavar='<share-group>',
action='single_alias',
help='Optional share group name or ID in which to create the share '
'(Default=None).',
default=None)
@cliutils.arg(
'--wait',
action='store_true',
help='Wait for share creation')
@cliutils.service_type('sharev2')
def do_create(cs, args):
"""Creates a new share (NFS, CIFS, CephFS, GlusterFS, HDFS or MAPRFS)."""
share_metadata = None
if args.metadata is not None:
share_metadata = _extract_metadata(args)
share_group = None
if args.share_group:
share_group = _find_share_group(cs, args.share_group).id
share_network = None
if args.share_network:
share_network = _find_share_network(cs, args.share_network)
share = cs.shares.create(args.share_protocol, args.size, args.snapshot_id,
args.name, args.description,
metadata=share_metadata,
share_network=share_network,
share_type=args.share_type,
is_public=args.public,
availability_zone=args.availability_zone,
share_group_id=share_group)
if args.wait:
share = _wait_for_share_status(cs, share)
_print_share(cs, share)
@api_versions.wraps("2.29")
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of share to migrate.')
@cliutils.arg(
'host',
metavar='<host@backend#pool>',
help="Destination host where share will be migrated to. Use the "
"format 'host@backend#pool'.")
@cliutils.arg(
'--force_host_assisted_migration',
'--force-host-assisted-migration',
metavar='<True|False>',
choices=['True', 'False'],
action='single_alias',
required=False,
default=False,
help="Enforces the use of the host-assisted migration approach, "
"which bypasses driver optimizations. Default=False.")
@cliutils.arg(
'--preserve-metadata',
'--preserve_metadata',
action='single_alias',
metavar='<True|False>',
choices=['True', 'False'],
required=True,
help="Enforces migration to preserve all file metadata when moving its "
"contents. If set to True, host-assisted migration will not be "
"attempted.")
@cliutils.arg(
'--preserve-snapshots',
'--preserve_snapshots',
action='single_alias',
metavar='<True|False>',
choices=['True', 'False'],
required=True,
help="Enforces migration of the share snapshots to the destination. If "
"set to True, host-assisted migration will not be attempted.")
@cliutils.arg(
'--writable',
metavar='<True|False>',
choices=['True', 'False'],
required=True,
help="Enforces migration to keep the share writable while contents are "
"being moved. If set to True, host-assisted migration will not be "
"attempted.")
@cliutils.arg(
'--nondisruptive',
metavar='<True|False>',
choices=['True', 'False'],
required=True,
help="Enforces migration to be nondisruptive. If set to True, "
"host-assisted migration will not be attempted.")
@cliutils.arg(
'--new_share_network',
'--new-share-network',
metavar='<new_share_network>',
action='single_alias',
required=False,
help='Specify the new share network for the share. Do not specify this '
'parameter if the migrating share has to be retained within its '
'current share network.',
default=None)
@cliutils.arg(
'--new_share_type',
'--new-share-type',
metavar='<new_share_type>',
required=False,
action='single_alias',
help='Specify the new share type for the share. Do not specify this '
'parameter if the migrating share has to be retained with its '
'current share type.',
default=None)
def do_migration_start(cs, args):
"""Migrates share to a new host (Admin only, Experimental)."""
share = _find_share(cs, args.share)
new_share_net_id = None
if args.new_share_network:
share_net = _find_share_network(cs, args.new_share_network)
new_share_net_id = share_net.id if share_net else None
new_share_type_id = None
if args.new_share_type:
share_type = _find_share_type(cs, args.new_share_type)
new_share_type_id = share_type.id if share_type else None
share.migration_start(args.host, args.force_host_assisted_migration,
args.preserve_metadata, args.writable,
args.nondisruptive, args.preserve_snapshots,
new_share_net_id, new_share_type_id)
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of share to complete migration.')
@api_versions.wraps("2.22")
def do_migration_complete(cs, args):
"""Completes migration for a given share (Admin only, Experimental)."""
share = _find_share(cs, args.share)
share.migration_complete()
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of share to cancel migration.')
@api_versions.wraps("2.22")
def do_migration_cancel(cs, args):
"""Cancels migration of a given share when copying
(Admin only, Experimental).
"""
share = _find_share(cs, args.share)
share.migration_cancel()
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the share to modify.')
@cliutils.arg(
'--task-state',
'--task_state',
'--state',
metavar='<task_state>',
default='None',
action='single_alias',
required=False,
help=('Indicate which task state to assign the share. Options include '
'migration_starting, migration_in_progress, migration_completing, '
'migration_success, migration_error, migration_cancelled, '
'migration_driver_in_progress, migration_driver_phase1_done, '
'data_copying_starting, data_copying_in_progress, '
'data_copying_completing, data_copying_completed, '
'data_copying_cancelled, data_copying_error. If no value is '
'provided, None will be used.'))
@api_versions.wraps("2.22")
def do_reset_task_state(cs, args):
"""Explicitly update the task state of a share
(Admin only, Experimental).
"""
state = args.task_state
if args.task_state == 'None':
state = None
share = _find_share(cs, args.share)
share.reset_task_state(state)
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the share to get share migration progress '
'information.')
@api_versions.wraps("2.22")
def do_migration_get_progress(cs, args):
"""Gets migration progress of a given share when copying
(Admin only, Experimental).
"""
share = _find_share(cs, args.share)
result = share.migration_get_progress()
# NOTE(ganso): result[0] is response code, result[1] is dict body
cliutils.print_dict(result[1])
@cliutils.arg(
'share_server_id',
metavar='<share_server_id>',
help='ID of the share server to check if the migration is possible.')
@cliutils.arg(
'host',
metavar='<host@backend>',
help="Destination to migrate the share server to. Use the format "
"'<node_hostname>@<backend_name>'.")
@cliutils.arg(
'--preserve-snapshots',
'--preserve_snapshots',
action='single_alias',
metavar='<True|False>',
choices=['True', 'False'],
required=True,
help="Set to True if snapshots must be preserved at the migration "
"destination.")
@cliutils.arg(
'--writable',
metavar='<True|False>',
choices=['True', 'False'],
required=True,
help="Set to True if shares associated with the share server must be "
"writable through the first phase of the migration.")
@cliutils.arg(
'--nondisruptive',
metavar='<True|False>',
choices=['True', 'False'],
required=True,
help="Set to True if migration must be non disruptive to clients that are "
"using the shares associated with the share server through both "
"phases of the migration.")
@cliutils.arg(
'--new_share_network',
'--new-share-network',
metavar='<new_share_network>',
action='single_alias',
required=False,
help="New share network to migrate to. Optional, default=None.",
default=None)
@api_versions.wraps("2.57")
@api_versions.experimental_api
def do_share_server_migration_check(cs, args):
"""Check migration compatibility for a share server with desired properties
(Admin only, Experimental).
"""
share_server = _find_share_server(cs, args.share_server_id)
new_share_net_id = None
if args.new_share_network:
share_net = _find_share_network(cs, args.new_share_network)
new_share_net_id = share_net.id
result = share_server.migration_check(
args.host, args.writable, args.nondisruptive, args.preserve_snapshots,
new_share_net_id)
cliutils.print_dict(result)
@cliutils.arg(
'share_server_id',
metavar='<share_server_id>',
help='ID of the share server to migrate.')
@cliutils.arg(
'host',
metavar='<host@backend>',
help="Destination to migrate the share server to. Use the format "
"'<node_hostname>@<backend_name>'.")
@cliutils.arg(
'--preserve-snapshots',
'--preserve_snapshots',
action='single_alias',
metavar='<True|False>',
choices=['True', 'False'],
required=True,
help="Set to True if snapshots must be preserved at the migration "
"destination.")
@cliutils.arg(
'--writable',
metavar='<True|False>',
choices=['True', 'False'],
required=True,
help="Enforces migration to keep all its shares writable while contents "
"are being moved.")
@cliutils.arg(
'--nondisruptive',
metavar='<True|False>',
choices=['True', 'False'],
required=True,
help="Enforces migration to be nondisruptive.")
@cliutils.arg(
'--new_share_network',
'--new-share-network',
metavar='<new_share_network>',
action='single_alias',
required=False,
help='Specify a new share network for the share server. Do not '
'specify this parameter if the migrating share server has '
'to be retained within its current share network.',
default=None)
@api_versions.wraps("2.57")
@api_versions.experimental_api
def do_share_server_migration_start(cs, args):
"""Migrates share server to a new host (Admin only, Experimental)."""
share_server = _find_share_server(cs, args.share_server_id)
new_share_net_id = None
if args.new_share_network:
share_net = _find_share_network(cs, args.new_share_network)
new_share_net_id = share_net.id
share_server.migration_start(args.host, args.writable, args.nondisruptive,
args.preserve_snapshots, new_share_net_id)
@cliutils.arg(
'share_server_id',
metavar='<share_server_id>',
help='ID of share server to complete migration.')
@api_versions.wraps("2.57")
@api_versions.experimental_api
def do_share_server_migration_complete(cs, args):
"""Completes migration for a given share server
(Admin only,Experimental).
"""
share_server = _find_share_server(cs, args.share_server_id)
result = share_server.migration_complete()
cliutils.print_dict(result)
@cliutils.arg(
'share_server_id',
metavar='<share_server_id>',
help='ID of share server to complete migration.')
@api_versions.wraps("2.57")
@api_versions.experimental_api
def do_share_server_migration_cancel(cs, args):
"""Cancels migration of a given share server when copying
(Admin only, Experimental).
"""
share_server = _find_share_server(cs, args.share_server_id)
share_server.migration_cancel()
@cliutils.arg(
'share_server_id',
metavar='<share_server_id>',
help='ID of share server to complete migration.')
@cliutils.arg(
'--task-state',
'--task_state',
'--state',
metavar='<task_state>',
default='None',
action='single_alias',
required=False,
help=('Indicate which task state to assign the share server. Options: '
'migration_starting, migration_in_progress, migration_completing, '
'migration_success, migration_error, migration_cancel_in_progress, '
'migration_cancelled, migration_driver_in_progress, '
'migration_driver_phase1_done. If no value is provided, None will '
'be used.'))
@api_versions.wraps("2.57")
@api_versions.experimental_api
def do_share_server_reset_task_state(cs, args):
"""Explicitly update the task state of a share
(Admin only, Experimental).
"""
state = args.task_state
if args.task_state == 'None':
state = None
share_server = _find_share_server(cs, args.share_server_id)
share_server.reset_task_state(state)
@cliutils.arg(
'share_server_id',
metavar='<share_server_id>',
help='ID of share server to complete migration.')
@api_versions.wraps("2.57")
@api_versions.experimental_api
def do_share_server_migration_get_progress(cs, args):
"""Gets migration progress of a given share server when copying
(Admin only, Experimental).
"""
share_server = _find_share_server(cs, args.share_server_id)
result = share_server.migration_get_progress()
cliutils.print_dict(result)
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the share to update metadata on.')
@cliutils.arg(
'action',
metavar='<action>',
choices=['set', 'unset'],
help="Actions: 'set' or 'unset'.")
@cliutils.arg(
'metadata',
metavar='<key=value>',
nargs='+',
default=[],
help='Metadata to set or unset (key is only necessary on unset).')
def do_metadata(cs, args):
"""Set or delete metadata on a share."""
share = _find_share(cs, args.share)
metadata = _extract_metadata(args)
if args.action == 'set':
cs.shares.set_metadata(share, metadata)
elif args.action == 'unset':
cs.shares.delete_metadata(share, sorted(list(metadata), reverse=True))
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the share.')
def do_metadata_show(cs, args):
"""Show metadata of given share."""
share = _find_share(cs, args.share)
metadata = cs.shares.get_metadata(share)._info
cliutils.print_dict(metadata, 'Property')
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the share to update metadata on.')
@cliutils.arg(
'metadata',
metavar='<key=value>',
nargs='+',
default=[],
help='Metadata entry or entries to update.')
def do_metadata_update_all(cs, args):
"""Update all metadata of a share."""
share = _find_share(cs, args.share)
metadata = _extract_metadata(args)
metadata = share.update_all_metadata(metadata)._info['metadata']
cliutils.print_dict(metadata, 'Property')
@api_versions.wraps("2.9")
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the share.')
@cliutils.arg(
'--columns',
metavar='<columns>',
type=str,
default=None,
help='Comma separated list of columns to be displayed '
'example --columns "id,host,status".')
def do_share_export_location_list(cs, args):
"""List export locations of a given share."""
if args.columns is not None:
list_of_keys = _split_columns(columns=args.columns)
else:
list_of_keys = [
'ID',
'Path',
'Preferred',
]
share = _find_share(cs, args.share)
export_locations = cs.share_export_locations.list(share)
cliutils.print_list(export_locations, list_of_keys)
@api_versions.wraps("2.9")
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the share.')
@cliutils.arg(
'export_location',
metavar='<export_location>',
help='ID of the share export location.')
def do_share_export_location_show(cs, args):
"""Show export location of the share."""
share = _find_share(cs, args.share)
export_location = cs.share_export_locations.get(
share, args.export_location)
view_data = export_location._info.copy()
cliutils.print_dict(view_data)
@cliutils.arg(
'service_host',
metavar='<service_host>',
type=str,
help='manage-share service host: some.host@driver#pool.')
@cliutils.arg(
'protocol',
metavar='<protocol>',
type=str,
help='Protocol of the share to manage, such as NFS or CIFS.')
@cliutils.arg(
'export_path',
metavar='<export_path>',
type=str,
help='Share export path, NFS share such as: 10.0.0.1:/example_path, '
'CIFS share such as: \\\\10.0.0.1\\example_cifs_share.')
@cliutils.arg(
'--name',
metavar='<name>',
help='Optional share name. (Default=None)',
default=None)
@cliutils.arg(
'--description',
metavar='<description>',
help='Optional share description. (Default=None)',
default=None)
@cliutils.arg(
'--share_type', '--share-type',
metavar='<share-type>',
default=None,
action='single_alias',
help='Optional share type assigned to share. (Default=None)')
@cliutils.arg(
'--driver_options', '--driver-options',
type=str,
nargs='*',
metavar='<key=value>',
action='single_alias',
help='Driver option key=value pairs (Optional, Default=None).',
default=None)
@cliutils.arg(
'--public',
dest='public',
action='store_true',
default=False,
help="Level of visibility for share. Defines whether other projects are "
"able to see it or not. Available only for microversion >= 2.8. "
"(Default=False)")
@cliutils.arg(
'--share_server_id', '--share-server-id',
metavar='<share-server-id>',
default=None,
action='single_alias',
help="Share server associated with share when using a share type with "
"'driver_handles_share_servers' extra_spec set to True. Available "
"only for microversion >= 2.49. (Default=None)")
def do_manage(cs, args):
"""Manage share not handled by Manila (Admin only)."""
driver_options = _extract_key_value_options(args, 'driver_options')
if cs.api_version.matches(api_versions.APIVersion("2.49"),
api_versions.APIVersion()):
share = cs.shares.manage(
args.service_host, args.protocol, args.export_path,
driver_options=driver_options, share_type=args.share_type,
name=args.name, description=args.description,
is_public=args.public, share_server_id=args.share_server_id)
else:
if args.share_server_id:
raise exceptions.CommandError("Invalid parameter "
"--share_server_id specified. This"
" parameter is only supported on"
" microversion 2.49 or newer.")
share = cs.shares.manage(
args.service_host, args.protocol, args.export_path,
driver_options=driver_options, share_type=args.share_type,
name=args.name, description=args.description,
is_public=args.public)
_print_share(cs, share)
@api_versions.wraps("2.49")
@cliutils.arg(
'host',
metavar='<host>',
type=str,
help='Backend name as "<node_hostname>@<backend_name>".')
@cliutils.arg(
'share_network',
metavar='<share_network>',
help="Share network where share server has network allocations in.")
@cliutils.arg(
'identifier',
metavar='<identifier>',
type=str,
help='A driver-specific share server identifier required by the driver to '
'manage the share server.')
@cliutils.arg(
'--driver_options', '--driver-options',
type=str,
nargs='*',
metavar='<key=value>',
action='single_alias',
help='One or more driver-specific key=value pairs that may be necessary to'
' manage the share server (Optional, Default=None).',
default=None)
@cliutils.arg(
'--share-network-subnet', '--share_network_subnet',
type=str,
metavar='<share_network_subnet>',
help="Share network subnet where share server has network allocations in. "
"The default subnet will be used if it's not specified. Available "
"for microversion >= 2.51 (Optional, Default=None).",
default=None)
def do_share_server_manage(cs, args):
"""Manage share server not handled by Manila (Admin only)."""
driver_options = _extract_key_value_options(args, 'driver_options')
manage_kwargs = {
'driver_options': driver_options,
}
if cs.api_version < api_versions.APIVersion("2.51"):
if getattr(args, 'share_network_subnet'):
raise exceptions.CommandError(
"Share network subnet option is only available with manila "
"API version >= 2.51")
else:
manage_kwargs['share_network_subnet_id'] = args.share_network_subnet
share_server = cs.share_servers.manage(
args.host, args.share_network, args.identifier,
**manage_kwargs)
cliutils.print_dict(share_server._info)
@cliutils.arg(
'share_server_id',
metavar='<share_server_id>',
help='ID of the share server to modify.')
@cliutils.arg(
'--state',
metavar='<state>',
default=constants.STATUS_ACTIVE,
help=('Indicate which state to assign the share server. Options include '
'active, error, creating, deleting, managing, unmanaging, '
'manage_error and unmanage_error. If no state is provided, active '
'will be used.'))
@api_versions.wraps("2.49")
def do_share_server_reset_state(cs, args):
"""Explicitly update the state of a share server (Admin only)."""
cs.share_servers.reset_state(args.share_server_id, args.state)
@api_versions.wraps("2.12")
@cliutils.arg(
'share',
metavar='<share>',
type=str,
help='Name or ID of the share.')
@cliutils.arg(
'provider_location',
metavar='<provider_location>',
type=str,
help='Provider location of the snapshot on the backend.')
@cliutils.arg(
'--name',
metavar='<name>',
help='Optional snapshot name (Default=None).',
default=None)
@cliutils.arg(
'--description',
metavar='<description>',
help='Optional snapshot description (Default=None).',
default=None)
@cliutils.arg(
'--driver_options', '--driver-options',
type=str,
nargs='*',
metavar='<key=value>',
action='single_alias',
help='Optional driver options as key=value pairs (Default=None).',
default=None)
def do_snapshot_manage(cs, args):
"""Manage share snapshot not handled by Manila (Admin only)."""
share_ref = _find_share(cs, args.share)
driver_options = _extract_key_value_options(args, 'driver_options')
share_snapshot = cs.share_snapshots.manage(
share_ref, args.provider_location,
driver_options=driver_options,
name=args.name, description=args.description
)
_print_share_snapshot(cs, share_snapshot)
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the share(s).')
def do_unmanage(cs, args):
"""Unmanage share (Admin only)."""
share_ref = _find_share(cs, args.share)
share_ref.unmanage()
@api_versions.wraps("2.49")
@cliutils.arg(
'share_server',
metavar='<share_server>',
nargs='+',
help='ID of the share server(s).')
@cliutils.arg(
'--force',
dest='force',
action="store_true",
required=False,
default=False,
help="Enforces the unmanage share server operation, even if the back-end "
"driver does not support it.")
def do_share_server_unmanage(cs, args):
"""Unmanage share server (Admin only)."""
failure_count = 0
for server in args.share_server:
try:
cs.share_servers.unmanage(server, args.force)
except Exception as e:
failure_count += 1
print("Unmanage for share server %s failed: %s" % (server, e),
file=sys.stderr)
if failure_count == len(args.share_server):
raise exceptions.CommandError("Unable to unmanage any of the "
"specified share servers.")
@api_versions.wraps("2.12")
@cliutils.arg(
'snapshot',
metavar='<snapshot>',
nargs='+',
help='Name or ID of the snapshot(s).')
def do_snapshot_unmanage(cs, args):
"""Unmanage one or more share snapshots (Admin only)."""
failure_count = 0
for snapshot in args.snapshot:
try:
snapshot_ref = _find_share_snapshot(cs, snapshot)
snapshot_ref.unmanage_snapshot()
except Exception as e:
failure_count += 1
print("Unmanage for share snapshot %s failed: %s" % (snapshot, e),
file=sys.stderr)
if failure_count == len(args.snapshot):
raise exceptions.CommandError("Unable to unmanage any of the "
"specified snapshots.")
@api_versions.wraps("2.27")
@cliutils.arg(
'snapshot',
metavar='<snapshot>',
help='Name or ID of the snapshot to restore. The snapshot must be the '
'most recent one known to manila.')
def do_revert_to_snapshot(cs, args):
"""Revert a share to the specified snapshot."""
snapshot = _find_share_snapshot(cs, args.snapshot)
share = _find_share(cs, snapshot.share_id)
share.revert_to_snapshot(snapshot)
@cliutils.arg(
'share',
metavar='<share>',
nargs='+',
help='Name or ID of the share(s).')
@cliutils.arg(
'--share-group', '--share_group', '--group',
metavar='<share-group>',
action='single_alias',
help='Optional share group name or ID which contains the share '
'(Default=None).',
default=None)
@cliutils.arg(
'--wait',
action='store_true',
help='Wait for share deletion')
@cliutils.service_type('sharev2')
def do_delete(cs, args):
"""Remove one or more shares."""
failure_count = 0
shares_to_delete = []
for share in args.share:
try:
share_ref = _find_share(cs, share)
shares_to_delete.append(share_ref)
if args.share_group:
share_group_id = _find_share_group(cs, args.share_group).id
cs.shares.delete(share_ref, share_group_id=share_group_id)
else:
cs.shares.delete(share_ref)
except Exception as e:
failure_count += 1
print("Delete for share %s failed: %s" % (share, e),
file=sys.stderr)
if failure_count == len(args.share):
raise exceptions.CommandError("Unable to delete any of the specified "
"shares.")
if args.wait:
for share in shares_to_delete:
try:
_wait_for_share_status(cs, share, expected_status='deleted')
except exceptions.CommandError as e:
print(e, file=sys.stderr)
@cliutils.arg(
'share',
metavar='<share>',
nargs='+',
help='Name or ID of the share(s) to force delete.')
def do_force_delete(cs, args):
"""Attempt force-delete of share, regardless of state (Admin only)."""
failure_count = 0
for share in args.share:
try:
_find_share(cs, share).force_delete()
except Exception as e:
failure_count += 1
print("Delete for share %s failed: %s" % (share, e),
file=sys.stderr)
if failure_count == len(args.share):
raise exceptions.CommandError("Unable to force delete any of "
"specified shares.")
@api_versions.wraps("1.0", "2.8")
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the NAS share.')
def do_show(cs, args):
"""Show details about a NAS share."""
share = _find_share(cs, args.share)
_print_share(cs, share)
@api_versions.wraps("2.9") # noqa
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the NAS share.')
def do_show(cs, args): # noqa
"""Show details about a NAS share."""
share = _find_share(cs, args.share)
export_locations = cs.share_export_locations.list(share)
share._info['export_locations'] = export_locations
_print_share(cs, share)
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the NAS share to modify.')
@cliutils.arg(
'access_type',
metavar='<access_type>',
help='Access rule type (only "ip", "user"(user or group), "cert" or '
'"cephx" are supported).')
@cliutils.arg(
'access_to',
metavar='<access_to>',
help='Value that defines access.')
@cliutils.arg(
'--access-level',
'--access_level', # alias
metavar='<access_level>',
type=str,
default=None,
choices=['rw', 'ro'],
action='single_alias',
help='Share access level ("rw" and "ro" access levels are supported). '
'Defaults to rw.')
@cliutils.arg(
'--metadata',
type=str,
nargs='*',
metavar='<key=value>',
help='Space Separated list of key=value pairs of metadata items. '
'OPTIONAL: Default=None. Available only for microversion >= 2.45.',
default=None)
def do_access_allow(cs, args):
"""Allow access to a given share."""
access_metadata = None
if cs.api_version.matches(api_versions.APIVersion("2.45"),
api_versions.APIVersion()):
access_metadata = _extract_metadata(args)
elif getattr(args, 'metadata'):
raise exceptions.CommandError(
"Adding metadata to access rules is supported only beyond "
"API version 2.45")
share = _find_share(cs, args.share)
access = share.allow(args.access_type, args.access_to, args.access_level,
access_metadata)
cliutils.print_dict(access)
@api_versions.wraps("2.45")
@cliutils.arg(
'access_id',
metavar='<access_id>',
help='ID of the NAS share access rule.')
def do_access_show(cs, args):
"""Show details about a NAS share access rule."""
access = cs.share_access_rules.get(args.access_id)
view_data = access._info.copy()
cliutils.print_dict(view_data)
@api_versions.wraps("2.45")
@cliutils.arg(
'access_id',
metavar='<access_id>',
help='ID of the NAS share access rule.')
@cliutils.arg(
'action',
metavar='<action>',
choices=['set', 'unset'],
help="Actions: 'set' or 'unset'.")
@cliutils.arg(
'metadata',
metavar='<key=value>',
nargs='+',
default=[],
help='Space separated key=value pairs of metadata items to set. '
'To unset only keys are required. ')
def do_access_metadata(cs, args):
"""Set or delete metadata on a share access rule."""
share_access = cs.share_access_rules.get(args.access_id)
metadata = _extract_metadata(args)
if args.action == 'set':
cs.share_access_rules.set_metadata(share_access, metadata)
elif args.action == 'unset':
cs.share_access_rules.unset_metadata(
share_access, sorted(list(metadata), reverse=True))
@api_versions.wraps("2.32")
@cliutils.arg(
'snapshot',
metavar='<snapshot>',
help='Name or ID of the share snapshot to allow access to.')
@cliutils.arg(
'access_type',
metavar='<access_type>',
help='Access rule type (only "ip", "user"(user or group), "cert" or '
'"cephx" are supported).')
@cliutils.arg(
'access_to',
metavar='<access_to>',
help='Value that defines access.')
def do_snapshot_access_allow(cs, args):
"""Allow read only access to a snapshot."""
share_snapshot = _find_share_snapshot(cs, args.snapshot)
access = share_snapshot.allow(args.access_type, args.access_to)
cliutils.print_dict(access)
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the NAS share to modify.')
@cliutils.arg(
'id',
metavar='<id>',
help='ID of the access rule to be deleted.')
def do_access_deny(cs, args):
"""Deny access to a share."""
share = _find_share(cs, args.share)
share.deny(args.id)
@api_versions.wraps("2.32")
@cliutils.arg(
'snapshot',
metavar='<snapshot>',
help='Name or ID of the share snapshot to deny access to.')
@cliutils.arg(
'id',
metavar='<id>',
nargs='+',
help='ID(s) of the access rule(s) to be deleted.')
def do_snapshot_access_deny(cs, args):
"""Deny access to a snapshot."""
failure_count = 0
snapshot = _find_share_snapshot(cs, args.snapshot)
for access_id in args.id:
try:
snapshot.deny(access_id)
except Exception as e:
failure_count += 1
print("Failed to remove rule %(access)s: %(reason)s."
% {'access': access_id, 'reason': e},
file=sys.stderr)
if failure_count == len(args.id):
raise exceptions.CommandError("Unable to delete any of the specified "
"snapshot rules.")
@api_versions.wraps("1.0", "2.20")
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the share.')
@cliutils.arg(
'--columns',
metavar='<columns>',
type=str,
default=None,
help='Comma separated list of columns to be displayed '
'example --columns "access_type,access_to".')
def do_access_list(cs, args):
"""Show access list for share."""
list_of_keys = [
'id', 'access_type', 'access_to', 'access_level', 'state',
]
if args.columns is not None:
list_of_keys = _split_columns(columns=args.columns)
share = _find_share(cs, args.share)
access_list = share.access_list()
cliutils.print_list(access_list, list_of_keys)
@api_versions.wraps("2.21") # noqa
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the share.')
@cliutils.arg(
'--columns',
metavar='<columns>',
type=str,
default=None,
help='Comma separated list of columns to be displayed '
'example --columns "access_type,access_to".')
def do_access_list(cs, args): # noqa
"""Show access list for share."""
list_of_keys = [
'id', 'access_type', 'access_to', 'access_level', 'state',
'access_key'
]
if args.columns is not None:
list_of_keys = _split_columns(columns=args.columns)
share = _find_share(cs, args.share)
access_list = share.access_list()
cliutils.print_list(access_list, list_of_keys)
@api_versions.wraps("2.33") # noqa
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the share.')
@cliutils.arg(
'--columns',
metavar='<columns>',
type=str,
default=None,
help='Comma separated list of columns to be displayed '
'example --columns "access_type,access_to".')
@cliutils.arg(
'--metadata',
type=str,
nargs='*',
metavar='<key=value>',
help='Filters results by a metadata key and value. OPTIONAL: '
'Default=None. Available only for microversion >= 2.45',
default=None)
def do_access_list(cs, args): # noqa
"""Show access list for share."""
list_of_keys = [
'id', 'access_type', 'access_to', 'access_level', 'state',
'access_key', 'created_at', 'updated_at',
]
share = _find_share(cs, args.share)
if cs.api_version < api_versions.APIVersion("2.45"):
if getattr(args, 'metadata'):
raise exceptions.CommandError(
"Filtering access rules by metadata is supported only beyond "
"API version 2.45")
access_list = share.access_list()
else:
access_list = cs.share_access_rules.access_list(
share, {'metadata': _extract_metadata(args)})
if args.columns is not None:
list_of_keys = _split_columns(columns=args.columns)
cliutils.print_list(access_list, list_of_keys)
@api_versions.wraps("2.32")
@cliutils.arg(
'snapshot',
metavar='<snapshot>',
help='Name or ID of the share snapshot to list access of.')
@cliutils.arg(
'--columns',
metavar='<columns>',
type=str,
default=None,
help='Comma separated list of columns to be displayed '
'example --columns "access_type,access_to".')
def do_snapshot_access_list(cs, args):
"""Show access list for a snapshot."""
if args.columns is not None:
list_of_keys = _split_columns(columns=args.columns)
else:
list_of_keys = ['id', 'access_type', 'access_to', 'state']
snapshot = _find_share_snapshot(cs, args.snapshot)
access_list = snapshot.access_list()
cliutils.print_list(access_list, list_of_keys)
@cliutils.arg(
'--all-tenants', '--all-projects',
action='single_alias',
dest='all_projects',
metavar='<0|1>',
nargs='?',
type=int,
const=1,
default=0,
help='Display information from all projects (Admin only).')
@cliutils.arg(
'--name',
metavar='<name>',
type=six.text_type,
default=None,
help='Filter results by name.')
@cliutils.arg(
'--description',
metavar='<description>',
type=six.text_type,
default=None,
help='Filter results by description. '
'Available only for microversion >= 2.36.')
@cliutils.arg(
'--name~',
metavar='<name~>',
type=six.text_type,
default=None,
help='Filter results matching a share name pattern. '
'Available only for microversion >= 2.36.')
@cliutils.arg(
'--description~',
metavar='<description~>',
type=six.text_type,
default=None,
help='Filter results matching a share description pattern. '
'Available only for microversion >= 2.36.')
@cliutils.arg(
'--status',
metavar='<status>',
type=str,
default=None,
help='Filter results by status.')
@cliutils.arg(
'--share-server-id',
'--share-server_id', '--share_server-id', '--share_server_id', # aliases
metavar='<share_server_id>',
type=str,
default=None,
action='single_alias',
help='Filter results by share server ID (Admin only).')
@cliutils.arg(
'--metadata',
type=str,
nargs='*',
metavar='<key=value>',
help='Filters results by a metadata key and value. OPTIONAL: '
'Default=None.',
default=None)
@cliutils.arg(
'--extra-specs',
'--extra_specs', # alias
type=str,
nargs='*',
metavar='<key=value>',
action='single_alias',
help='Filters results by a extra specs key and value of share type that '
'was used for share creation. OPTIONAL: Default=None.',
default=None)
@cliutils.arg(
'--share-type', '--volume-type',
'--share_type', '--share-type-id', '--volume-type-id', # aliases
'--share-type_id', '--share_type-id', '--share_type_id', # aliases
'--volume_type', '--volume_type_id',
metavar='<share_type>',
type=str,
default=None,
action='single_alias',
help='Filter results by a share type id or name that was used for share '
'creation.')
@cliutils.arg(
'--limit',
metavar='<limit>',
type=int,
default=None,
help='Maximum number of shares to return. OPTIONAL: Default=None.')
@cliutils.arg(
'--offset',
metavar='<offset>',
type=int,
default=None,
help='Set offset to define start point of share listing. '
'OPTIONAL: Default=None.')
@cliutils.arg(
'--sort-key',
'--sort_key', # alias
metavar='<sort_key>',
type=str,
default=None,
action='single_alias',
help='Key to be sorted, available keys are %(keys)s. '
'OPTIONAL: Default=None.' % {'keys': constants.SHARE_SORT_KEY_VALUES})
@cliutils.arg(
'--sort-dir',
'--sort_dir', # alias
metavar='<sort_dir>',
type=str,
default=None,
action='single_alias',
help='Sort direction, available values are %(values)s. '
'OPTIONAL: Default=None.' % {'values': constants.SORT_DIR_VALUES})
@cliutils.arg(
'--snapshot',
metavar='<snapshot>',
type=str,
default=None,
help='Filer results by snapshot name or id, that was used for share.')
@cliutils.arg(
'--host',
metavar='<host>',
default=None,
help='Filter results by host.')
@cliutils.arg(
'--share-network',
'--share_network', # alias
metavar='<share_network>',
type=str,
default=None,
action='single_alias',
help='Filter results by share-network name or id.')
@cliutils.arg(
'--project-id',
'--project_id', # alias
metavar='<project_id>',
type=str,
default=None,
action='single_alias',
help="Filter results by project id. Useful with set key '--all-projects'.")
@cliutils.arg(
'--public',
dest='public',
action='store_true',
default=False,
help="Add public shares from all projects to result. (Default=False)")
@cliutils.arg(
'--share-group', '--share_group', '--group',
metavar='<share_group>',
type=str,
default=None,
action='single_alias',
help='Filter results by share group name or ID (Default=None).')
@cliutils.arg(
'--columns',
metavar='<columns>',
type=str,
default=None,
help='Comma separated list of columns to be displayed '
'example --columns "export_location,is public".')
@cliutils.arg(
'--export-location', '--export_location',
metavar='<export_location>',
type=str,
default=None,
action='single_alias',
help='ID or path of the share export location. '
'Available only for microversion >= 2.35.')
@cliutils.arg(
'--count',
dest='count',
metavar='<True|False>',
choices=['True', 'False'],
default=False,
help='Display total number of shares to return. '
'Available only for microversion >= 2.42.')
@cliutils.service_type('sharev2')
def do_list(cs, args):
"""List NAS shares with filters."""
columns = args.columns
all_projects = int(
os.environ.get("ALL_TENANTS",
os.environ.get("ALL_PROJECTS",
args.all_projects))
)
if columns is not None:
list_of_keys = _split_columns(columns=columns)
else:
list_of_keys = [
'ID', 'Name', 'Size', 'Share Proto', 'Status', 'Is Public',
'Share Type Name', 'Host', 'Availability Zone'
]
if all_projects or args.public:
list_of_keys.append('Project ID')
empty_obj = type('Empty', (object,), {'id': None})
share_type = (_find_share_type(cs, args.share_type)
if args.share_type else empty_obj)
snapshot = (_find_share_snapshot(cs, args.snapshot)
if args.snapshot else empty_obj)
share_network = (_find_share_network(cs, args.share_network)
if args.share_network else empty_obj)
share_group = None
if args.share_group:
share_group = _find_share_group(cs, args.share_group)
search_opts = {
'offset': args.offset,
'limit': args.limit,
'all_tenants': all_projects,
'name': args.name,
'status': args.status,
'host': args.host,
'share_network_id': share_network.id,
'snapshot_id': snapshot.id,
'share_type_id': share_type.id,
'metadata': _extract_metadata(args),
'extra_specs': _extract_extra_specs(args),
'share_server_id': args.share_server_id,
'project_id': args.project_id,
'is_public': args.public,
}
if cs.api_version.matches(api_versions.APIVersion("2.36"),
api_versions.APIVersion()):
search_opts['name~'] = getattr(args, 'name~')
search_opts['description~'] = getattr(args, 'description~')
search_opts['description'] = getattr(args, 'description')
elif (getattr(args, 'name~') or getattr(args, 'description~') or
getattr(args, 'description')):
raise exceptions.CommandError(
"Pattern based filtering (name~, description~ and description)"
" is only available with manila API version >= 2.36")
if cs.api_version.matches(api_versions.APIVersion("2.35"),
api_versions.APIVersion()):
search_opts['export_location'] = args.export_location
elif args.export_location:
raise exceptions.CommandError(
"Filtering by export location is only "
"available with manila API version >= 2.35")
if (args.count and
cs.api_version.matches(
api_versions.APIVersion(), api_versions.APIVersion("2.41"))):
raise exceptions.CommandError(
"Display total number of shares is only "
"available with manila API version >= 2.42")
if share_group:
search_opts['share_group_id'] = share_group.id
total_count = 0
if strutils.bool_from_string(args.count, strict=True):
search_opts['with_count'] = args.count
shares, total_count = cs.shares.list(
search_opts=search_opts, sort_key=args.sort_key,
sort_dir=args.sort_dir,
)
else:
shares = cs.shares.list(
search_opts=search_opts, sort_key=args.sort_key,
sort_dir=args.sort_dir,
)
# NOTE(vponomaryov): usage of 'export_location' and
# 'export_locations' columns may cause scaling issue using API 2.9+ and
# when lots of shares are returned.
if (shares and columns is not None and 'export_location' in columns and
not hasattr(shares[0], 'export_location')):
# NOTE(vponomaryov): we will get here only using API 2.9+
for share in shares:
els_objs = cs.share_export_locations.list(share)
els = [el.to_dict()['path'] for el in els_objs]
setattr(share, 'export_locations', els)
setattr(share, 'export_location', els[0] if els else None)
cliutils.print_list(shares, list_of_keys, sortby_index=None)
if args.count:
print("Shares in total: %s" % total_count)
@cliutils.arg(
'--share-id',
'--share_id', # alias
metavar='<share_id>',
default=None,
action='single_alias',
help='Filter results by share ID.')
@cliutils.arg(
'--columns',
metavar='<columns>',
type=str,
default=None,
help='Comma separated list of columns to be displayed '
'example --columns "id,host,status".')
@cliutils.arg(
'--export-location', '--export_location',
metavar='<export_location>',
type=str,
default=None,
action='single_alias',
help='ID or path of the share instance export location. '
'Available only for microversion >= 2.35.')
@api_versions.wraps("2.3")
def do_share_instance_list(cs, args):
"""List share instances (Admin only)."""
share = _find_share(cs, args.share_id) if args.share_id else None
list_of_keys = [
'ID', 'Share ID', 'Host', 'Status', 'Availability Zone',
'Share Network ID', 'Share Server ID', 'Share Type ID',
]
if args.columns is not None:
list_of_keys = _split_columns(columns=args.columns)
if share:
instances = cs.shares.list_instances(share)
else:
if cs.api_version.matches(
api_versions.APIVersion("2.35"), api_versions.APIVersion()):
instances = cs.share_instances.list(args.export_location)
else:
if args.export_location:
raise exceptions.CommandError(
"Filtering by export location is only "
"available with manila API version >= 2.35")
instances = cs.share_instances.list()
cliutils.print_list(instances, list_of_keys)
@api_versions.wraps("2.3", "2.8")
@cliutils.arg(
'instance',
metavar='<instance>',
help='Name or ID of the share instance.')
def do_share_instance_show(cs, args):
"""Show details about a share instance."""
instance = _find_share_instance(cs, args.instance)
_print_share_instance(cs, instance)
@api_versions.wraps("2.9") # noqa
@cliutils.arg(
'instance',
metavar='<instance>',
help='Name or ID of the share instance.')
def do_share_instance_show(cs, args): # noqa
"""Show details about a share instance (Admin only)."""
instance = _find_share_instance(cs, args.instance)
export_locations = cs.share_instance_export_locations.list(instance)
instance._info['export_locations'] = export_locations
_print_share_instance(cs, instance)
@cliutils.arg(
'instance',
metavar='<instance>',
nargs='+',
help='Name or ID of the instance(s) to force delete.')
@api_versions.wraps("2.3")
def do_share_instance_force_delete(cs, args):
"""Force-delete the share instance, regardless of state (Admin only)."""
failure_count = 0
for instance in args.instance:
try:
_find_share_instance(cs, instance).force_delete()
except Exception as e:
failure_count += 1
print("Delete for share instance %s failed: %s" % (instance, e),
file=sys.stderr)
if failure_count == len(args.instance):
raise exceptions.CommandError("Unable to force delete any of "
"specified share instances.")
@cliutils.arg(
'instance',
metavar='<instance>',
help='Name or ID of the share instance to modify.')
@cliutils.arg(
'--state',
metavar='<state>',
default='available',
help=('Indicate which state to assign the instance. Options include '
'available, error, creating, deleting, error_deleting, migrating,'
'migrating_to. If no state is provided, available will be used.'))
@api_versions.wraps("2.3")
def do_share_instance_reset_state(cs, args):
"""Explicitly update the state of a share instance (Admin only)."""
instance = _find_share_instance(cs, args.instance)
instance.reset_state(args.state)
@api_versions.wraps("2.9")
@cliutils.arg(
'instance',
metavar='<instance>',
help='Name or ID of the share instance.')
@cliutils.arg(
'--columns',
metavar='<columns>',
type=str,
default=None,
help='Comma separated list of columns to be displayed '
'example --columns "id,host,status".')
def do_share_instance_export_location_list(cs, args):
"""List export locations of a given share instance."""
if args.columns is not None:
list_of_keys = _split_columns(columns=args.columns)
else:
list_of_keys = [
'ID',
'Path',
'Is Admin only',
'Preferred',
]
instance = _find_share_instance(cs, args.instance)
export_locations = cs.share_instance_export_locations.list(instance)
cliutils.print_list(export_locations, list_of_keys)
@api_versions.wraps("2.9")
@cliutils.arg(
'instance',
metavar='<instance>',
help='Name or ID of the share instance.')
@cliutils.arg(
'export_location',
metavar='<export_location>',
help='ID of the share instance export location.')
def do_share_instance_export_location_show(cs, args):
"""Show export location for the share instance."""
instance = _find_share_instance(cs, args.instance)
export_location = cs.share_instance_export_locations.get(
instance, args.export_location)
view_data = export_location._info.copy()
cliutils.print_dict(view_data)
@cliutils.arg(
'--all-tenants', '--all-projects',
action='single_alias',
dest='all_projects',
metavar='<0|1>',
nargs='?',
type=int,
const=1,
default=0,
help='Display information from all projects (Admin only).')
@cliutils.arg(
'--name',
metavar='<name>',
type=six.text_type,
default=None,
help='Filter results by name.')
@cliutils.arg(
'--description',
metavar='<description>',
type=six.text_type,
default=None,
help='Filter results by description. '
'Available only for microversion >= 2.36.')
@cliutils.arg(
'--status',
metavar='<status>',
default=None,
help='Filter results by status.')
@cliutils.arg(
'--share-id',
'--share_id', # alias
metavar='<share_id>',
default=None,
action='single_alias',
help='Filter results by source share ID.')
@cliutils.arg(
'--usage',
dest='usage',
metavar='any|used|unused',
nargs='?',
type=str,
const='any',
default=None,
choices=['any', 'used', 'unused', ],
help='Either filter or not snapshots by its usage. OPTIONAL: Default=any.')
@cliutils.arg(
'--limit',
metavar='<limit>',
type=int,
default=None,
help='Maximum number of share snapshots to return. '
'OPTIONAL: Default=None.')
@cliutils.arg(
'--offset',
metavar='<offset>',
type=int,
default=None,
help='Set offset to define start point of share snapshots listing. '
'OPTIONAL: Default=None.')
@cliutils.arg(
'--sort-key',
'--sort_key', # alias
metavar='<sort_key>',
type=str,
default=None,
action='single_alias',
help='Key to be sorted, available keys are %(keys)s. '
'Default=None.' % {'keys': constants.SNAPSHOT_SORT_KEY_VALUES})
@cliutils.arg(
'--sort-dir',
'--sort_dir', # alias
metavar='<sort_dir>',
type=str,
default=None,
action='single_alias',
help='Sort direction, available values are %(values)s. '
'OPTIONAL: Default=None.' % {'values': constants.SORT_DIR_VALUES})
@cliutils.arg(
'--columns',
metavar='<columns>',
type=str,
default=None,
help='Comma separated list of columns to be displayed '
'example --columns "id,name".')
@cliutils.arg(
'--name~',
metavar='<name~>',
type=six.text_type,
default=None,
help='Filter results matching a share snapshot name pattern. '
'Available only for microversion >= 2.36.')
@cliutils.arg(
'--description~',
metavar='<description~>',
type=six.text_type,
default=None,
help='Filter results matching a share snapshot description pattern. '
'Available only for microversion >= 2.36.')
def do_snapshot_list(cs, args):
"""List all the snapshots."""
all_projects = int(
os.environ.get("ALL_TENANTS",
os.environ.get("ALL_PROJECTS",
args.all_projects))
)
if args.columns is not None:
list_of_keys = _split_columns(columns=args.columns)
else:
list_of_keys = [
'ID', 'Share ID', 'Status', 'Name', 'Share Size',
]
if all_projects:
list_of_keys.append('Project ID')
empty_obj = type('Empty', (object,), {'id': None})
share = _find_share(cs, args.share_id) if args.share_id else empty_obj
search_opts = {
'offset': args.offset,
'limit': args.limit,
'all_tenants': all_projects,
'name': args.name,
'status': args.status,
'share_id': share.id,
'usage': args.usage,
}
if cs.api_version.matches(api_versions.APIVersion("2.36"),
api_versions.APIVersion()):
search_opts['name~'] = getattr(args, 'name~')
search_opts['description~'] = getattr(args, 'description~')
search_opts['description'] = getattr(args, 'description')
elif (getattr(args, 'name~') or getattr(args, 'description~') or
getattr(args, 'description')):
raise exceptions.CommandError(
"Pattern based filtering (name~, description~ and description)"
" is only available with manila API version >= 2.36")