From e448fc88bf625f660d11a508ca48732e7ae3d411 Mon Sep 17 00:00:00 2001 From: Goutham Pacha Ravi Date: Fri, 16 Jan 2026 15:28:15 -0800 Subject: [PATCH] Remove the legacy "manila" CLI The "manila" CLI was deprecated during the 2023.2 release (4.6.0 [1]). All new manila API changes were supported via the "openstack" CLI. After an extended deprecation window, we're now officially removing this CLI utility. [1] https://docs.openstack.org/releasenotes/python-manilaclient/2023.2.html#relnotes-4-6-0-stable-2023-2 Change-Id: Ifbdf8f9385b855c1e5f273bac31cb3b4a538a5ae Signed-off-by: Goutham Pacha Ravi --- doc/source/cli/decoder.rst | 6 +- doc/source/index.rst | 27 +- doc/source/user/shell.rst | 59 - manilaclient/osc/v2/data/manila.csv | 2 + manilaclient/shell.py | 858 -- manilaclient/tests/functional/base.py | 649 -- manilaclient/tests/functional/client.py | 2379 ----- .../functional/test_availability_zones.py | 59 - manilaclient/tests/functional/test_common.py | 73 - .../tests/functional/test_export_locations.py | 173 - manilaclient/tests/functional/test_limits.py | 29 - .../tests/functional/test_messages.py | 80 - manilaclient/tests/functional/test_quotas.py | 395 - .../tests/functional/test_scheduler_stats.py | 49 - .../functional/test_security_services.py | 80 - .../tests/functional/test_services.py | 44 - .../tests/functional/test_share_access.py | 384 - .../functional/test_share_network_subnets.py | 129 - .../tests/functional/test_share_networks.py | 695 -- .../test_share_replica_export_locations.py | 122 - .../tests/functional/test_share_replicas.py | 51 - .../tests/functional/test_share_servers.py | 405 - .../tests/functional/test_share_transfers.py | 100 - .../tests/functional/test_share_types.py | 627 -- manilaclient/tests/functional/test_shares.py | 339 - .../tests/functional/test_shares_listing.py | 414 - .../tests/functional/test_shares_metadata.py | 155 - .../tests/functional/test_snapshot_access.py | 221 - .../functional/test_snapshot_instances.py | 140 - ...est_snapshot_instances_export_locations.py | 131 - .../test_snapshots_export_locations.py | 96 - manilaclient/tests/unit/test_shell.py | 556 -- manilaclient/tests/unit/v2/test_shell.py | 4859 ---------- manilaclient/v2/shell.py | 7907 ----------------- pyproject.toml | 3 - ...remove-python-client-a142b99ccc89731c.yaml | 5 + tools/manila.bash_completion | 15 - 37 files changed, 17 insertions(+), 22299 deletions(-) delete mode 100644 doc/source/user/shell.rst delete mode 100644 manilaclient/shell.py delete mode 100644 manilaclient/tests/functional/base.py delete mode 100644 manilaclient/tests/functional/client.py delete mode 100644 manilaclient/tests/functional/test_availability_zones.py delete mode 100644 manilaclient/tests/functional/test_common.py delete mode 100644 manilaclient/tests/functional/test_export_locations.py delete mode 100644 manilaclient/tests/functional/test_limits.py delete mode 100644 manilaclient/tests/functional/test_messages.py delete mode 100644 manilaclient/tests/functional/test_quotas.py delete mode 100644 manilaclient/tests/functional/test_scheduler_stats.py delete mode 100644 manilaclient/tests/functional/test_security_services.py delete mode 100644 manilaclient/tests/functional/test_services.py delete mode 100644 manilaclient/tests/functional/test_share_access.py delete mode 100644 manilaclient/tests/functional/test_share_network_subnets.py delete mode 100644 manilaclient/tests/functional/test_share_networks.py delete mode 100644 manilaclient/tests/functional/test_share_replica_export_locations.py delete mode 100644 manilaclient/tests/functional/test_share_replicas.py delete mode 100644 manilaclient/tests/functional/test_share_servers.py delete mode 100644 manilaclient/tests/functional/test_share_transfers.py delete mode 100644 manilaclient/tests/functional/test_share_types.py delete mode 100644 manilaclient/tests/functional/test_shares.py delete mode 100644 manilaclient/tests/functional/test_shares_listing.py delete mode 100644 manilaclient/tests/functional/test_shares_metadata.py delete mode 100644 manilaclient/tests/functional/test_snapshot_access.py delete mode 100644 manilaclient/tests/functional/test_snapshot_instances.py delete mode 100644 manilaclient/tests/functional/test_snapshot_instances_export_locations.py delete mode 100644 manilaclient/tests/functional/test_snapshots_export_locations.py delete mode 100644 manilaclient/tests/unit/test_shell.py delete mode 100644 manilaclient/tests/unit/v2/test_shell.py delete mode 100644 manilaclient/v2/shell.py create mode 100644 releasenotes/notes/remove-python-client-a142b99ccc89731c.yaml delete mode 100644 tools/manila.bash_completion diff --git a/doc/source/cli/decoder.rst b/doc/source/cli/decoder.rst index 2c336df4b..b97d745e6 100644 --- a/doc/source/cli/decoder.rst +++ b/doc/source/cli/decoder.rst @@ -2,10 +2,14 @@ OpenStackClient Mapping Guide ============================= +.. note:: + + The legacy ``manila`` CLI has been removed. This guide is maintained for + historical reference to help users upgrading from older versions. + The following is a mapping between the legacy ``manila`` CLI client and OpenStackClient. Command options are only shown when necessary. - .. only:: html .. csv-table:: diff --git a/doc/source/index.rst b/doc/source/index.rst index bbd0c7f39..934e83e34 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -2,26 +2,17 @@ Python bindings to the OpenStack Manila API =========================================== This is a client for OpenStack Manila API. There's :doc:`a Python API -` (the :mod:`manilaclient` module), and a :doc:`command-line script -` (installed as :program:`manila`). Each implements the entire -OpenStack Manila API. See :ref:`project-structure` for more information. +` (the :mod:`manilaclient` module) for programmatic access, and +a command-line interface via the OpenStack CLI client. See +:ref:`project-structure` for more information. You'll need credentials for an OpenStack cloud that implements the Manila API in order to use the manila client. Command-Line Reference ~~~~~~~~~~~~~~~~~~~~~~ -There are two shell implementations in python-manilaclient. - -.. important:: - - The legacy "manila" shell client is deprecated as of version ``5.0.0``. - A future version of python-manilaclient may not ship this legacy shell - client. If you rely on it, it is highly recommended that you begin using - the openstack CLI client right away. Refer to the `mapping guide - `_ to help with this transition. - -The "openstack" CLI client intends to be fully compatible with the manila API: +Use the "openstack" CLI client to interact with the Manila API from the +command line: .. toctree:: :maxdepth: 1 @@ -29,14 +20,6 @@ The "openstack" CLI client intends to be fully compatible with the manila API: cli/osc_plugin_cli cli/decoder -The legacy "manila" client is deprecated and may not support newer API -features: - -.. toctree:: - :maxdepth: 2 - - user/shell - Using the python module ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/source/user/shell.rst b/doc/source/user/shell.rst deleted file mode 100644 index 13553ae75..000000000 --- a/doc/source/user/shell.rst +++ /dev/null @@ -1,59 +0,0 @@ -The :program:`manila` shell utility -========================================= - -.. important:: - - This shell client is deprecated as of version ``5.0.0``. A future - version of python-manilaclient may not ship this legacy shell client. If - you rely on it, it is highly recommended that you begin using the - openstack CLI client right away. Refer to the `mapping guide - <../cli/decoder.html>`_ to help with this transition. - -.. program:: manila -.. highlight:: bash - -The :program:`manila` shell utility interacts with the OpenStack Manila API -from the command line. It supports the entirety of the OpenStack Manila API. - -You'll need to provide :program:`manila` with your OpenStack username and API -key. You can do this with the `--os-username`, `--os-password` and -`--os-tenant-name` options, but it's easier to just set them as environment -variables by setting two environment variables: - -.. envvar:: OS_USERNAME or MANILA_USERNAME - - Your OpenStack Manila username. - -.. envvar:: OS_PASSWORD or MANILA_PASSWORD - - Your password. - -.. envvar:: OS_TENANT_NAME or MANILA_PROJECT_ID - - Project for work. - -.. envvar:: OS_AUTH_URL or MANILA_URL - - The OpenStack API server URL. - -.. envvar:: OS_SHARE_API_VERSION - - The OpenStack Shared Filesystems API version. - -For example, in Bash you'd use:: - - export OS_USERNAME=foo - export OS_PASSWORD=bar - export OS_TENANT_NAME=foobarproject - export OS_AUTH_URL=http://... - export OS_SHARE_API_VERSION=2 - -From there, all shell commands take the form:: - - manila [arguments...] - -Run :program:`manila help` to get a full list of all possible commands, -and run :program:`manila help ` to get detailed help for that -command. - -.. program-output:: manila --help diff --git a/manilaclient/osc/v2/data/manila.csv b/manilaclient/osc/v2/data/manila.csv index dbfeba344..24199a7e4 100644 --- a/manilaclient/osc/v2/data/manila.csv +++ b/manilaclient/osc/v2/data/manila.csv @@ -1,3 +1,5 @@ +# NOTE: The legacy "manila" CLI has been removed. This file is maintained +# for historical reference and migration purposes. manila command,openstack command,command description --version,module list,List the client software version absolute-limits,share limits show --absolute,Print a list of absolute limits for a user diff --git a/manilaclient/shell.py b/manilaclient/shell.py deleted file mode 100644 index 4455defb0..000000000 --- a/manilaclient/shell.py +++ /dev/null @@ -1,858 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# Copyright 2014 Mirantis, Inc. -# 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. - -""" -Command-line interface to the OpenStack Manila API. -""" - -import argparse -import csv -import glob -from importlib import util as importlib_util -import itertools -import logging -import os -import pkgutil -import sys - -from oslo_utils import importutils - -from manilaclient import api_versions -from manilaclient import client -from manilaclient.common import cliutils -from manilaclient.common import constants -from manilaclient import exceptions as exc -import manilaclient.extension -from manilaclient.v2 import shell as shell_v2 - -DEFAULT_OS_SHARE_API_VERSION = api_versions.MAX_VERSION -DEFAULT_MANILA_ENDPOINT_TYPE = 'publicURL' -DEFAULT_MAJOR_OS_SHARE_API_VERSION = "2" -V1_MAJOR_VERSION = '1' -V2_MAJOR_VERSION = '2' - -try: - osprofiler_profiler = importutils.try_import("osprofiler.profiler") -except ImportError: - pass - - -logger = logging.getLogger(__name__) - - -class AllowOnlyOneAliasAtATimeAction(argparse.Action): - """Allows only one alias of argument to be used at a time.""" - - def __call__(self, parser, namespace, values, option_string=None): - # NOTE(vponomaryov): this method is redefinition of - # argparse.Action.__call__ interface - - if not hasattr(self, 'calls'): - self.calls = {} - - if self.dest not in self.calls: - self.calls[self.dest] = set() - - local_values = sorted(values) if isinstance(values, list) else values - self.calls[self.dest].add(str(local_values)) - - if len(self.calls[self.dest]) == 1: - setattr(namespace, self.dest, local_values) - else: - msg = "Only one alias is allowed at a time." - raise argparse.ArgumentError(self, msg) - - -class ManilaClientArgumentParser(argparse.ArgumentParser): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - # NOTE(vponomaryov): Register additional action to be used by arguments - # with multiple aliases. - self.register('action', 'single_alias', AllowOnlyOneAliasAtATimeAction) - - def error(self, message): - """error(message: string) - - Prints a usage message incorporating the message to stderr and - exits. - """ - self.print_usage(sys.stderr) - # FIXME(lzyeval): if changes occur in argparse.ArgParser._check_value - choose_from = ' (choose from' - progparts = self.prog.partition(' ') - self.exit( - 2, - f"error: {message.split(choose_from)[0]}\n" - f"Try '{progparts[0]} help {progparts[2]}' " - f"for more information.\n", - ) - - def _get_option_tuples(self, option_string): - """Avoid ambiguity in argument abbreviation. - - Manilaclient uses aliases for command parameters and this method - is used for avoiding parameter ambiguity alert. - """ - option_tuples = super()._get_option_tuples(option_string) - if len(option_tuples) > 1: - opt_strings_list = [] - opts = [] - for opt in option_tuples: - if opt[0].option_strings not in opt_strings_list: - opt_strings_list.append(opt[0].option_strings) - opts.append(opt) - return opts - return option_tuples - - -class OpenStackManilaShell: - def get_base_parser(self): - parser = ManilaClientArgumentParser( - prog='manila', - description=__doc__.strip(), - epilog='See "manila help COMMAND" for help on a specific command.', - add_help=False, - formatter_class=OpenStackHelpFormatter, - ) - - # Global arguments - parser.add_argument( - '-h', '--help', action='store_true', help=argparse.SUPPRESS - ) - - parser.add_argument( - '--version', action='version', version=manilaclient.__version__ - ) - - parser.add_argument( - '-d', - '--debug', - action='store_true', - default=cliutils.env( - 'manilaclient_DEBUG', 'MANILACLIENT_DEBUG', default=False - ), - help="Print debugging output.", - ) - - parser.add_argument( - '--os-cache', - default=cliutils.env('OS_CACHE', default=False), - action='store_true', - help='Use the auth token cache. Defaults to env[OS_CACHE].', - ) - - parser.add_argument( - '--os-reset-cache', - default=False, - action='store_true', - help='Delete cached password and auth token.', - ) - - parser.add_argument( - '--os-user-id', - metavar='', - default=cliutils.env('OS_USER_ID'), - help=('Defaults to env [OS_USER_ID].'), - ) - parser.add_argument('--os_user_id', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-username', - metavar='', - default=cliutils.env('OS_USERNAME', 'MANILA_USERNAME'), - help='Defaults to env[OS_USERNAME].', - ) - parser.add_argument('--os_username', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-password', - metavar='', - default=cliutils.env('OS_PASSWORD', 'MANILA_PASSWORD'), - help='Defaults to env[OS_PASSWORD].', - ) - parser.add_argument('--os_password', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-tenant-name', - metavar='', - default=cliutils.env('OS_TENANT_NAME', 'MANILA_PROJECT_ID'), - help='Defaults to env[OS_TENANT_NAME].', - ) - parser.add_argument('--os_tenant_name', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-project-name', - metavar='', - default=cliutils.env('OS_PROJECT_NAME'), - help=( - 'Another way to specify tenant name. ' - 'This option is mutually exclusive with ' - '--os-tenant-name. ' - 'Defaults to env[OS_PROJECT_NAME].' - ), - ) - parser.add_argument('--os_project_name', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-tenant-id', - metavar='', - default=cliutils.env('OS_TENANT_ID', 'MANILA_TENANT_ID'), - help='Defaults to env[OS_TENANT_ID].', - ) - parser.add_argument('--os_tenant_id', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-project-id', - metavar='', - default=cliutils.env('OS_PROJECT_ID'), - help=( - 'Another way to specify tenant ID. ' - 'This option is mutually exclusive with ' - '--os-tenant-id. ' - 'Defaults to env[OS_PROJECT_ID].' - ), - ) - parser.add_argument('--os_project_id', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-user-domain-id', - metavar='', - default=cliutils.env('OS_USER_DOMAIN_ID'), - help=( - 'OpenStack user domain ID. Defaults to env[OS_USER_DOMAIN_ID].' - ), - ) - parser.add_argument('--os_user_domain_id', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-user-domain-name', - metavar='', - default=cliutils.env('OS_USER_DOMAIN_NAME'), - help=( - 'OpenStack user domain name. ' - 'Defaults to env[OS_USER_DOMAIN_NAME].' - ), - ) - parser.add_argument('--os_user_domain_name', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-project-domain-id', - metavar='', - default=cliutils.env('OS_PROJECT_DOMAIN_ID'), - help='Defaults to env[OS_PROJECT_DOMAIN_ID].', - ) - parser.add_argument('--os_project_domain_id', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-project-domain-name', - metavar='', - default=cliutils.env('OS_PROJECT_DOMAIN_NAME'), - help='Defaults to env[OS_PROJECT_DOMAIN_NAME].', - ) - parser.add_argument('--os_project_domain_name', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-auth-url', - metavar='', - default=cliutils.env('OS_AUTH_URL', 'MANILA_URL'), - help='Defaults to env[OS_AUTH_URL].', - ) - parser.add_argument('--os_auth_url', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-region-name', - metavar='', - default=cliutils.env('OS_REGION_NAME', 'MANILA_REGION_NAME'), - help='Defaults to env[OS_REGION_NAME].', - ) - parser.add_argument('--os_region_name', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-token', - metavar='', - default=cliutils.env('OS_TOKEN'), - help='Defaults to env[OS_TOKEN].', - ) - parser.add_argument('--os_token', help=argparse.SUPPRESS) - - parser.add_argument( - '--bypass-url', - metavar='', - default=cliutils.env( - 'OS_MANILA_BYPASS_URL', 'MANILACLIENT_BYPASS_URL' - ), - help=( - "Use this API endpoint instead of the " - "Service Catalog. Defaults to " - "env[OS_MANILA_BYPASS_URL]." - ), - ) - parser.add_argument('--bypass_url', help=argparse.SUPPRESS) - - parser.add_argument( - '--service-type', - metavar='', - help='Defaults to compute for most actions.', - ) - parser.add_argument('--service_type', help=argparse.SUPPRESS) - - parser.add_argument( - '--service-name', - metavar='', - default=cliutils.env( - 'OS_MANILA_SERVICE_NAME', 'MANILA_SERVICE_NAME' - ), - help='Defaults to env[OS_MANILA_SERVICE_NAME].', - ) - parser.add_argument('--service_name', help=argparse.SUPPRESS) - - parser.add_argument( - '--share-service-name', - metavar='', - default=cliutils.env( - 'OS_MANILA_SHARE_SERVICE_NAME', 'MANILA_share_service_name' - ), - help='Defaults to env[OS_MANILA_SHARE_SERVICE_NAME].', - ) - parser.add_argument('--share_service_name', help=argparse.SUPPRESS) - - parser.add_argument( - '--endpoint-type', - metavar='', - default=cliutils.env( - 'OS_MANILA_ENDPOINT_TYPE', - 'MANILA_ENDPOINT_TYPE', - default=DEFAULT_MANILA_ENDPOINT_TYPE, - ), - help='Defaults to env[OS_MANILA_ENDPOINT_TYPE] or ' - + DEFAULT_MANILA_ENDPOINT_TYPE - + '.', - ) - parser.add_argument('--endpoint_type', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-share-api-version', - metavar='', - default=cliutils.env( - 'OS_SHARE_API_VERSION', default=DEFAULT_OS_SHARE_API_VERSION - ), - help='Accepts 1.x to override default ' - 'to env[OS_SHARE_API_VERSION].', - ) - parser.add_argument('--os_share_api_version', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-cacert', - metavar='', - default=cliutils.env('OS_CACERT', default=None), - help='Specify a CA bundle file to use in ' - 'verifying a TLS (https) server certificate. ' - 'Defaults to env[OS_CACERT].', - ) - - parser.add_argument( - '--insecure', - default=cliutils.env( - 'manilaclient_INSECURE', 'MANILACLIENT_INSECURE', default=False - ), - action='store_true', - help=argparse.SUPPRESS, - ) - - parser.add_argument( - '--retries', - metavar='', - type=int, - default=0, - help='Number of retries.', - ) - - parser.add_argument( - '--os-cert', - metavar='', - default=cliutils.env('OS_CERT'), - help='Defaults to env[OS_CERT].', - ) - parser.add_argument('--os_cert', help=argparse.SUPPRESS) - - parser.add_argument( - '--os-key', - metavar='', - default=cliutils.env('OS_KEY'), - help='Defaults to env[OS_KEY].', - ) - parser.add_argument('--os_key', help=argparse.SUPPRESS) - - if osprofiler_profiler: - parser.add_argument( - '--profile', - metavar='HMAC_KEY', - default=cliutils.env('OS_PROFILE'), - help='HMAC key to use for encrypting ' - 'context data for performance profiling ' - 'of operation. This key needs to match the ' - 'one configured on the manila api server. ' - 'Without key the profiling will not be ' - 'triggered even if osprofiler is enabled ' - 'on server side. Defaults to ' - 'env[OS_PROFILE].', - ) - - parser.set_defaults(func=self.do_help) - parser.set_defaults(command='') - - return parser - - def get_subcommand_parser(self, version): - parser = self.get_base_parser() - - self.subcommands = {} - subparsers = parser.add_subparsers(metavar='') - - try: - actions_module = { - V2_MAJOR_VERSION: shell_v2, - }[version] - except KeyError: - actions_module = shell_v2 - - self._find_actions(subparsers, actions_module) - self._find_actions(subparsers, self) - - for extension in self.extensions: - self._find_actions(subparsers, extension.module) - - self._add_bash_completion_subparser(subparsers) - - return parser - - def _discover_extensions(self, api_version): - extensions = [] - for name, module in itertools.chain( - self._discover_via_python_path(), - self._discover_via_contrib_path(api_version), - ): - extension = manilaclient.extension.Extension(name, module) - extensions.append(extension) - - return extensions - - def _discover_via_python_path(self): - for module_loader, name, ispkg in pkgutil.iter_modules(): - if name.endswith('python_manilaclient_ext'): - if not hasattr(module_loader, 'load_module'): - # Python 2.6 compat: actually get an ImpImporter obj - module_loader = module_loader.find_module(name) - - module = module_loader.load_module(name) - yield name, module - - def _load_module(self, name, path): - module_spec = importlib_util.spec_from_file_location(name, path) - module = importlib_util.module_from_spec(module_spec) - module_spec.loader.exec_module(module) - return module - - def _discover_via_contrib_path(self, api_version): - module_path = os.path.dirname(os.path.abspath(__file__)) - version_str = 'v' + api_version.get_major_version() - ext_path = os.path.join(module_path, version_str, 'contrib') - ext_glob = os.path.join(ext_path, "*.py") - - for ext_path in glob.iglob(ext_glob): - name = os.path.basename(ext_path)[:-3] - - if name == "__init__": - continue - - module = self._load_module(name, ext_path) - yield name, module - - def _add_bash_completion_subparser(self, subparsers): - subparser = subparsers.add_parser( - 'bash_completion', - add_help=False, - formatter_class=OpenStackHelpFormatter, - ) - - self.subcommands['bash_completion'] = subparser - subparser.set_defaults(func=self.do_bash_completion) - - def _find_actions(self, subparsers, actions_module): - for attr in (a for a in dir(actions_module) if a.startswith('do_')): - # I prefer to be hypen-separated instead of underscores. - command = attr[3:].replace('_', '-') - callback = getattr(actions_module, attr) - desc = callback.__doc__ or '' - help = desc.strip() - arguments = getattr(callback, 'arguments', []) - - subparser = subparsers.add_parser( - command, - help=help, - description=desc, - add_help=False, - formatter_class=OpenStackHelpFormatter, - ) - - subparser.add_argument( - '-h', - '--help', - action='help', - help=argparse.SUPPRESS, - ) - - self.subcommands[command] = subparser - for args, kwargs in arguments: - subparser.add_argument(*args, **kwargs) - subparser.set_defaults(func=callback) - - def setup_debugging(self, debug): - if not debug: - return - - streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s" - logging.basicConfig(level=logging.DEBUG, format=streamformat) - logging.getLogger('requests.packages.urllib3.connectionpool').setLevel( - logging.WARNING - ) - logging.getLogger('keystoneauth1.session').setLevel(logging.WARNING) - - def _build_subcommands_and_extensions(self, os_api_version, argv, options): - self.extensions = self._discover_extensions(os_api_version) - self._run_extension_hooks('__pre_parse_args__') - - self.parser = self.get_subcommand_parser( - os_api_version.get_major_version() - ) - - if argv and len(argv) > 1 and '--help' in argv: - argv = [x for x in argv if x != '--help'] - if argv[0] in self.subcommands: - self.subcommands[argv[0]].print_help() - return False - - if options.help or not argv: - self.parser.print_help() - return False - - args = self.parser.parse_args(argv) - self._run_extension_hooks('__post_parse_args__', args) - - return args - - def main(self, argv): - # Parse args once to find version and debug settings - parser = self.get_base_parser() - (options, args) = parser.parse_known_args(argv) - self.setup_debugging(options.debug) - - os_api_version = self._validate_input_api_version(options) - - # build available subcommands based on version - args = self._build_subcommands_and_extensions( - os_api_version, argv, options - ) - if not args: - return 0 - - # Short-circuit and deal with help right away. - if args.func == self.do_help: - self.do_help(args) - return 0 - elif args.func == self.do_bash_completion: - self.do_bash_completion(args) - return 0 - - if not options.os_share_api_version: - api_version = api_versions.get_api_version( - DEFAULT_MAJOR_OS_SHARE_API_VERSION - ) - else: - api_version = api_versions.get_api_version( - options.os_share_api_version - ) - - major_version_string = str(api_version.ver_major) - os_service_type = args.service_type - if not os_service_type: - os_service_type = constants.SERVICE_TYPES[major_version_string] - - os_endpoint_type = args.endpoint_type or DEFAULT_MANILA_ENDPOINT_TYPE - cert = args.os_cert or None - if cert and args.os_key: - cert = cert, args.os_key - - client_args = dict( - username=args.os_username, - password=args.os_password, - project_name=args.os_project_name or args.os_tenant_name, - auth_url=args.os_auth_url, - insecure=args.insecure, - region_name=args.os_region_name, - tenant_id=args.os_project_id or args.os_tenant_id, - endpoint_type=os_endpoint_type, - extensions=self.extensions, - service_type=os_service_type, - service_name=args.service_name, - retries=options.retries, - http_log_debug=args.debug, - cacert=args.os_cacert, - use_keyring=args.os_cache, - force_new_token=args.os_reset_cache, - user_id=args.os_user_id, - user_domain_id=args.os_user_domain_id, - user_domain_name=args.os_user_domain_name, - project_domain_id=args.os_project_domain_id, - project_domain_name=args.os_project_domain_name, - cert=cert, - input_auth_token=args.os_token, - service_catalog_url=args.bypass_url, - ) - - # Handle deprecated parameters - if args.share_service_name: - client_args['share_service_name'] = args.share_service_name - - self._validate_required_options( - args.os_tenant_name, - args.os_tenant_id, - args.os_project_name, - args.os_project_id, - args.os_token, - args.bypass_url, - client_args['auth_url'], - ) - - # This client is needed to discover the server api version. - temp_client = client.Client( - manilaclient.API_MAX_VERSION, **client_args - ) - - self.cs, discovered_version = self._discover_client( - temp_client, - os_api_version, - os_endpoint_type, - os_service_type, - client_args, - ) - - args = self._build_subcommands_and_extensions( - discovered_version, argv, options - ) - - profile = osprofiler_profiler and options.profile - if profile: - osprofiler_profiler.init(options.profile) - - try: - decoder_path = '{}/{}'.format( - os.path.dirname(os.path.abspath(__file__)), - 'osc/v2/data/manila.csv', - ) - with open(decoder_path) as f: - decoder_data = { - r['manila command']: r['openstack command'] - for r in csv.DictReader(f, skipinitialspace=True) - } - except Exception: - # this is fine - decoder_data = {} - - deprecation_message = ( - "manila CLI is deprecated and will be removed " - "in the future. Use openstack CLI instead." - ) - cmd = args.func.__name__.lstrip('do_').replace("_", "-") - if decoder_data and cmd in decoder_data: - deprecation_message = " ".join( - [ - deprecation_message, - "The equivalent command is \" openstack", - f"{decoder_data[cmd]}", - "\"", - ] - ) - - print(deprecation_message, file=sys.stderr) - - args.func(self.cs, args) - - if profile: - trace_id = osprofiler_profiler.get().get_base_id() - print(f"Profiling trace ID: {trace_id}") - print( - "To display trace use next command:\n" - f"osprofiler trace show --html {trace_id} " - ) - - def _discover_client( - self, - current_client, - os_api_version, - os_endpoint_type, - os_service_type, - client_args, - ): - if os_api_version == manilaclient.API_DEPRECATED_VERSION: - discovered_version = manilaclient.API_DEPRECATED_VERSION - os_service_type = constants.V1_SERVICE_TYPE - else: - discovered_version = api_versions.discover_version( - current_client, os_api_version - ) - - if not os_endpoint_type: - os_endpoint_type = DEFAULT_MANILA_ENDPOINT_TYPE - - if not os_service_type: - os_service_type = self._discover_service_type(discovered_version) - - if ( - discovered_version != manilaclient.API_MAX_VERSION - or os_service_type != constants.V1_SERVICE_TYPE - or os_endpoint_type != DEFAULT_MANILA_ENDPOINT_TYPE - ): - client_args['version'] = discovered_version - client_args['service_type'] = os_service_type - client_args['endpoint_type'] = os_endpoint_type - - return ( - client.Client(discovered_version, **client_args), - discovered_version, - ) - else: - return current_client, discovered_version - - def _discover_service_type(self, discovered_version): - major_version = discovered_version.get_major_version() - service_type = constants.SERVICE_TYPES[major_version] - return service_type - - def _validate_input_api_version(self, options): - if not options.os_share_api_version: - api_version = manilaclient.API_MAX_VERSION - else: - api_version = api_versions.get_api_version( - options.os_share_api_version - ) - return api_version - - def _validate_required_options( - self, - tenant_name, - tenant_id, - project_name, - project_id, - token, - service_catalog_url, - auth_url, - ): - if token and not service_catalog_url: - raise exc.CommandError( - "bypass_url missing: When specifying a token the bypass_url " - "must be set via --bypass-url or env[OS_MANILA_BYPASS_URL]" - ) - if service_catalog_url and not token: - raise exc.CommandError( - "Token missing: When specifying a bypass_url a token must be " - "set via --os-token or env[OS_TOKEN]" - ) - if token and service_catalog_url: - return - - if not (tenant_name or tenant_id or project_name or project_id): - raise exc.CommandError( - "You must provide a tenant_name, tenant_id, " - "project_id or project_name (with " - "project_domain_name or project_domain_id) via " - "--os-tenant-name or env[OS_TENANT_NAME], " - "--os-tenant-id or env[OS_TENANT_ID], " - "--os-project-id or env[OS_PROJECT_ID], " - "--os-project-name or env[OS_PROJECT_NAME], " - "--os-project-domain-id or env[OS_PROJECT_DOMAIN_ID] and " - "--os-project-domain-name or env[OS_PROJECT_DOMAIN_NAME]." - ) - - if not auth_url: - raise exc.CommandError( - "You must provide an auth url " - "via either --os-auth-url or env[OS_AUTH_URL]" - ) - - def _run_extension_hooks(self, hook_type, *args, **kwargs): - """Run hooks for all registered extensions.""" - for extension in self.extensions: - extension.run_hooks(hook_type, *args, **kwargs) - - def do_bash_completion(self, args): - """Print arguments for bash_completion. - - Prints all of the commands and options to stdout so that the - manila.bash_completion script doesn't have to hard code them. - """ - commands = set() - options = set() - for sc_str, sc in list(self.subcommands.items()): - commands.add(sc_str) - for option in sc._optionals._option_string_actions: - options.add(option) - - commands.remove('bash-completion') - commands.remove('bash_completion') - print(' '.join(commands | options)) - - @cliutils.arg( - 'command', - metavar='', - nargs='?', - help='Display help for ', - ) - def do_help(self, args): - """Display help about this program or one of its subcommands.""" - if args.command: - if args.command in self.subcommands: - self.subcommands[args.command].print_help() - else: - raise exc.CommandError( - f"'{args.command}' is not a valid subcommand" - ) - else: - self.parser.print_help() - - -# I'm picky about my shell help. -class OpenStackHelpFormatter(argparse.HelpFormatter): - def start_section(self, heading): - # Title-case the headings - heading = f'{heading[0].upper()}{heading[1:]}' - super().start_section(heading) - - -def main(): - try: - OpenStackManilaShell().main(sys.argv[1:]) - except KeyboardInterrupt: - print("... terminating manila client", file=sys.stderr) - sys.exit(130) - except Exception as e: - logger.debug(e, exc_info=1) - print(f"ERROR: {str(e)}", file=sys.stderr) - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/manilaclient/tests/functional/base.py b/manilaclient/tests/functional/base.py deleted file mode 100644 index c77ee7b1b..000000000 --- a/manilaclient/tests/functional/base.py +++ /dev/null @@ -1,649 +0,0 @@ -# Copyright 2014 Mirantis Inc. -# 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. - -import re -import traceback - -from oslo_log import log -from tempest.lib.cli import base -from tempest.lib.common.utils import data_utils -from tempest.lib import exceptions as lib_exc - -from manilaclient.common import constants -from manilaclient import config -from manilaclient.tests.functional import client -from manilaclient.tests.functional import utils - -CONF = config.CONF -LOG = log.getLogger(__name__) - - -class handle_cleanup_exceptions: - """Handle exceptions raised with cleanup operations. - - Always suppress errors when lib_exc.NotFound or lib_exc.Forbidden - are raised. - Suppress all other exceptions only in case config opt - 'suppress_errors_in_cleanup' is True. - """ - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, exc_traceback): - if isinstance(exc_value, (lib_exc.NotFound, lib_exc.Forbidden)): - return True - elif CONF.suppress_errors_in_cleanup: - LOG.error("Suppressed cleanup error: \n%s", traceback.format_exc()) - return True - return False # Don't suppress cleanup errors - - -class BaseTestCase(base.ClientTestBase): - # Will be cleaned up after test suite run - class_resources = [] - - # Will be cleaned up after single test run - method_resources = [] - - def setUp(self): - super().setUp() - self.addCleanup(self.clear_resources) - - @classmethod - def tearDownClass(cls): - super().tearDownClass() - cls.clear_resources(cls.class_resources) - - @classmethod - def clear_resources(cls, resources=None): - """Deletes resources, that were created in test suites. - - This method tries to remove resources from resource list, - if it is not found, assume it was deleted in test itself. - It is expected, that all resources were added as LIFO - due to restriction of deletion resources, that are in the chain. - :param resources: dict with keys 'type','id','client', - 'deletion_params' and 'deleted'. Optional 'deletion_params' - contains additional data needed to delete some type of resources. - Ex: - params = { - 'type': 'share_network_subnet', - 'id': 'share-network-subnet-id', - 'client': None, - 'deletion_params': { - 'share_network': 'share-network-id', - }, - 'deleted': False, - } - """ - - if resources is None: - resources = cls.method_resources - for res in resources: - if "deleted" not in res: - res["deleted"] = False - if "client" not in res: - res["client"] = cls.get_cleanup_client() - if not res["deleted"]: - res_id = res["id"] - client = res["client"] - deletion_params = res.get("deletion_params") - with handle_cleanup_exceptions(): - # TODO(vponomaryov): add support for other resources - if res["type"] == "share_type": - client.delete_share_type( - res_id, microversion=res["microversion"] - ) - client.wait_for_share_type_deletion( - res_id, microversion=res["microversion"] - ) - elif res["type"] == "share_network": - client.delete_share_network( - res_id, microversion=res["microversion"] - ) - client.wait_for_share_network_deletion( - res_id, microversion=res["microversion"] - ) - elif res["type"] == "share_network_subnet": - client.delete_share_network_subnet( - share_network_subnet=res_id, - share_network=deletion_params["share_network"], - microversion=res["microversion"], - ) - client.wait_for_share_network_subnet_deletion( - share_network_subnet=res_id, - share_network=deletion_params["share_network"], - microversion=res["microversion"], - ) - elif res["type"] == "share": - client.delete_share( - res_id, microversion=res["microversion"] - ) - client.wait_for_share_deletion( - res_id, microversion=res["microversion"] - ) - elif res["type"] == "snapshot": - client.delete_snapshot( - res_id, microversion=res["microversion"] - ) - client.wait_for_snapshot_deletion( - res_id, microversion=res["microversion"] - ) - elif res["type"] == "share_replica": - client.delete_share_replica( - res_id, microversion=res["microversion"] - ) - client.wait_for_share_replica_deletion( - res_id, microversion=res["microversion"] - ) - else: - LOG.warning( - "Provided unsupported resource type for " - "cleanup '%s'. Skipping.", - res["type"], - ) - res["deleted"] = True - - @classmethod - def get_admin_client(cls): - manilaclient = client.ManilaCLIClient( - username=CONF.admin_username, - password=CONF.admin_password, - tenant_name=CONF.admin_tenant_name, - project_domain_name=CONF.admin_project_domain_name or None, - project_domain_id=CONF.admin_project_domain_id or None, - user_domain_name=CONF.admin_user_domain_name or None, - user_domain_id=CONF.admin_user_domain_id or None, - uri=CONF.admin_auth_url or CONF.auth_url, - insecure=CONF.insecure, - cli_dir=CONF.manila_exec_dir, - ) - # Set specific for admin project share network - manilaclient.share_network = CONF.admin_share_network - return manilaclient - - @classmethod - def get_user_client(cls): - manilaclient = client.ManilaCLIClient( - username=CONF.username, - password=CONF.password, - tenant_name=CONF.tenant_name, - project_domain_name=CONF.project_domain_name or None, - project_domain_id=CONF.project_domain_id or None, - user_domain_name=CONF.user_domain_name or None, - user_domain_id=CONF.user_domain_id or None, - uri=CONF.auth_url, - insecure=CONF.insecure, - cli_dir=CONF.manila_exec_dir, - ) - # Set specific for user project share network - manilaclient.share_network = CONF.share_network - return manilaclient - - @property - def admin_client(self): - if not hasattr(self, '_admin_client'): - self._admin_client = self.get_admin_client() - return self._admin_client - - @property - def user_client(self): - if not hasattr(self, '_user_client'): - self._user_client = self.get_user_client() - return self._user_client - - def _get_clients(self): - return {'admin': self.admin_client, 'user': self.user_client} - - def skip_if_microversion_not_supported(self, microversion): - if not utils.is_microversion_supported(microversion): - raise self.skipException( - f"Microversion '{microversion}' is not supported." - ) - - @classmethod - def create_share_type( - cls, - name=None, - driver_handles_share_servers=True, - snapshot_support=None, - create_share_from_snapshot=None, - revert_to_snapshot=None, - mount_snapshot=None, - is_public=True, - client=None, - cleanup_in_class=False, - microversion=None, - extra_specs=None, - description=None, - ): - if client is None: - client = cls.get_admin_client() - data = { - "name": name, - "driver_handles_share_servers": driver_handles_share_servers, - "snapshot_support": snapshot_support, - "is_public": is_public, - "microversion": microversion, - "extra_specs": extra_specs, - "create_share_from_snapshot": create_share_from_snapshot, - "revert_to_snapshot": revert_to_snapshot, - "mount_snapshot": mount_snapshot, - } - if description: - data["description"] = description - share_type = client.create_share_type(**data) - resource = { - "type": "share_type", - "id": share_type["ID"], - "client": client, - "microversion": microversion, - } - if cleanup_in_class: - cls.class_resources.insert(0, resource) - else: - cls.method_resources.insert(0, resource) - return share_type - - @classmethod - def update_share_type( - cls, - share_type_id, - name=None, - is_public=None, - client=None, - microversion=None, - description=None, - ): - if client is None: - client = cls.get_admin_client() - data = { - "share_type_id": share_type_id, - "microversion": microversion, - } - if name is not None: - data["name"] = name - if description is not None: - data["description"] = description - if is_public is not None: - data["is_public"] = is_public - share_type = client.update_share_type(**data) - return share_type - - @classmethod - def create_share_network( - cls, - name=None, - description=None, - neutron_net_id=None, - neutron_subnet_id=None, - availability_zone=None, - client=None, - cleanup_in_class=False, - microversion=None, - ): - if client is None: - client = cls.get_admin_client() - share_network = client.create_share_network( - name=name, - description=description, - neutron_net_id=neutron_net_id, - neutron_subnet_id=neutron_subnet_id, - availability_zone=availability_zone, - microversion=microversion, - ) - resource = { - "type": "share_network", - "id": share_network["id"], - "client": client, - "microversion": microversion, - } - if cleanup_in_class: - cls.class_resources.insert(0, resource) - else: - cls.method_resources.insert(0, resource) - return share_network - - @classmethod - def add_share_network_subnet( - cls, - share_network, - neutron_net_id=None, - neutron_subnet_id=None, - availability_zone=None, - client=None, - cleanup_in_class=False, - microversion=None, - ): - if client is None: - client = cls.get_admin_client() - share_network_subnet = client.add_share_network_subnet( - share_network, neutron_net_id, neutron_subnet_id, availability_zone - ) - resource = { - "type": "share_network_subnet", - "id": share_network_subnet["id"], - "client": client, - "deletion_params": { - "share_network": share_network, - }, - "microversion": microversion, - } - if cleanup_in_class: - cls.class_resources.insert(0, resource) - else: - cls.method_resources.insert(0, resource) - return share_network_subnet - - @classmethod - def create_share( - cls, - share_protocol=None, - size=None, - share_network=None, - share_type=None, - name=None, - description=None, - public=False, - snapshot=None, - metadata=None, - client=None, - use_wait_option=False, - cleanup_in_class=False, - wait_for_creation=True, - microversion=None, - ): - client = client or cls.get_admin_client() - data = { - 'share_protocol': share_protocol or client.share_protocol, - 'size': size or 1, - 'name': name, - 'description': description, - 'public': public, - 'snapshot': snapshot, - 'metadata': metadata or {}, - 'microversion': microversion, - 'wait': use_wait_option, - } - - share_type = share_type or CONF.share_type - share_network = share_network or cls._determine_share_network_to_use( - client, share_type, microversion=microversion - ) - - data['share_type'] = share_type - data['share_network'] = share_network - share = client.create_share(**data) - resource = { - "type": "share", - "id": share["id"], - "client": client, - "microversion": microversion, - } - if cleanup_in_class: - cls.class_resources.insert(0, resource) - else: - cls.method_resources.insert(0, resource) - if wait_for_creation and not use_wait_option: - client.wait_for_resource_status( - share['id'], constants.STATUS_AVAILABLE - ) - return share - - @classmethod - def delete_share( - cls, - shares_to_delete, - share_group_id=None, - wait=False, - client=None, - microversion=None, - ): - client = client or cls.get_admin_client() - client.delete_share( - shares_to_delete, - share_group_id=share_group_id, - wait=wait, - microversion=microversion, - ) - - @classmethod - def soft_delete_share( - cls, shares_to_soft_delete, client=None, microversion=None - ): - client = client or cls.get_admin_client() - client.soft_delete_share( - shares_to_soft_delete, microversion=microversion - ) - - @classmethod - def restore_share(cls, shares_to_restore, client=None, microversion=None): - client = client or cls.get_admin_client() - client.restore_share(shares_to_restore, microversion=microversion) - - @classmethod - def create_share_transfer( - cls, share_id, name=None, client=None, microversion=None - ): - client = client or cls.get_admin_client() - return client.create_share_transfer( - share_id, name=name, microversion=microversion - ) - - @classmethod - def delete_share_transfer(cls, transfer, client=None, microversion=None): - client = client or cls.get_admin_client() - client.delete_share_transfer(transfer, microversion=microversion) - - @classmethod - def get_share_transfer(cls, transfer, client=None, microversion=None): - client = client or cls.get_admin_client() - return client.get_share_transfer(transfer, microversion=microversion) - - @classmethod - def list_share_transfer(cls, client=None, microversion=None): - client = client or cls.get_admin_client() - return client.list_share_transfer(microversion=microversion) - - @classmethod - def accept_share_transfer( - cls, transfer, auth_key, client=None, microversion=None - ): - client = client or cls.get_admin_client() - client.accept_share_transfer( - transfer, auth_key, microversion=microversion - ) - - @classmethod - def _determine_share_network_to_use( - cls, client, share_type, microversion=None - ): - """Determine what share network we need from the share type.""" - - # Get share type, determine if we need the share network - share_type = client.get_share_type( - share_type, microversion=microversion - ) - dhss_pattern = re.compile('driver_handles_share_servers : ([a-zA-Z]+)') - dhss = dhss_pattern.search(share_type['required_extra_specs']).group(1) - return client.share_network if dhss.lower() == 'true' else None - - @classmethod - def create_security_service( - cls, - type='ldap', - name=None, - description=None, - dns_ip=None, - ou=None, - server=None, - domain=None, - user=None, - password=None, - default_ad_site=None, - client=None, - cleanup_in_class=False, - microversion=None, - ): - if client is None: - client = cls.get_admin_client() - data = { - 'type': type, - 'name': name, - 'description': description, - 'user': user, - 'password': password, - 'server': server, - 'domain': domain, - 'dns_ip': dns_ip, - 'ou': ou, - 'microversion': microversion, - 'default_ad_site': default_ad_site, - } - ss = client.create_security_service(**data) - resource = { - "type": "share", - "id": ss["id"], - "client": client, - "microversion": microversion, - } - if cleanup_in_class: - cls.class_resources.insert(0, resource) - else: - cls.method_resources.insert(0, resource) - return ss - - @classmethod - def create_snapshot( - cls, - share, - name=None, - description=None, - force=False, - client=None, - wait_for_creation=True, - cleanup_in_class=False, - microversion=None, - ): - if client is None: - client = cls.get_admin_client() - data = { - 'share': share, - 'name': name, - 'description': description, - 'force': force, - 'microversion': microversion, - } - snapshot = client.create_snapshot(**data) - resource = { - "type": "snapshot", - "id": snapshot["id"], - "client": client, - "microversion": microversion, - } - if cleanup_in_class: - cls.class_resources.insert(0, resource) - else: - cls.method_resources.insert(0, resource) - if wait_for_creation: - client.wait_for_snapshot_status(snapshot['id'], 'available') - return snapshot - - @classmethod - def create_message( - cls, - client=None, - wait_for_creation=True, - cleanup_in_class=False, - microversion=None, - ): - """Trigger a 'no valid host' situation to generate a message.""" - if client is None: - client = cls.get_admin_client() - - extra_specs = { - 'vendor_name': 'foobar', - } - share_type_name = data_utils.rand_name("share-type") - cls.create_share_type( - name=share_type_name, - extra_specs=extra_specs, - driver_handles_share_servers=False, - client=client, - cleanup_in_class=cleanup_in_class, - microversion=microversion, - ) - - share_name = data_utils.rand_name("share") - share = cls.create_share( - name=share_name, - share_type=share_type_name, - cleanup_in_class=cleanup_in_class, - microversion=microversion, - wait_for_creation=False, - client=client, - ) - - client.wait_for_resource_status(share['id'], constants.STATUS_ERROR) - message = client.wait_for_message(share['id']) - - resource = { - "type": "message", - "id": message["ID"], - "client": client, - "microversion": microversion, - } - if cleanup_in_class: - cls.class_resources.insert(0, resource) - else: - cls.method_resources.insert(0, resource) - return message - - @classmethod - def create_share_replica( - cls, - share_id, - client=None, - wait_for_creation=True, - cleanup_in_class=False, - availability_zone=None, - share_network=None, - microversion=None, - ): - client = client or cls.get_user_client() - - share_replica = client.create_share_replica( - share_id, - availability_zone=availability_zone, - share_network=share_network, - microversion=microversion, - ) - if wait_for_creation: - share_replica = client.wait_for_share_replica_status( - share_replica['id'] - ) - - resource = { - "type": "share_replica", - "id": share_replica["id"], - "client": client, - "microversion": microversion, - } - if cleanup_in_class: - cls.class_resources.insert(0, resource) - else: - cls.method_resources.insert(0, resource) - return share_replica diff --git a/manilaclient/tests/functional/client.py b/manilaclient/tests/functional/client.py deleted file mode 100644 index da712fb50..000000000 --- a/manilaclient/tests/functional/client.py +++ /dev/null @@ -1,2379 +0,0 @@ -# Copyright 2014 Mirantis Inc. -# 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. - -import ast -import re -import time - -from oslo_utils import strutils -from tempest.lib.cli import base -from tempest.lib.cli import output_parser -from tempest.lib.common.utils import data_utils -from tempest.lib import exceptions as tempest_lib_exc - -from manilaclient.common import constants -from manilaclient import config -from manilaclient.tests.functional import exceptions -from manilaclient.tests.functional import utils - -CONF = config.CONF -MESSAGE = 'message' -SHARE = 'share' -SHARE_TYPE = 'share_type' -SHARE_NETWORK = 'share_network' -SHARE_NETWORK_SUBNET = 'share_network_subnet' -SHARE_SERVER = 'share_server' -SNAPSHOT = 'snapshot' -SHARE_REPLICA = 'share_replica' -TRANSFER = 'transfer' - - -def not_found_wrapper(f): - def wrapped_func(self, *args, **kwargs): - try: - return f(self, *args, **kwargs) - except tempest_lib_exc.CommandFailed as e: - for regexp in ( - r'No (\w+) with a name or ID', - r'not(.*){0,5}found', - ): - if re.search(regexp, str(e.stderr)): - # Raise appropriate 'NotFound' error - raise tempest_lib_exc.NotFound() - raise - - return wrapped_func - - -def forbidden_wrapper(f): - def wrapped_func(self, *args, **kwargs): - try: - return f(self, *args, **kwargs) - except tempest_lib_exc.CommandFailed as e: - if re.search('HTTP 403', str(e.stderr)): - # Raise appropriate 'Forbidden' error. - raise tempest_lib_exc.Forbidden() - raise - - return wrapped_func - - -class ManilaCLIClient(base.CLIClient): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if CONF.enable_protocols: - self.share_protocol = CONF.enable_protocols[0] - else: - msg = "Configuration option 'enable_protocols' is not defined." - raise exceptions.InvalidConfiguration(reason=msg) - self.build_interval = CONF.build_interval - self.build_timeout = CONF.build_timeout - - def manila( - self, - action, - flags='', - params='', - fail_ok=False, - endpoint_type='publicURL', - merge_stderr=False, - microversion=None, - ): - """Executes manila command for the given action. - - :param action: the cli command to run using manila - :type action: string - :param flags: any optional cli flags to use. For specifying - microversion, please, use 'microversion' param - :type flags: string - :param params: any optional positional args to use - :type params: string - :param fail_ok: if True an exception is not raised when the - cli return code is non-zero - :type fail_ok: boolean - :param endpoint_type: the type of endpoint for the service - :type endpoint_type: string - :param merge_stderr: if True the stderr buffer is merged into stdout - :type merge_stderr: boolean - :param microversion: API microversion to be used for request - :type microversion: str - """ - flags += f' --endpoint-type {endpoint_type}' - if not microversion: - # NOTE(vponomaryov): use max API version from config - microversion = CONF.max_api_microversion - - # NOTE(vponomaryov): it is possible that param 'flags' already - # can contain '--os-share-api-version' key. If it is so and we - # reached this part then value of 'microversion' param will be - # used and existing one in 'flags' param will be ignored. - flags += f' --os-share-api-version {microversion}' - return self.cmd_with_auth( - 'manila', action, flags, params, fail_ok, merge_stderr - ) - - def wait_for_resource_deletion( - self, - res_type, - res_id, - interval=3, - timeout=180, - microversion=None, - **kwargs, - ): - """Resource deletion waiter. - - :param res_type: text -- type of resource - :param res_id: text -- ID of resource to use for deletion check - :param interval: int -- interval between requests in seconds - :param timeout: int -- total time in seconds to wait for deletion - :param args: dict -- additional keyword arguments for deletion func - """ - if res_type == SHARE_TYPE: - func = self.is_share_type_deleted - elif res_type == SHARE_NETWORK: - func = self.is_share_network_deleted - elif res_type == SHARE_NETWORK_SUBNET: - func = self.is_share_network_subnet_deleted - elif res_type == SHARE_SERVER: - func = self.is_share_server_deleted - elif res_type == SHARE: - func = self.is_share_deleted - elif res_type == SNAPSHOT: - func = self.is_snapshot_deleted - elif res_type == MESSAGE: - func = self.is_message_deleted - elif res_type == SHARE_REPLICA: - func = self.is_share_replica_deleted - elif res_type == TRANSFER: - func = self.is_share_transfer_deleted - else: - raise exceptions.InvalidResource(message=res_type) - - end_loop_time = time.time() + timeout - deleted = func(res_id, microversion=microversion, **kwargs) - - while not (deleted or time.time() > end_loop_time): - time.sleep(interval) - deleted = func(res_id, microversion=microversion, **kwargs) - - if not deleted: - raise exceptions.ResourceReleaseFailed( - res_type=res_type, res_id=res_id - ) - - def list_availability_zones(self, columns=None, microversion=None): - """List availability zones. - - :param columns: comma separated string of columns. - Example, "--columns id,name" - :param microversion: API microversion that should be used. - """ - cmd = 'availability-zone-list' - if columns is not None: - cmd += ' --columns ' + columns - azs_raw = self.manila(cmd, microversion=microversion) - azs = output_parser.listing(azs_raw) - return azs - - # Share types - - def create_share_type( - self, - name=None, - driver_handles_share_servers=True, - snapshot_support=None, - create_share_from_snapshot=None, - revert_to_snapshot=None, - mount_snapshot=None, - is_public=True, - microversion=None, - extra_specs=None, - description=None, - ): - """Creates share type. - - :param name: text -- name of share type to use, if not set then - autogenerated will be used - :param description: text -- description of share type to use. - Default is None. - :param driver_handles_share_servers: bool/str -- boolean or its - string alias. Default is True. - :param snapshot_support: bool/str -- boolean or its - string alias. Default is None. - :param is_public: bool/str -- boolean or its string alias. Default is - True. - :param extra_specs: -- dictionary of extra specs Default is None. - :param create_share_from_snapshot: -- boolean or its string - alias. Default is None. - :param revert_to_snapshot: -- boolean or its string alias. Default is - None. - :param mount_snapshot: -- boolean or its string alias. Default is None. - """ - if name is None: - name = data_utils.rand_name('manilaclient_functional_test') - dhss = driver_handles_share_servers - if not isinstance(dhss, str): - dhss = str(dhss) - if not isinstance(is_public, str): - is_public = str(is_public) - - cmd = f'type-create {name} {dhss} --is-public {is_public} ' - - if description is not None: - cmd += " --description " + description - - if snapshot_support is not None: - if not isinstance(snapshot_support, str): - snapshot_support = str(snapshot_support) - cmd += " --snapshot-support " + snapshot_support - - if create_share_from_snapshot is not None: - if not isinstance(create_share_from_snapshot, str): - create_share_from_snapshot = str(create_share_from_snapshot) - cmd += ( - " --create-share-from-snapshot-support " - + create_share_from_snapshot - ) - - if revert_to_snapshot is not None: - if not isinstance(revert_to_snapshot, str): - revert_to_snapshot = str(revert_to_snapshot) - cmd += " --revert-to-snapshot-support " + revert_to_snapshot - - if mount_snapshot is not None: - if not isinstance(mount_snapshot, str): - mount_snapshot = str(mount_snapshot) - cmd += " --mount-snapshot-support " + mount_snapshot - - if extra_specs is not None: - extra_spec_str = '' - for k, v in extra_specs.items(): - if not isinstance(v, str): - extra_specs[k] = str(v) - extra_spec_str += f"{k}='{v}' " - cmd += " --extra_specs " + extra_spec_str - - share_type_raw = self.manila(cmd, microversion=microversion) - share_type = utils.details(share_type_raw) - return share_type - - def update_share_type( - self, - share_type_id, - name=None, - is_public=None, - microversion=None, - description=None, - ): - """Update share type. - - :param share_type_id: text -- id of share type. - :param name: text -- new name of share type, if not set then - it will not be updated. - :param description: text -- new description of share type. - if not set then it will not be updated. - :param is_public: bool/str -- boolean or its string alias. - new visibility of the share type.If set to True, share - type will be available to all tenants in the cloud. - """ - - cmd = f'type-update {share_type_id} ' - - if is_public is not None: - if not isinstance(is_public, str): - is_public = str(is_public) - cmd += " --is_public " + is_public - - if description: - cmd += " --description " + description - elif description == "": - cmd += ' --description "" ' - - if name: - cmd += " --name " + name - - share_type_raw = self.manila(cmd, microversion=microversion) - share_type = utils.details(share_type_raw) - return share_type - - @not_found_wrapper - def delete_share_type(self, share_type, microversion=None): - """Deletes share type by its Name or ID.""" - return self.manila( - f'type-delete {share_type}', microversion=microversion - ) - - def list_share_types( - self, list_all=True, columns=None, search_opts=None, microversion=None - ): - """List share types. - - :param list_all: bool -- whether to list all share types or only public - :param search_opts: dict search_opts for filter search. - :param columns: comma separated string of columns. - Example, "--columns id,name" - """ - cmd = 'type-list' - if list_all: - cmd += ' --all' - if search_opts is not None: - extra_specs = search_opts.get('extra_specs') - if extra_specs: - cmd += ' --extra_specs' - for spec_key in extra_specs.keys(): - cmd += ' ' + spec_key + '=' + extra_specs[spec_key] - if columns is not None: - cmd += ' --columns ' + columns - share_types_raw = self.manila(cmd, microversion=microversion) - share_types = output_parser.listing(share_types_raw) - return share_types - - def get_share_type(self, share_type, microversion=None): - """Get share type. - - :param share_type: str -- Name or ID of share type, or None to - retrieve default share type - """ - share_types = self.list_share_types(True, microversion=microversion) - for stype in share_types: - if share_type is None and stype["is_default"] == 'YES': - return stype - elif share_type in (stype['ID'], stype['Name']): - return stype - raise tempest_lib_exc.NotFound() - - def is_share_type_deleted(self, share_type, microversion=None): - """Says whether share type is deleted or not. - - :param share_type: text -- Name or ID of share type - """ - # NOTE(vponomaryov): we use 'list' operation because there is no - # 'get/show' operation for share-types available for CLI - share_types = self.list_share_types( - list_all=True, microversion=microversion - ) - for list_element in share_types: - if share_type in (list_element['ID'], list_element['Name']): - return False - return True - - def wait_for_share_type_deletion(self, share_type, microversion=None): - """Wait for share type deletion by its Name or ID. - - :param share_type: text -- Name or ID of share type - """ - self.wait_for_resource_deletion( - SHARE_TYPE, - res_id=share_type, - interval=2, - timeout=6, - microversion=microversion, - ) - - def get_project_id(self, name_or_id): - identity_api_version = '3' - flags = ( - f"--os-username {CONF.admin_username} " - f"--os-project-name {CONF.admin_tenant_name} " - f"--os-password {CONF.admin_password} " - f"--os-identity-api-version {identity_api_version} " - ) - - if identity_api_version == "3": - if CONF.admin_project_domain_name: - flags += ( - f"--os-project-domain-name " - f"{CONF.admin_project_domain_name} " - ) - elif CONF.admin_project_domain_id: - flags += ( - f"--os-project-domain-id {CONF.admin_project_domain_id} " - ) - - if CONF.admin_user_domain_name: - flags += ( - f"--os-user-domain-name {CONF.admin_user_domain_name} " - ) - elif CONF.admin_user_domain_id: - flags += f"--os-user-domain-id {CONF.admin_user_domain_id} " - - project_id = self.openstack( - f'project show -f value -c id {name_or_id}', flags=flags - ) - return project_id.strip() - - @not_found_wrapper - def add_share_type_access( - self, share_type_name_or_id, project_id, microversion=None - ): - data = dict(st=share_type_name_or_id, project=project_id) - self.manila( - 'type-access-add {st} {project}'.format(**data), - microversion=microversion, - ) - - @not_found_wrapper - def remove_share_type_access( - self, share_type_name_or_id, project_id, microversion=None - ): - data = dict(st=share_type_name_or_id, project=project_id) - self.manila( - 'type-access-remove {st} {project}'.format(**data), - microversion=microversion, - ) - - @not_found_wrapper - def list_share_type_access(self, share_type_id, microversion=None): - projects_raw = self.manila( - f'type-access-list {share_type_id}', microversion=microversion - ) - projects = output_parser.listing(projects_raw) - project_ids = [pr['Project_ID'] for pr in projects] - return project_ids - - @not_found_wrapper - def set_share_type_extra_specs( - self, share_type_name_or_id, extra_specs, microversion=None - ): - """Set key-value pair for share type.""" - if not (isinstance(extra_specs, dict) and extra_specs): - raise exceptions.InvalidData( - message=f'Provided invalid extra specs - {extra_specs}' - ) - cmd = f'type-key {share_type_name_or_id} set ' - for key, value in extra_specs.items(): - cmd += f'{key}={value} ' - return self.manila(cmd, microversion=microversion) - - @not_found_wrapper - def unset_share_type_extra_specs( - self, share_type_name_or_id, extra_specs_keys, microversion=None - ): - """Unset key-value pair for share type.""" - if not ( - isinstance(extra_specs_keys, (list, tuple, set)) - and extra_specs_keys - ): - raise exceptions.InvalidData( - message=f'Provided invalid extra specs - {extra_specs_keys}' - ) - cmd = f'type-key {share_type_name_or_id} unset ' - for key in extra_specs_keys: - cmd += f'{key} ' - return self.manila(cmd, microversion=microversion) - - def list_all_share_type_extra_specs(self, microversion=None): - """List extra specs for all share types.""" - extra_specs_raw = self.manila( - 'extra-specs-list', microversion=microversion - ) - extra_specs = utils.listing(extra_specs_raw) - return extra_specs - - def list_share_type_extra_specs( - self, share_type_name_or_id, microversion=None - ): - """List extra specs for specific share type by its Name or ID.""" - all_share_types = self.list_all_share_type_extra_specs( - microversion=microversion - ) - for share_type in all_share_types: - if share_type_name_or_id in (share_type['ID'], share_type['Name']): - return share_type['all_extra_specs'] - raise exceptions.ShareTypeNotFound(share_type=share_type_name_or_id) - - # Share networks - - def create_share_network( - self, - name=None, - description=None, - neutron_net_id=None, - neutron_subnet_id=None, - availability_zone=None, - microversion=None, - ): - """Creates share network. - - :param name: text -- desired name of new share network - :param description: text -- desired description of new share network - :param neutron_net_id: text -- ID of Neutron network - :param neutron_subnet_id: text -- ID of Neutron subnet - """ - params = self._combine_share_network_data( - name=name, - description=description, - neutron_net_id=neutron_net_id, - neutron_subnet_id=neutron_subnet_id, - availability_zone=availability_zone, - ) - share_network_raw = self.manila( - f'share-network-create {params}', microversion=microversion - ) - share_network = output_parser.details(share_network_raw) - return share_network - - def _combine_share_network_data( - self, - name=None, - description=None, - neutron_net_id=None, - neutron_subnet_id=None, - availability_zone=None, - ): - """Combines params for share network operations 'create' and 'update'. - - :returns: text -- set of CLI parameters - """ - data = dict() - if name is not None: - data['--name'] = name - if description is not None: - data['--description'] = description - if neutron_net_id is not None: - data['--neutron_net_id'] = neutron_net_id - if neutron_subnet_id is not None: - data['--neutron_subnet_id'] = neutron_subnet_id - if availability_zone is not None: - data['--availability_zone'] = availability_zone - cmd = '' - for key, value in data.items(): - cmd += f"{key}={value} " - return cmd - - @not_found_wrapper - def get_share_network(self, share_network, microversion=None): - """Returns share network by its Name or ID.""" - share_network_raw = self.manila( - f'share-network-show {share_network}', microversion=microversion - ) - share_network = output_parser.details(share_network_raw) - return share_network - - @not_found_wrapper - def update_share_network( - self, - share_network, - name=None, - description=None, - neutron_net_id=None, - neutron_subnet_id=None, - microversion=None, - ): - """Updates share-network by its name or ID. - - :param name: text -- new name for share network - :param description: text -- new description for share network - :param neutron_net_id: text -- ID of some Neutron network - :param neutron_subnet_id: text -- ID of some Neutron subnet - """ - sn_params = self._combine_share_network_data( - name=name, - description=description, - neutron_net_id=neutron_net_id, - neutron_subnet_id=neutron_subnet_id, - ) - share_network_raw = self.manila( - 'share-network-update {sn} {params}'.format( - **dict(sn=share_network, params=sn_params) - ), - microversion=microversion, - ) - share_network = output_parser.details(share_network_raw) - return share_network - - @not_found_wrapper - def delete_share_network(self, share_network, microversion=None): - """Deletes share network by its Name or ID.""" - return self.manila( - f'share-network-delete {share_network}', microversion=microversion - ) - - @staticmethod - def _stranslate_to_cli_optional_param(param): - if len(param) < 1 or not isinstance(param, str): - raise exceptions.InvalidData( - 'Provided wrong parameter for translation.' - ) - while not param[0:2] == '--': - param = '-' + param - return param.replace('_', '-') - - def list_share_networks( - self, all_tenants=False, filters=None, columns=None, microversion=None - ): - """List share networks. - - :param all_tenants: bool -- whether to list share-networks that belong - only to current project or for all tenants. - :param filters: dict -- filters for listing of share networks. - Example, input: - {'project_id': 'foo'} - {'-project_id': 'foo'} - {'--project_id': 'foo'} - {'project-id': 'foo'} - will be transformed to filter parameter "--project-id=foo" - :param columns: comma separated string of columns. - Example, "--columns id" - """ - cmd = 'share-network-list ' - if columns is not None: - cmd += ' --columns ' + columns - if all_tenants: - cmd += ' --all-tenants ' - if filters and isinstance(filters, dict): - for k, v in filters.items(): - cmd += f'{self._stranslate_to_cli_optional_param(k)}={v} ' - share_networks_raw = self.manila(cmd, microversion=microversion) - share_networks = utils.listing(share_networks_raw) - return share_networks - - def share_network_reset_state( - self, id=None, state=None, microversion=None - ): - cmd = f'share-network-reset-state {id} ' - if state: - cmd += f'--state {state}' - share_network_raw = self.manila(cmd, microversion=microversion) - share_network = utils.listing(share_network_raw) - return share_network - - def share_network_security_service_add( - self, share_network_id, security_service_id, microversion=None - ): - cmd = ( - f'share-network-security-service-add {share_network_id} ' - f'{security_service_id}' - ) - self.manila(cmd, microversion=microversion) - - def share_network_security_service_update( - self, - share_network_id, - current_security_service_id, - new_security_service_id, - microversion=None, - ): - cmd = ( - f'share-network-security-service-update {share_network_id} ' - f'{current_security_service_id} {new_security_service_id}' - ) - self.manila(cmd, microversion=microversion) - - def share_network_security_service_add_check( - self, - share_network_id, - security_service_id, - reset=False, - microversion=None, - ): - cmd = ( - f'share-network-security-service-add-check {share_network_id} ' - f'{security_service_id}' - ) - - if reset: - cmd += f'--reset {reset}' - - return output_parser.details( - self.manila(cmd, microversion=microversion) - ) - - def share_network_security_service_update_check( - self, - share_network_id, - current_security_service_id, - new_security_service_id, - reset=False, - microversion=None, - ): - cmd = ( - f'share-network-security-service-update-check {share_network_id} ' - f'{current_security_service_id} {new_security_service_id} ' - ) - - if reset: - cmd += f'--reset {reset}' - - return output_parser.details( - self.manila(cmd, microversion=microversion) - ) - - def share_network_security_service_list( - self, share_network_id, microversion=None - ): - cmd = f'share-network-security-service-list {share_network_id}' - share_networks_raw = self.manila(cmd, microversion=microversion) - network_services = utils.listing(share_networks_raw) - return network_services - - def is_share_network_deleted(self, share_network, microversion=None): - """Says whether share network is deleted or not. - - :param share_network: text -- Name or ID of share network - """ - share_types = self.list_share_networks(True, microversion=microversion) - for list_element in share_types: - if share_network in (list_element['id'], list_element['name']): - return False - return True - - def wait_for_share_network_deletion( - self, share_network, microversion=None - ): - """Wait for share network deletion by its Name or ID. - - :param share_network: text -- Name or ID of share network - """ - self.wait_for_resource_deletion( - SHARE_NETWORK, - res_id=share_network, - interval=2, - timeout=6, - microversion=microversion, - ) - - def share_network_subnet_create_check( - self, - share_network_id, - neutron_net_id=None, - neutron_subnet_id=None, - availability_zone=None, - reset=False, - microversion=None, - ): - params = self._combine_share_network_subnet_data( - neutron_net_id=neutron_net_id, - neutron_subnet_id=neutron_subnet_id, - availability_zone=availability_zone, - ) - cmd = f'share-network-subnet-create-check {share_network_id} {params}' - - if reset: - cmd += f'--reset {reset}' - - return output_parser.details( - self.manila(cmd, microversion=microversion) - ) - - # Share Network Subnets - - def _combine_share_network_subnet_data( - self, - neutron_net_id=None, - neutron_subnet_id=None, - availability_zone=None, - ): - """Combines params for share network subnet 'create' operation. - - :returns: text -- set of CLI parameters - """ - data = dict() - if neutron_net_id is not None: - data['--neutron_net_id'] = neutron_net_id - if neutron_subnet_id is not None: - data['--neutron_subnet_id'] = neutron_subnet_id - if availability_zone is not None: - data['--availability_zone'] = availability_zone - cmd = '' - for key, value in data.items(): - cmd += "{k}={v} ".format(**dict(k=key, v=value)) - return cmd - - def add_share_network_subnet( - self, - share_network, - neutron_net_id=None, - neutron_subnet_id=None, - availability_zone=None, - microversion=None, - ): - """Create new share network subnet for the given share network.""" - params = self._combine_share_network_subnet_data( - neutron_net_id=neutron_net_id, - neutron_subnet_id=neutron_subnet_id, - availability_zone=availability_zone, - ) - share_network_subnet_raw = self.manila( - f'share-network-subnet-create {share_network} {params}', - microversion=microversion, - ) - share_network_subnet = output_parser.details(share_network_subnet_raw) - return share_network_subnet - - def get_share_network_subnet( - self, share_network, share_network_subnet, microversion=None - ): - """Returns share network subnet by share network ID and subnet ID.""" - - share_network_subnet_raw = self.manila( - f'share-network-subnet-show {share_network} {share_network_subnet}' - ) - share_network_subnet = output_parser.details(share_network_subnet_raw) - return share_network_subnet - - def get_share_network_subnets(self, share_network, microversion=None): - share_network = self.get_share_network( - share_network, microversion=microversion - ) - raw_subnets = share_network.get('share_network_subnets') - subnets = ast.literal_eval(raw_subnets) - # NOTE(lseki): convert literal None to string 'None' - for subnet in subnets: - for k, v in subnet.items(): - subnet[k] = str(v) if v is None else v - return subnets - - @not_found_wrapper - def delete_share_network_subnet( - self, share_network, share_network_subnet, microversion=None - ): - """Delete a share_network.""" - self.manila( - f'share-network-subnet-delete {share_network} ' - f'{share_network_subnet}', - microversion=microversion, - ) - - def is_share_network_subnet_deleted( - self, share_network_subnet, share_network, microversion=None - ): - # NOTE(lseki): the parameter share_network_subnet comes before - # share_network, because the wrapper method wait_for_resource_deletion - # expects the resource id in first place (subnet ID in this case). - """Says whether share network subnet is deleted or not. - - :param share_network_subnet: text -- Name or ID of share network subnet - :param share_network: text -- Name or ID of share network the subnet - belongs to - """ - subnets = self.get_share_network_subnets(share_network) - return not any( - subnet['id'] == share_network_subnet for subnet in subnets - ) - - def wait_for_share_network_subnet_deletion( - self, share_network_subnet, share_network, microversion=None - ): - # NOTE(lseki): the parameter share_network_subnet comes before - # share_network, because the wrapper method wait_for_resource_deletion - # expects the resource id in first place (subnet ID in this case). - """Wait for share network subnet deletion by its Name or ID. - - :param share_network_subnet: text -- Name or ID of share network subnet - :param share_network: text -- Name or ID of share network the subnet - belongs to - """ - args = {'share_network': share_network} - self.wait_for_resource_deletion( - SHARE_NETWORK_SUBNET, - res_id=share_network_subnet, - interval=2, - timeout=6, - microversion=microversion, - **args, - ) - - # Shares - - def create_share( - self, - share_protocol, - size, - share_network=None, - share_type=None, - name=None, - description=None, - public=False, - snapshot=None, - metadata=None, - wait=False, - microversion=None, - ): - """Creates a share. - - :param share_protocol: str -- share protocol of a share. - :param size: int/str -- desired size of a share. - :param share_network: str -- Name or ID of share network to use. - :param share_type: str -- Name or ID of share type to use. - :param name: str -- desired name of new share. - :param description: str -- desired description of new share. - :param public: bool -- should a share be public or not. - Default is False. - :param snapshot: str -- Name or ID of a snapshot to use as source. - :param metadata: dict -- key-value data to provide with share creation. - :param wait: bool - the client must wait for "available" state - :param microversion: str -- API microversion that should be used. - """ - cmd = f'create {share_protocol} {size} ' - if share_network is not None: - cmd += f'--share-network {share_network} ' - if share_type is not None: - cmd += f'--share-type {share_type} ' - if name is None: - name = data_utils.rand_name('autotest_share_name') - cmd += f'--name {name} ' - if description is None: - description = data_utils.rand_name('autotest_share_description') - cmd += f'--description {description} ' - if public: - cmd += '--public ' - if snapshot is not None: - cmd += f'--snapshot {snapshot} ' - if metadata: - metadata_cli = '' - for k, v in metadata.items(): - metadata_cli += f'{k}={v} ' - if metadata_cli: - cmd += f'--metadata {metadata_cli} ' - if wait: - cmd += '--wait ' - share_raw = self.manila(cmd, microversion=microversion) - share = output_parser.details(share_raw) - return share - - @not_found_wrapper - def get_share(self, share, microversion=None): - """Returns a share by its Name or ID.""" - share_raw = self.manila(f'show {share}', microversion=microversion) - share = output_parser.details(share_raw) - return share - - @not_found_wrapper - def update_share( - self, - share, - name=None, - description=None, - is_public=False, - microversion=None, - ): - """Updates a share. - - :param share: str -- name or ID of a share that should be updated. - :param name: str -- desired name of new share. - :param description: str -- desired description of new share. - :param is_public: bool -- should a share be public or not. - Default is False. - """ - cmd = f'update {share} ' - if name: - cmd += f'--name {name} ' - if description: - cmd += f'--description {description} ' - is_public = strutils.bool_from_string(is_public, strict=True) - cmd += f'--is-public {is_public} ' - - return self.manila(cmd, microversion=microversion) - - @not_found_wrapper - @forbidden_wrapper - def delete_share( - self, shares, share_group_id=None, wait=False, microversion=None - ): - """Deletes share[s] by Names or IDs. - - :param shares: either str or list of str that can be either Name - or ID of a share(s) that should be deleted. - :param share_group_id: a common share group ID for the shares being - deleted - :param wait: bool -- whether to wait for the shares to be deleted - """ - if not isinstance(shares, list): - shares = [shares] - cmd = 'delete ' - for share in shares: - cmd += f'{share} ' - if share_group_id: - cmd += f'--share-group-id {share_group_id} ' - if wait: - cmd += '--wait ' - return self.manila(cmd, microversion=microversion) - - @not_found_wrapper - @forbidden_wrapper - def soft_delete_share(self, shares, microversion=None): - """Soft Delete share[s] by Names or IDs. - - :param shares: either str or list of str that can be either Name - or ID of a share(s) that should be soft deleted. - """ - if not isinstance(shares, list): - shares = [shares] - cmd = 'soft-delete ' - for share in shares: - cmd += f'{share} ' - return self.manila(cmd, microversion=microversion) - - @not_found_wrapper - @forbidden_wrapper - def restore_share(self, shares, microversion=None): - """Restore share[s] by Names or IDs. - - :param shares: either str or list of str that can be either Name - or ID of a share(s) that should be soft deleted. - """ - if not isinstance(shares, list): - shares = [shares] - cmd = 'restore ' - for share in shares: - cmd += f'{share} ' - return self.manila(cmd, microversion=microversion) - - def create_share_transfer(self, share_id, name=None, microversion=None): - """Create a share transfer. - - :param share_id: ID of share. - ":param name: name of transfer. - """ - cmd = f'share-transfer-create {share_id} ' - if name: - cmd += f'--name {name}' - transfer_raw = self.manila(cmd, microversion=microversion) - transfer = output_parser.details(transfer_raw) - return transfer - - def delete_share_transfer(self, transfer, microversion=None): - """Delete a share transfer. - - :param transfer: ID or name of share transfer. - """ - cmd = f'share-transfer-delete {transfer} ' - self.manila(cmd, microversion=microversion) - - def get_share_transfer(self, transfer, microversion=None): - """Get a share transfer. - - :param transfer: ID or name of share transfer. - """ - cmd = f'share-transfer-show {transfer} ' - transfer_raw = self.manila(cmd, microversion=microversion) - transfer = output_parser.details(transfer_raw) - return transfer - - def list_share_transfer(self, microversion=None): - """Get a share transfer.""" - - cmd = 'share-transfer-list ' - transfer_raw = self.manila(cmd, microversion=microversion) - transfers = utils.listing(transfer_raw) - return transfers - - def accept_share_transfer(self, transfer, auth_key, microversion=None): - """Accept a share transfer. - - :param transfer: ID or name of share transfer. - """ - cmd = f'share-transfer-accept {transfer} {auth_key}' - self.manila(cmd, microversion=microversion) - - def list_shares( - self, - all_tenants=False, - is_soft_deleted=False, - filters=None, - columns=None, - is_public=False, - microversion=None, - ): - """List shares. - - :param all_tenants: bool -- whether to list shares that belong - only to current project or for all tenants. - :param is_soft_deleted: bool -- whether to list shares that has - been soft deleted to recycle bin. - :param filters: dict -- filters for listing of shares. - Example, input: - {'project_id': 'foo'} - {-'project_id': 'foo'} - {--'project_id': 'foo'} - {'project-id': 'foo'} - will be transformed to filter parameter "--project-id=foo" - :param columns: comma separated string of columns. - Example, "--columns Name,Size" - :param is_public: bool -- should list public shares or not. - Default is False. - :param microversion: str -- the request api version. - """ - cmd = 'list ' - if all_tenants: - cmd += '--all-tenants ' - if is_public: - cmd += '--public ' - if is_soft_deleted: - cmd += '--soft-deleted ' - if filters and isinstance(filters, dict): - for k, v in filters.items(): - cmd += f'{self._stranslate_to_cli_optional_param(k)}={v} ' - if columns is not None: - cmd += '--columns ' + columns - shares_raw = self.manila(cmd, microversion=microversion) - shares = utils.listing(shares_raw) - return shares - - def list_share_instances( - self, share_id=None, filters=None, microversion=None - ): - """List share instances. - - :param share_id: ID of a share to filter by. - :param filters: dict -- filters for listing of shares. - Example, input: - {'project_id': 'foo'} - {-'project_id': 'foo'} - {--'project_id': 'foo'} - {'project-id': 'foo'} - will be transformed to filter parameter "--export-location=foo" - :param microversion: API microversion to be used for request. - """ - cmd = 'share-instance-list ' - if share_id: - cmd += f'--share-id {share_id}' - if filters and isinstance(filters, dict): - for k, v in filters.items(): - cmd += f'{self._stranslate_to_cli_optional_param(k)}={v} ' - share_instances_raw = self.manila(cmd, microversion=microversion) - share_instances = utils.listing(share_instances_raw) - return share_instances - - def is_share_deleted(self, share, microversion=None): - """Says whether share is deleted or not. - - :param share: str -- Name or ID of share - """ - try: - self.get_share(share, microversion=microversion) - return False - except tempest_lib_exc.NotFound: - return True - - def wait_for_share_deletion(self, share, microversion=None): - """Wait for share deletion by its Name or ID. - - :param share: str -- Name or ID of share - """ - self.wait_for_resource_deletion( - SHARE, - res_id=share, - interval=5, - timeout=300, - microversion=microversion, - ) - - def is_share_transfer_deleted(self, transfer, microversion=None): - """Says whether transfer is deleted or not. - - :param transfer: str -- Name or ID of transfer - """ - try: - self.get_transfer(transfer, microversion=microversion) - return False - except tempest_lib_exc.NotFound: - return True - - def wait_for_transfer_deletion(self, transfer, microversion=None): - """Wait for transfer deletion by its Name or ID. - - :param transfer: str -- Name or ID of transfer. - """ - self.wait_for_resource_deletion( - SHARE, - res_id=transfer, - interval=5, - timeout=300, - microversion=microversion, - ) - - def wait_for_share_soft_deletion(self, share_id, microversion=None): - body = self.get_share(share_id, microversion=microversion) - is_soft_deleted = body['is_soft_deleted'] - start = int(time.time()) - - while is_soft_deleted == "False": - time.sleep(self.build_interval) - body = self.get_share(share_id, microversion=microversion) - is_soft_deleted = body['is_soft_deleted'] - - if is_soft_deleted == "True": - return - - if int(time.time()) - start >= self.build_timeout: - message = ( - f"Share {share_id} failed to be soft deleted " - f"within the required time {self.build_timeout}." - ) - raise tempest_lib_exc.TimeoutException(message) - - def wait_for_share_restore(self, share_id, microversion=None): - body = self.get_share(share_id, microversion=microversion) - is_soft_deleted = body['is_soft_deleted'] - start = int(time.time()) - - while is_soft_deleted == "True": - time.sleep(self.build_interval) - body = self.get_share(share_id, microversion=microversion) - is_soft_deleted = body['is_soft_deleted'] - - if is_soft_deleted == "False": - return - - if int(time.time()) - start >= self.build_timeout: - message = ( - f"Share {share_id} failed to be restored " - f"within the required time {self.build_timeout}." - ) - raise tempest_lib_exc.TimeoutException(message) - - def wait_for_resource_status( - self, resource_id, status, microversion=None, resource_type="share" - ): - """Waits for a share to reach a given status.""" - get_func = getattr(self, 'get_' + resource_type) - body = get_func(resource_id, microversion=microversion) - share_status = body['status'] - start = int(time.time()) - - while share_status != status: - time.sleep(self.build_interval) - body = get_func(resource_id, microversion=microversion) - share_status = body['status'] - - if share_status == status: - return - elif 'error' in share_status.lower(): - raise exceptions.ShareBuildErrorException(share=resource_id) - - if int(time.time()) - start >= self.build_timeout: - message = ( - f"Resource {resource_id} failed to reach " - f"{status} status within the required time " - f"({self.build_timeout})." - ) - raise tempest_lib_exc.TimeoutException(message) - - def wait_for_migration_task_state( - self, share_id, dest_host, task_state_to_wait, microversion=None - ): - """Waits for a share to migrate to a certain host.""" - statuses = ( - (task_state_to_wait,) - if not isinstance(task_state_to_wait, (tuple, list, set)) - else task_state_to_wait - ) - share = self.get_share(share_id, microversion=microversion) - start = int(time.time()) - while share['task_state'] not in statuses: - time.sleep(self.build_interval) - share = self.get_share(share_id, microversion=microversion) - if share['task_state'] in statuses: - break - elif share['task_state'] == constants.TASK_STATE_MIGRATION_ERROR: - raise exceptions.ShareMigrationException( - share_id=share['id'], src=share['host'], dest=dest_host - ) - elif int(time.time()) - start >= self.build_timeout: - message = ( - 'Share {share_id} failed to reach a status in' - '{status} when migrating from host {src} to ' - 'host {dest} within the required time ' - '{timeout}.'.format( - src=share['host'], - dest=dest_host, - share_id=share['id'], - timeout=self.build_timeout, - status=str(statuses), - ) - ) - raise tempest_lib_exc.TimeoutException(message) - return share - - @not_found_wrapper - def _set_share_metadata( - self, share, data, update_all=False, microversion=None - ): - """Sets a share metadata. - - :param share: str -- Name or ID of a share. - :param data: dict -- key-value pairs to set as metadata. - :param update_all: bool -- if set True then all keys except provided - will be deleted. - """ - if not (isinstance(data, dict) and data): - msg = ( - f'Provided invalid data for setting of share metadata - {data}' - ) - raise exceptions.InvalidData(message=msg) - if update_all: - cmd = f'metadata-update-all {share} ' - else: - cmd = f'metadata {share} set ' - for k, v in data.items(): - cmd += f'{k}={v} ' - return self.manila(cmd, microversion=microversion) - - def update_all_share_metadata(self, share, data, microversion=None): - metadata_raw = self._set_share_metadata( - share, data, True, microversion=microversion - ) - metadata = output_parser.details(metadata_raw) - return metadata - - def set_share_metadata(self, share, data, microversion=None): - return self._set_share_metadata( - share, data, False, microversion=microversion - ) - - @not_found_wrapper - def unset_share_metadata(self, share, keys, microversion=None): - """Unsets some share metadata by keys. - - :param share: str -- Name or ID of a share - :param keys: str/list -- key or list of keys to unset. - """ - if not (isinstance(keys, list) and keys): - msg = ( - 'Provided invalid data for unsetting of share metadata - ' - f'{keys}' - ) - raise exceptions.InvalidData(message=msg) - cmd = f'metadata {share} unset ' - for key in keys: - cmd += f'{key} ' - return self.manila(cmd, microversion=microversion) - - @not_found_wrapper - def get_share_metadata(self, share, microversion=None): - """Returns list of all share metadata. - - :param share: str -- Name or ID of a share. - """ - metadata_raw = self.manila( - f'metadata-show {share}', microversion=microversion - ) - metadata = output_parser.details(metadata_raw) - return metadata - - def create_snapshot( - self, - share, - name=None, - description=None, - force=False, - microversion=None, - ): - """Creates a snapshot.""" - cmd = f'snapshot-create {share} ' - if name is None: - name = data_utils.rand_name('autotest_snapshot_name') - cmd += f'--name {name} ' - if description is None: - description = data_utils.rand_name('autotest_snapshot_description') - cmd += f'--description {description} ' - if force: - cmd += f'--force {force}' - snapshot_raw = self.manila(cmd, microversion=microversion) - snapshot = output_parser.details(snapshot_raw) - return snapshot - - @not_found_wrapper - def get_snapshot(self, snapshot, microversion=None): - """Retrieves a snapshot by its Name or ID.""" - snapshot_raw = self.manila( - f'snapshot-show {snapshot}', microversion=microversion - ) - snapshot = output_parser.details(snapshot_raw) - return snapshot - - @not_found_wrapper - def list_snapshot_export_locations( - self, snapshot, columns=None, microversion=None - ): - """List snapshot export locations. - - :param snapshot: str -- Name or ID of a snapshot. - :param columns: str -- comma separated string of columns. - Example, "--columns uuid,path". - :param microversion: API microversion to be used for request. - """ - cmd = f"snapshot-export-location-list {snapshot}" - if columns is not None: - cmd += " --columns " + columns - export_locations_raw = self.manila(cmd, microversion=microversion) - export_locations = utils.listing(export_locations_raw) - return export_locations - - @not_found_wrapper - @forbidden_wrapper - def list_snapshot_instance_export_locations( - self, snapshot_instance, columns=None, microversion=None - ): - """List snapshot instance export locations. - - :param snapshot_instance: str -- Name or ID of a snapshot instance. - :param columns: str -- comma separated string of columns. - Example, "--columns uuid,path". - :param microversion: API microversion to be used for request. - """ - cmd = f"snapshot-instance-export-location-list {snapshot_instance}" - if columns is not None: - cmd += " --columns " + columns - export_locations_raw = self.manila(cmd, microversion=microversion) - export_locations = utils.listing(export_locations_raw) - return export_locations - - @not_found_wrapper - @forbidden_wrapper - def delete_snapshot(self, snapshot, microversion=None): - """Deletes snapshot by Names or IDs.""" - return self.manila( - f"snapshot-delete {snapshot}", microversion=microversion - ) - - def list_snapshot_instances( - self, snapshot_id=None, columns=None, detailed=None, microversion=None - ): - """List snapshot instances.""" - cmd = 'snapshot-instance-list ' - if snapshot_id: - cmd += f'--snapshot {snapshot_id}' - if columns is not None: - cmd += ' --columns ' + columns - if detailed: - cmd += ' --detailed True ' - snapshot_instances_raw = self.manila(cmd, microversion=microversion) - snapshot_instances = utils.listing(snapshot_instances_raw) - return snapshot_instances - - def get_snapshot_instance(self, id=None, microversion=None): - """Get snapshot instance.""" - cmd = f'snapshot-instance-show {id} ' - snapshot_instance_raw = self.manila(cmd, microversion=microversion) - snapshot_instance = output_parser.details(snapshot_instance_raw) - return snapshot_instance - - def reset_snapshot_instance(self, id=None, state=None, microversion=None): - """Reset snapshot instance status.""" - cmd = f'snapshot-instance-reset-state {id} ' - if state: - cmd += f'--state {state}' - snapshot_instance_raw = self.manila(cmd, microversion=microversion) - snapshot_instance = utils.listing(snapshot_instance_raw) - return snapshot_instance - - def is_snapshot_deleted(self, snapshot, microversion=None): - """Indicates whether snapshot is deleted or not. - - :param snapshot: str -- Name or ID of snapshot - """ - try: - self.get_snapshot(snapshot, microversion=microversion) - return False - except tempest_lib_exc.NotFound: - return True - - def wait_for_snapshot_deletion(self, snapshot, microversion=None): - """Wait for snapshot deletion by its Name or ID. - - :param snapshot: str -- Name or ID of snapshot - """ - self.wait_for_resource_deletion( - SNAPSHOT, - res_id=snapshot, - interval=5, - timeout=300, - microversion=microversion, - ) - - def wait_for_snapshot_status(self, snapshot, status, microversion=None): - """Waits for a snapshot to reach a given status.""" - body = self.get_snapshot(snapshot, microversion=microversion) - snapshot_name = body['name'] - snapshot_status = body['status'] - start = int(time.time()) - - while snapshot_status != status: - time.sleep(self.build_interval) - body = self.get_snapshot(snapshot, microversion=microversion) - snapshot_status = body['status'] - - if snapshot_status == status: - return - elif 'error' in snapshot_status.lower(): - raise exceptions.SnapshotBuildErrorException(snapshot=snapshot) - - if int(time.time()) - start >= self.build_timeout: - message = ( - f"Snapshot {snapshot_name} failed to reach {status} " - f"status within the required time " - f"({self.build_timeout} s)." - ) - raise tempest_lib_exc.TimeoutException(message) - - @not_found_wrapper - def list_access( - self, - entity_id, - columns=None, - microversion=None, - is_snapshot=False, - metadata=None, - ): - """Returns list of access rules for a share. - - :param entity_id: str -- Name or ID of a share or snapshot. - :param columns: comma separated string of columns. - Example, "--columns access_type,access_to" - :param is_snapshot: Boolean value to determine if should list - access of a share or snapshot. - """ - if is_snapshot: - cmd = f'snapshot-access-list {entity_id} ' - else: - cmd = f'access-list {entity_id} ' - if columns is not None: - cmd += ' --columns ' + columns - if metadata: - metadata_cli = '' - for k, v in metadata.items(): - metadata_cli += f'{k}={v} ' - if metadata_cli: - cmd += f' --metadata {metadata_cli} ' - access_list_raw = self.manila(cmd, microversion=microversion) - return output_parser.listing(access_list_raw) - - @not_found_wrapper - def get_access( - self, share_id, access_id, microversion=None, is_snapshot=False - ): - for access in self.list_access( - share_id, microversion=microversion, is_snapshot=is_snapshot - ): - if access['id'] == access_id: - return access - raise tempest_lib_exc.NotFound() - - @not_found_wrapper - def access_show(self, access_id, microversion=None): - raw_access = self.manila( - f"access-show {access_id}", microversion=microversion - ) - return output_parser.details(raw_access) - - @not_found_wrapper - def access_set_metadata(self, access_id, metadata, microversion=None): - if not (isinstance(metadata, dict) and metadata): - msg = ( - 'Provided invalid metadata for setting of access rule' - f' metadata - {metadata}' - ) - raise exceptions.InvalidData(message=msg) - cmd = f"access-metadata {access_id} set " - for k, v in metadata.items(): - cmd += f'{k}={v} ' - return self.manila(cmd, microversion=microversion) - - @not_found_wrapper - def access_unset_metadata(self, access_id, keys, microversion=None): - if not (isinstance(keys, (list, tuple, set)) and keys): - raise exceptions.InvalidData( - message=f'Provided invalid keys - {keys}' - ) - cmd = f'access-metadata {access_id} unset ' - for key in keys: - cmd += f'{key} ' - return self.manila(cmd, microversion=microversion) - - @not_found_wrapper - def snapshot_access_allow( - self, snapshot_id, access_type, access_to, microversion=None - ): - raw_access = self.manila( - f'snapshot-access-allow {snapshot_id} {access_type} {access_to}', - microversion=microversion, - ) - return output_parser.details(raw_access) - - @not_found_wrapper - def snapshot_access_deny(self, snapshot_id, access_id, microversion=None): - return self.manila( - f'snapshot-access-deny {snapshot_id} {access_id}', - microversion=microversion, - ) - - @not_found_wrapper - def access_allow( - self, - share_id, - access_type, - access_to, - access_level, - metadata=None, - microversion=None, - ): - cmd = ( - f'access-allow --access-level {access_level} {share_id} ' - f'{access_type} {access_to}' - ) - if metadata: - metadata_cli = '' - for k, v in metadata.items(): - metadata_cli += f'{k}={v} ' - if metadata_cli: - cmd += f' --metadata {metadata_cli} ' - raw_access = self.manila(cmd, microversion=microversion) - return output_parser.details(raw_access) - - @not_found_wrapper - def access_deny(self, share_id, access_id, microversion=None): - return self.manila( - f'access-deny {share_id} {access_id}', microversion=microversion - ) - - def wait_for_access_rule_status( - self, - share_id, - access_id, - state='active', - microversion=None, - is_snapshot=False, - ): - access = self.get_access( - share_id, - access_id, - microversion=microversion, - is_snapshot=is_snapshot, - ) - - start = int(time.time()) - while access['state'] != state: - time.sleep(self.build_interval) - access = self.get_access( - share_id, - access_id, - microversion=microversion, - is_snapshot=is_snapshot, - ) - - if access['state'] == state: - return - elif access['state'] == 'error': - raise exceptions.AccessRuleCreateErrorException( - access=access_id - ) - - if int(time.time()) - start >= self.build_timeout: - message = ( - f"Access rule {access_id} failed to reach {state} state " - f"within the required time ({self.build_timeout} s)." - ) - raise tempest_lib_exc.TimeoutException(message) - - def wait_for_access_rule_deletion( - self, share_id, access_id, microversion=None, is_snapshot=False - ): - try: - access = self.get_access( - share_id, - access_id, - microversion=microversion, - is_snapshot=is_snapshot, - ) - except tempest_lib_exc.NotFound: - return - - start = int(time.time()) - while True: - time.sleep(self.build_interval) - try: - access = self.get_access( - share_id, - access_id, - microversion=microversion, - is_snapshot=is_snapshot, - ) - except tempest_lib_exc.NotFound: - return - - if access['state'] == 'error': - raise exceptions.AccessRuleDeleteErrorException( - access=access_id - ) - - if int(time.time()) - start >= self.build_timeout: - message = ( - f"Access rule {access_id} failed to reach deleted state " - f"within the required time ({self.build_timeout} s)." - ) - raise tempest_lib_exc.TimeoutException(message) - - def reset_task_state(self, share_id, state, version=None): - state = f'--task_state {state}' if state else '' - return self.manila( - f'reset-task-state {state} {share_id}', microversion=version - ) - - def migration_start( - self, - share_id, - dest_host, - writable, - nondisruptive, - preserve_metadata, - preserve_snapshots, - force_host_assisted_migration, - new_share_network=None, - new_share_type=None, - ): - cmd = ( - f'migration-start {share_id} {dest_host} ' - f'--writable {writable} --nondisruptive {nondisruptive} ' - f'--preserve-metadata {preserve_metadata} ' - f'--preserve-snapshots {preserve_snapshots}' - ) - if force_host_assisted_migration: - cmd += ( - f' --force-host-assisted-migration ' - f'{force_host_assisted_migration}' - ) - if new_share_network: - cmd += f' --new-share-network {new_share_network}' - if new_share_type: - cmd += f' --new-share-type {new_share_type}' - return self.manila(cmd) - - def migration_complete(self, share_id): - return self.manila(f'migration-complete {share_id}') - - def migration_cancel(self, share_id): - return self.manila(f'migration-cancel {share_id}') - - def migration_get_progress(self, share_id): - result = self.manila(f'migration-get-progress {share_id}') - return output_parser.details(result) - - def pool_list(self, detail=False): - cmd = 'pool-list' - if detail: - cmd += ' --column name,host,backend,pool,capabilities' - response = self.manila(cmd) - return output_parser.listing(response) - - def create_security_service( - self, - type='ldap', - name=None, - description=None, - dns_ip=None, - ou=None, - server=None, - domain=None, - user=None, - password=None, - default_ad_site=None, - microversion=None, - ): - """Creates security service. - - :param type: security service type (ldap, kerberos or active_directory) - :param name: desired name of new security service. - :param description: desired description of new security service. - :param dns_ip: DNS IP address inside tenant's network. - :param ou: security service organizational unit - :param server: security service IP address or hostname. - :param domain: security service domain. - :param user: user of the new security service. - :param password: password used by user. - :param default_ad_site: default AD site - """ - - cmd = f'security-service-create {type} ' - cmd += self._combine_security_service_data( - name=name, - description=description, - dns_ip=dns_ip, - ou=ou, - server=server, - domain=domain, - user=user, - password=password, - default_ad_site=default_ad_site, - ) - - ss_raw = self.manila(cmd, microversion=microversion) - security_service = output_parser.details(ss_raw) - return security_service - - @not_found_wrapper - def update_security_service( - self, - security_service, - name=None, - description=None, - dns_ip=None, - ou=None, - server=None, - domain=None, - user=None, - password=None, - default_ad_site=None, - microversion=None, - ): - cmd = f'security-service-update {security_service} ' - cmd += self._combine_security_service_data( - name=name, - description=description, - dns_ip=dns_ip, - ou=ou, - server=server, - domain=domain, - user=user, - password=password, - default_ad_site=default_ad_site, - ) - return output_parser.details( - self.manila(cmd, microversion=microversion) - ) - - def _combine_security_service_data( - self, - name=None, - description=None, - dns_ip=None, - ou=None, - server=None, - domain=None, - user=None, - password=None, - default_ad_site=None, - ): - data = '' - if name is not None: - data += f'--name {name} ' - if description is not None: - data += f'--description {description} ' - if dns_ip is not None: - data += f'--dns-ip {dns_ip} ' - if ou is not None: - data += f'--ou {ou} ' - if server is not None: - data += f'--server {server} ' - if domain is not None: - data += f'--domain {domain} ' - if user is not None: - data += f'--user {user} ' - if password is not None: - data += f'--password {password} ' - if default_ad_site is not None: - data += f'--default-ad-site {default_ad_site} ' - return data - - @not_found_wrapper - def list_share_export_locations( - self, share, columns=None, microversion=None - ): - """List share export locations. - - :param share: str -- Name or ID of a share. - :param columns: str -- comma separated string of columns. - Example, "--columns uuid,path". - :param microversion: API microversion to be used for request. - """ - cmd = f"share-export-location-list {share}" - if columns is not None: - cmd += " --columns " + columns - export_locations_raw = self.manila(cmd, microversion=microversion) - export_locations = utils.listing(export_locations_raw) - return export_locations - - @not_found_wrapper - def get_snapshot_export_location( - self, snapshot, export_location_uuid, microversion=None - ): - """Returns an export location by snapshot and its UUID. - - :param snapshot: str -- Name or ID of a snapshot. - :param export_location_uuid: str -- UUID of an export location. - :param microversion: API microversion to be used for request. - """ - snapshot_raw = self.manila( - f'snapshot-export-location-show {snapshot} {export_location_uuid}', - microversion=microversion, - ) - snapshot = output_parser.details(snapshot_raw) - return snapshot - - @not_found_wrapper - def get_snapshot_instance_export_location( - self, snapshot, export_location_uuid, microversion=None - ): - """Returns an export location by snapshot instance and its UUID. - - :param snapshot: str -- Name or ID of a snapshot instance. - :param export_location_uuid: str -- UUID of an export location. - :param microversion: API microversion to be used for request. - """ - snapshot_raw = self.manila( - f'snapshot-instance-export-location-show {snapshot} ' - f'{export_location_uuid}', - microversion=microversion, - ) - snapshot = output_parser.details(snapshot_raw) - return snapshot - - @not_found_wrapper - def get_share_export_location( - self, share, export_location_uuid, microversion=None - ): - """Returns an export location by share and its UUID. - - :param share: str -- Name or ID of a share. - :param export_location_uuid: str -- UUID of an export location. - :param microversion: API microversion to be used for request. - """ - share_raw = self.manila( - f'share-export-location-show {share} {export_location_uuid}', - microversion=microversion, - ) - share = output_parser.details(share_raw) - return share - - @not_found_wrapper - @forbidden_wrapper - def list_share_instance_export_locations( - self, share_instance, columns=None, microversion=None - ): - """List share instance export locations. - - :param share_instance: str -- Name or ID of a share instance. - :param columns: str -- comma separated string of columns. - Example, "--columns uuid,path". - :param microversion: API microversion to be used for request. - """ - cmd = f"share-instance-export-location-list {share_instance}" - if columns is not None: - cmd += " --columns " + columns - export_locations_raw = self.manila(cmd, microversion=microversion) - export_locations = utils.listing(export_locations_raw) - return export_locations - - @not_found_wrapper - @forbidden_wrapper - def get_share_instance_export_location( - self, share_instance, export_location_uuid, microversion=None - ): - """Returns an export location by share instance and its UUID. - - :param share_instance: str -- Name or ID of a share instance. - :param export_location_uuid: str -- UUID of an export location. - :param microversion: API microversion to be used for request. - """ - share_raw = self.manila( - 'share-instance-export-location-show ' - f'{share_instance} {export_location_uuid}', - microversion=microversion, - ) - share = output_parser.details(share_raw) - return share - - # Share servers - - @not_found_wrapper - def get_share_server(self, share_server, microversion=None): - """Returns share server by its Name or ID.""" - share_server_raw = self.manila( - f'share-server-show {share_server}', microversion=microversion - ) - share_server = output_parser.details(share_server_raw) - return share_server - - def list_share_servers( - self, filters=None, columns=None, microversion=None - ): - """List share servers. - - :param filters: dict -- filters for listing of share servers. - Example, input: - {'project_id': 'foo'} - {'-project_id': 'foo'} - {'--project_id': 'foo'} - {'project-id': 'foo'} - will be transformed to filter parameter "--project-id=foo" - :param columns: comma separated string of columns. - Example, "--columns id" - """ - cmd = 'share-server-list ' - if columns is not None: - cmd += ' --columns ' + columns - if filters and isinstance(filters, dict): - for k, v in filters.items(): - cmd += f'{self._stranslate_to_cli_optional_param(k)}={v} ' - share_servers_raw = self.manila(cmd, microversion=microversion) - share_servers = utils.listing(share_servers_raw) - return share_servers - - @not_found_wrapper - def delete_share_server(self, share_server, microversion=None): - """Deletes share server by its Name or ID.""" - return self.manila( - f'share-server-delete {share_server}', microversion=microversion - ) - - def is_share_server_deleted(self, share_server_id, microversion=None): - """Says whether share server is deleted or not. - - :param share_server: text -- ID of the share server - """ - servers = self.list_share_servers(microversion=microversion) - for list_element in servers: - if share_server_id == list_element['Id']: - return False - return True - - def wait_for_share_server_deletion(self, share_server, microversion=None): - """Wait for share server deletion by its Name or ID. - - :param share_server: text -- Name or ID of share server - """ - self.wait_for_resource_deletion( - SHARE_SERVER, - res_id=share_server, - interval=3, - timeout=60, - microversion=microversion, - ) - - def unmanage_share(self, server_id): - return self.manila(f'unmanage {server_id} ') - - def unmanage_server(self, share_server_id): - return self.manila(f'share-server-unmanage {share_server_id} ') - - def share_server_manage( - self, host, share_network, identifier, driver_options=None - ): - if driver_options: - command = ( - f'share-server-manage {host} {share_network} {identifier} ' - f'{driver_options}' - ) - else: - command = ( - f'share-server-manage {host} {share_network} {identifier}' - ) - managed_share_server_raw = self.manila(command) - managed_share_server = output_parser.details(managed_share_server_raw) - return managed_share_server['id'] - - def manage_share(self, host, protocol, export_location, share_server): - managed_share_raw = self.manila( - f'manage {host} {protocol} {export_location} ' - f'--share-server-id {share_server}' - ) - managed_share = output_parser.details(managed_share_raw) - return managed_share['id'] - - def share_server_migration_check( - self, - server_id, - dest_host, - writable, - nondisruptive, - preserve_snapshots, - new_share_network=None, - ): - cmd = ( - f'share-server-migration-check {server_id} {dest_host} ' - f'--writable {writable} --nondisruptive {nondisruptive} ' - f'--preserve-snapshots {preserve_snapshots}' - ) - if new_share_network: - cmd += f' --new-share-network {new_share_network}' - result = self.manila(cmd) - return output_parser.details(result) - - def share_server_migration_start( - self, - server_id, - dest_host, - writable=False, - nondisruptive=False, - preserve_snapshots=False, - new_share_network=None, - ): - cmd = ( - f'share-server-migration-start {server_id} {dest_host} ' - f'--writable {writable} --nondisruptive {nondisruptive} ' - f'--preserve-snapshots {preserve_snapshots}' - ) - if new_share_network: - cmd += f' --new-share-network {new_share_network}' - return self.manila(cmd) - - def share_server_migration_complete(self, server_id): - return self.manila(f'share-server-migration-complete {server_id}') - - def share_server_migration_cancel(self, server_id): - return self.manila(f'share-server-migration-cancel {server_id}') - - def share_server_migration_get_progress(self, server_id): - result = self.manila( - f'share-server-migration-get-progress {server_id}' - ) - return output_parser.details(result) - - def wait_for_server_migration_task_state( - self, share_server_id, dest_host, task_state_to_wait, microversion=None - ): - """Waits for a certain server task state.""" - statuses = ( - (task_state_to_wait,) - if not isinstance(task_state_to_wait, (tuple, list, set)) - else task_state_to_wait - ) - server = self.get_share_server( - share_server=share_server_id, microversion=microversion - ) - start = int(time.time()) - while server['task_state'] not in statuses: - time.sleep(self.build_interval) - server = self.get_share_server( - share_server=share_server_id, microversion=microversion - ) - if server['task_state'] in statuses: - return server - elif server['task_state'] == constants.TASK_STATE_MIGRATION_ERROR: - raise exceptions.ShareServerMigrationException( - server_id=server['id'] - ) - elif int(time.time()) - start >= self.build_timeout: - message = ( - 'Server {share_server_id} failed to reach the ' - 'status in {status} while migrating from host ' - '{src} to host {dest} within the required time ' - '{timeout}.'.format( - src=server['host'], - dest=dest_host, - share_server_id=server['id'], - timeout=self.build_timeout, - status=str(statuses), - ) - ) - raise tempest_lib_exc.TimeoutException(message) - - # user messages - - def wait_for_message(self, resource_id): - """Waits until a message for a resource with given id exists""" - start = int(time.time()) - message = None - - while not message: - time.sleep(self.build_interval) - for msg in self.list_messages(): - if msg['Resource ID'] == resource_id: - return msg - - if int(time.time()) - start >= self.build_timeout: - message = ( - f'No message for resource with id {resource_id} was ' - f'created in the required time ({self.build_timeout} s).' - ) - raise tempest_lib_exc.TimeoutException(message) - - def list_messages(self, columns=None, microversion=None): - """List messages. - - :param columns: str -- comma separated string of columns. - Example, "--columns id,resource_id". - :param microversion: API microversion to be used for request. - """ - cmd = "message-list" - if columns is not None: - cmd += " --columns " + columns - messages_raw = self.manila(cmd, microversion=microversion) - messages = utils.listing(messages_raw) - return messages - - @not_found_wrapper - def get_message(self, message, microversion=None): - """Returns share server by its Name or ID.""" - message_raw = self.manila( - f'message-show {message}', microversion=microversion - ) - message = output_parser.details(message_raw) - return message - - @not_found_wrapper - def delete_message(self, message, microversion=None): - """Deletes message by its ID.""" - return self.manila( - f'message-delete {message}', microversion=microversion - ) - - def is_message_deleted(self, message, microversion=None): - """Indicates whether message is deleted or not. - - :param message: str -- ID of message - """ - try: - self.get_message(message, microversion=microversion) - return False - except tempest_lib_exc.NotFound: - return True - - def wait_for_message_deletion(self, message, microversion=None): - """Wait for message deletion by its ID. - - :param message: text -- ID of message - """ - self.wait_for_resource_deletion( - MESSAGE, - res_id=message, - interval=3, - timeout=60, - microversion=microversion, - ) - - # Share replicas - - def create_share_replica( - self, - share, - availability_zone=None, - share_network=None, - microversion=None, - ): - """Create a share replica. - - :param share: str -- Name or ID of a share to create a replica of - """ - cmd = f"share-replica-create {share}" - if availability_zone is not None: - cmd += " --availability_zone " + availability_zone - if share_network is not None: - cmd += " --share_network " + share_network - - replica = self.manila(cmd, microversion=microversion) - return output_parser.details(replica) - - @not_found_wrapper - def get_share_replica(self, replica, microversion=None): - cmd = f"share-replica-show {replica}" - replica = self.manila(cmd, microversion=microversion) - return output_parser.details(replica) - - @not_found_wrapper - @forbidden_wrapper - def delete_share_replica(self, share_replica, microversion=None): - """Deletes share replica by ID.""" - return self.manila( - f"share-replica-delete {share_replica}", microversion=microversion - ) - - def is_share_replica_deleted(self, replica, microversion=None): - """Indicates whether a share replica is deleted or not. - - :param replica: str -- ID of share replica - """ - try: - self.get_share_replica(replica, microversion=microversion) - return False - except tempest_lib_exc.NotFound: - return True - - def wait_for_share_replica_deletion(self, replica, microversion=None): - """Wait for share replica deletion by its ID. - - :param replica: text -- ID of share replica - """ - self.wait_for_resource_deletion( - SHARE_REPLICA, - res_id=replica, - interval=3, - timeout=60, - microversion=microversion, - ) - - def wait_for_share_replica_status( - self, share_replica, status="available", microversion=None - ): - """Waits for a share replica to reach a given status.""" - replica = self.get_share_replica( - share_replica, microversion=microversion - ) - share_replica_status = replica['status'] - start = int(time.time()) - - while share_replica_status != status: - time.sleep(self.build_interval) - replica = self.get_share_replica( - share_replica, microversion=microversion - ) - share_replica_status = replica['status'] - - if share_replica_status == status: - return replica - elif 'error' in share_replica_status.lower(): - raise exceptions.ShareReplicaBuildErrorException( - replica=share_replica - ) - - if int(time.time()) - start >= self.build_timeout: - message = ( - f"Share replica {share_replica} failed to reach {status} " - "status within the required time " - f"({self.build_timeout} s)." - ) - raise tempest_lib_exc.TimeoutException(message) - return replica - - @not_found_wrapper - @forbidden_wrapper - def list_share_replica_export_locations( - self, share_replica, columns=None, microversion=None - ): - """List share replica export locations. - - :param share_replica: str -- ID of share replica. - :param columns: str -- comma separated string of columns. - Example, "--columns id,path". - :param microversion: API microversion to be used for request. - """ - cmd = f"share-replica-export-location-list {share_replica}" - if columns is not None: - cmd += " --columns " + columns - export_locations_raw = self.manila(cmd, microversion=microversion) - export_locations = utils.listing(export_locations_raw) - return export_locations - - @not_found_wrapper - @forbidden_wrapper - def get_share_replica_export_location( - self, share_replica, export_location_uuid, microversion=None - ): - """Returns an export location by share replica and export location ID. - - :param share_replica: str -- ID of share replica. - :param export_location_uuid: str -- UUID of an export location. - :param microversion: API microversion to be used for request. - """ - export_raw = self.manila( - 'share-replica-export-location-show ' - f'{share_replica} {export_location_uuid}', - microversion=microversion, - ) - export = output_parser.details(export_raw) - return export diff --git a/manilaclient/tests/functional/test_availability_zones.py b/manilaclient/tests/functional/test_availability_zones.py deleted file mode 100644 index 425c0afcf..000000000 --- a/manilaclient/tests/functional/test_availability_zones.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2016 Mirantis Inc. -# 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. - -import ddt -from oslo_utils import uuidutils - -from manilaclient.tests.functional import base - - -@ddt.ddt -class ManilaClientTestAvailabilityZonesReadOnly(base.BaseTestCase): - @ddt.data("2.6", "2.7", "2.22") - def test_availability_zone_list(self, microversion): - self.skip_if_microversion_not_supported(microversion) - - azs = self.user_client.list_availability_zones( - microversion=microversion - ) - - for az in azs: - self.assertEqual(4, len(az)) - for key in ('Id', 'Name', 'Created_At', 'Updated_At'): - self.assertIn(key, az) - self.assertTrue(uuidutils.is_uuid_like(az['Id'])) - self.assertIsNotNone(az['Name']) - self.assertIsNotNone(az['Created_At']) - - @ddt.data( - ('name', ['Name']), - ('name,id', ['Name', 'Id']), - ('name,created_at', ['Name', 'Created_At']), - ('name,id,created_at', ['Name', 'Id', 'Created_At']), - ) - @ddt.unpack - def test_availability_zone_list_with_columns(self, columns_arg, expected): - azs = self.user_client.list_availability_zones(columns=columns_arg) - - for az in azs: - self.assertEqual(len(expected), len(az)) - for key in expected: - self.assertIn(key, az) - if 'Id' in expected: - self.assertTrue(uuidutils.is_uuid_like(az['Id'])) - if 'Name' in expected: - self.assertIsNotNone(az['Name']) - if 'Created_At' in expected: - self.assertIsNotNone(az['Created_At']) diff --git a/manilaclient/tests/functional/test_common.py b/manilaclient/tests/functional/test_common.py deleted file mode 100644 index ac2745142..000000000 --- a/manilaclient/tests/functional/test_common.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright 2015 Mirantis Inc. -# 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. - -import re - -import ddt - -from manilaclient.tests.functional import base - - -@ddt.ddt -class ManilaClientTestCommonReadOnly(base.BaseTestCase): - @ddt.data('admin', 'user') - def test_manila_version(self, role): - self.clients[role].manila('', flags='--version') - - @ddt.data('admin', 'user') - def test_help(self, role): - help_text = self.clients[role].manila('help') - lines = help_text.split('\n') - self.assertFirstLineStartsWith(lines, 'usage: manila') - - commands = [] - cmds_start = lines.index('Positional arguments:') - cmds_end = lines.index('Options:') - command_pattern = re.compile(r'^ {4}([a-z0-9\-\_]+)') - for line in lines[cmds_start:cmds_end]: - match = command_pattern.match(line) - if match: - commands.append(match.group(1)) - commands = set(commands) - wanted_commands = set( - ( - 'absolute-limits', - 'list', - 'help', - 'quota-show', - 'access-list', - 'snapshot-list', - 'access-allow', - 'access-deny', - 'share-network-list', - 'security-service-list', - ) - ) - self.assertFalse(wanted_commands - commands) - - @ddt.data('admin', 'user') - def test_credentials(self, role): - self.clients[role].manila('credentials') - - @ddt.data('admin', 'user') - def test_list_extensions(self, role): - roles = self.parser.listing( - self.clients[role].manila('list-extensions') - ) - self.assertTableStruct(roles, ['Name', 'Summary', 'Alias', 'Updated']) - - @ddt.data('admin', 'user') - def test_endpoints(self, role): - self.clients[role].manila('endpoints') diff --git a/manilaclient/tests/functional/test_export_locations.py b/manilaclient/tests/functional/test_export_locations.py deleted file mode 100644 index 0bf6e0aeb..000000000 --- a/manilaclient/tests/functional/test_export_locations.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright 2015 Mirantis Inc. -# 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. - -import ddt -from oslo_utils import uuidutils - -from manilaclient.tests.functional import base - - -@ddt.ddt -class ExportLocationReadWriteTest(base.BaseTestCase): - def setUp(self): - super().setUp() - self.share = self.create_share(client=self.get_user_client()) - - @ddt.data('admin', 'user') - def test_list_share_export_locations(self, role): - self.skip_if_microversion_not_supported('2.14') - - client = self.admin_client if role == 'admin' else self.user_client - export_locations = client.list_share_export_locations(self.share['id']) - - self.assertGreater(len(export_locations), 0) - expected_keys = ('ID', 'Path', 'Preferred') - for el in export_locations: - for key in expected_keys: - self.assertIn(key, el) - self.assertTrue(uuidutils.is_uuid_like(el['ID'])) - self.assertIn(el['Preferred'], ('True', 'False')) - - @ddt.data('admin', 'user') - def test_list_share_export_locations_with_columns(self, role): - self.skip_if_microversion_not_supported('2.9') - - client = self.admin_client if role == 'admin' else self.user_client - export_locations = client.list_share_export_locations( - self.share['id'], columns='id,path' - ) - - self.assertGreater(len(export_locations), 0) - expected_keys = ('Id', 'Path') - unexpected_keys = ('Updated At', 'Created At') - for el in export_locations: - for key in expected_keys: - self.assertIn(key, el) - for key in unexpected_keys: - self.assertNotIn(key, el) - self.assertTrue(uuidutils.is_uuid_like(el['Id'])) - - @ddt.data('admin', 'user') - def test_get_share_export_location(self, role): - self.skip_if_microversion_not_supported('2.14') - - client = self.admin_client if role == 'admin' else self.user_client - export_locations = client.list_share_export_locations(self.share['id']) - - el = client.get_share_export_location( - self.share['id'], export_locations[0]['ID'] - ) - - expected_keys = ['path', 'updated_at', 'created_at', 'id', 'preferred'] - if role == 'admin': - expected_keys.extend(['is_admin_only', 'share_instance_id']) - for key in expected_keys: - self.assertIn(key, el) - if role == 'admin': - self.assertTrue(uuidutils.is_uuid_like(el['share_instance_id'])) - self.assertIn(el['is_admin_only'], ('True', 'False')) - self.assertTrue(uuidutils.is_uuid_like(el['id'])) - self.assertIn(el['preferred'], ('True', 'False')) - for list_k, get_k in ( - ('ID', 'id'), - ('Path', 'path'), - ('Preferred', 'preferred'), - ): - self.assertEqual(export_locations[0][list_k], el[get_k]) - - def test_list_share_instance_export_locations(self): - self.skip_if_microversion_not_supported('2.14') - - client = self.admin_client - share_instances = client.list_share_instances(self.share['id']) - self.assertGreater(len(share_instances), 0) - self.assertIn('ID', share_instances[0]) - self.assertTrue(uuidutils.is_uuid_like(share_instances[0]['ID'])) - share_instance_id = share_instances[0]['ID'] - - export_locations = client.list_share_instance_export_locations( - share_instance_id - ) - - self.assertGreater(len(export_locations), 0) - expected_keys = ('ID', 'Path', 'Is Admin only', 'Preferred') - for el in export_locations: - for key in expected_keys: - self.assertIn(key, el) - self.assertTrue(uuidutils.is_uuid_like(el['ID'])) - - def test_list_share_instance_export_locations_with_columns(self): - self.skip_if_microversion_not_supported('2.9') - - client = self.admin_client - share_instances = client.list_share_instances(self.share['id']) - self.assertGreater(len(share_instances), 0) - self.assertIn('ID', share_instances[0]) - self.assertTrue(uuidutils.is_uuid_like(share_instances[0]['ID'])) - share_instance_id = share_instances[0]['ID'] - - export_locations = client.list_share_instance_export_locations( - share_instance_id, columns='id,path' - ) - - self.assertGreater(len(export_locations), 0) - expected_keys = ('Id', 'Path') - unexpected_keys = ('Updated At', 'Created At', 'Is Admin only') - for el in export_locations: - for key in expected_keys: - self.assertIn(key, el) - for key in unexpected_keys: - self.assertNotIn(key, el) - self.assertTrue(uuidutils.is_uuid_like(el['Id'])) - - def test_get_share_instance_export_location(self): - self.skip_if_microversion_not_supported('2.14') - - client = self.admin_client - share_instances = client.list_share_instances(self.share['id']) - self.assertGreater(len(share_instances), 0) - self.assertIn('ID', share_instances[0]) - self.assertTrue(uuidutils.is_uuid_like(share_instances[0]['ID'])) - share_instance_id = share_instances[0]['ID'] - - export_locations = client.list_share_instance_export_locations( - share_instance_id - ) - - el = client.get_share_instance_export_location( - share_instance_id, export_locations[0]['ID'] - ) - - expected_keys = ( - 'path', - 'updated_at', - 'created_at', - 'id', - 'preferred', - 'is_admin_only', - 'share_instance_id', - ) - for key in expected_keys: - self.assertIn(key, el) - self.assertIn(el['is_admin_only'], ('True', 'False')) - self.assertIn(el['preferred'], ('True', 'False')) - self.assertTrue(uuidutils.is_uuid_like(el['id'])) - for list_k, get_k in ( - ('ID', 'id'), - ('Path', 'path'), - ('Preferred', 'preferred'), - ('Is Admin only', 'is_admin_only'), - ): - self.assertEqual(export_locations[0][list_k], el[get_k]) diff --git a/manilaclient/tests/functional/test_limits.py b/manilaclient/tests/functional/test_limits.py deleted file mode 100644 index 8d79cb6b0..000000000 --- a/manilaclient/tests/functional/test_limits.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2015 Mirantis Inc. -# 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. - -import ddt - -from manilaclient.tests.functional import base - - -@ddt.ddt -class ManilaClientTestLimitsReadOnly(base.BaseTestCase): - @ddt.data('admin', 'user') - def test_rate_limits(self, role): - self.clients[role].manila('rate-limits') - - @ddt.data('admin', 'user') - def test_absolute_limits(self, role): - self.clients[role].manila('absolute-limits') diff --git a/manilaclient/tests/functional/test_messages.py b/manilaclient/tests/functional/test_messages.py deleted file mode 100644 index dd06c524b..000000000 --- a/manilaclient/tests/functional/test_messages.py +++ /dev/null @@ -1,80 +0,0 @@ -# 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 ddt - -from manilaclient.tests.functional import base - - -@ddt.ddt -class MessagesReadOnlyTest(base.BaseTestCase): - @ddt.data( - ("admin", "2.37"), - ("user", "2.37"), - ) - @ddt.unpack - def test_message_list(self, role, microversion): - self.skip_if_microversion_not_supported(microversion) - self.clients[role].manila("message-list", microversion=microversion) - - -@ddt.ddt -class MessagesReadWriteTest(base.BaseTestCase): - def setUp(self): - super().setUp() - self.message = self.create_message() - - def test_list_messages(self): - self.skip_if_microversion_not_supported('2.37') - messages = self.admin_client.list_messages() - self.assertTrue(any(m['ID'] is not None for m in messages)) - self.assertTrue(any(m['User Message'] is not None for m in messages)) - self.assertTrue(any(m['Resource ID'] is not None for m in messages)) - self.assertTrue(any(m['Action ID'] is not None for m in messages)) - self.assertTrue(any(m['Detail ID'] is not None for m in messages)) - self.assertTrue(any(m['Resource Type'] is not None for m in messages)) - - @ddt.data( - 'id', - 'action_id', - 'resource_id', - 'action_id', - 'detail_id', - 'resource_type', - 'created_at', - 'action_id,detail_id,resource_id', - ) - def test_list_share_type_select_column(self, columns): - self.skip_if_microversion_not_supported('2.37') - self.admin_client.list_messages(columns=columns) - - def test_get_message(self): - self.skip_if_microversion_not_supported('2.37') - message = self.admin_client.get_message(self.message['ID']) - expected_keys = ( - 'id', - 'action_id', - 'resource_id', - 'action_id', - 'detail_id', - 'resource_type', - 'created_at', - 'created_at', - ) - for key in expected_keys: - self.assertIn(key, message) - - def test_delete_message(self): - self.skip_if_microversion_not_supported('2.37') - message = self.create_message(cleanup_in_class=False) - self.admin_client.delete_message(message['ID']) - self.admin_client.wait_for_message_deletion(message['ID']) diff --git a/manilaclient/tests/functional/test_quotas.py b/manilaclient/tests/functional/test_quotas.py deleted file mode 100644 index ed5663c47..000000000 --- a/manilaclient/tests/functional/test_quotas.py +++ /dev/null @@ -1,395 +0,0 @@ -# Copyright 2015 Mirantis Inc. -# 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 random import randint - -import ddt -from tempest.lib.cli import output_parser -from tempest.lib.common.utils import data_utils -from tempest.lib import exceptions - -from manilaclient import api_versions -from manilaclient.tests.functional import base -from manilaclient.tests.functional import utils - -REPLICA_QUOTAS_MICROVERSION = '2.53' - - -def _get_share_type_quota_values(project_quota_value): - project_quota_value = int(project_quota_value) - if project_quota_value == -1: - return randint(1, 999) - elif project_quota_value == 0: - return 0 - else: - return project_quota_value - 1 - - -@ddt.ddt -@utils.skip_if_microversion_not_supported("2.39") -class QuotasReadWriteTest(base.BaseTestCase): - def setUp(self): - super(self.__class__, self).setUp() - self.microversion = "2.39" - self.project_id = self.admin_client.get_project_id( - self.admin_client.tenant_name - ) - - # Create share type - self.share_type = self.create_share_type( - name=data_utils.rand_name("manilaclient_functional_test"), - driver_handles_share_servers=False, - is_public=True, - microversion=self.microversion, - ) - self.st_id = self.share_type["ID"] - - def _verify_current_st_quotas_equal_to(self, quotas, microversion): - # Read share type quotas - cmd = ( - f'quota-show --tenant-id {self.project_id} ' - f'--share-type {self.st_id}' - ) - st_quotas_raw = self.admin_client.manila( - cmd, microversion=microversion - ) - st_quotas = output_parser.details(st_quotas_raw) - - # Verify that quotas - self.assertGreater(len(st_quotas), 3) - for key, value in st_quotas.items(): - if key not in ( - 'shares', - 'gigabytes', - 'snapshots', - 'snapshot_gigabytes', - ): - continue - self.assertIn(key, quotas) - self.assertEqual(int(quotas[key]), int(value)) - - def _verify_current_quotas_equal_to(self, quotas, microversion): - # Read quotas - cmd = f'quota-show --tenant-id {self.project_id}' - quotas_raw = self.admin_client.manila(cmd, microversion=microversion) - quotas = output_parser.details(quotas_raw) - - # Verify that quotas - self.assertGreater(len(quotas), 3) - for key, value in quotas.items(): - if key not in ( - 'shares', - 'gigabytes', - 'snapshots', - 'snapshot_gigabytes', - 'share_groups', - 'share_group_snapshots', - ): - continue - self.assertIn(key, quotas) - self.assertEqual(int(quotas[key]), int(value)) - - @ddt.data( - *set( - [ - "2.40", - api_versions.MAX_VERSION, - ] - ) - ) - def test_update_quotas_for_share_groups(self, microversion): - if not utils.is_microversion_supported(microversion): - msg = f"Microversion '{microversion}' not supported." - raise self.skipException(msg) - - # Get default quotas - cmd = 'quota-defaults' - quotas_raw = self.admin_client.manila(cmd, microversion=microversion) - default_quotas = output_parser.details(quotas_raw) - - # Get project quotas - cmd = f'quota-show --tenant-id {self.project_id} ' - quotas_raw = self.admin_client.manila(cmd, microversion=microversion) - p_quotas = output_parser.details(quotas_raw) - - # Define custom share group quotas for project - p_custom_quotas = { - 'share_groups': -1 if int(p_quotas['share_groups']) != -1 else 999, - 'share_group_snapshots': -1 - if int(p_quotas['share_group_snapshots']) != -1 - else 999, - } - - # Update share group quotas for project - cmd = ( - 'quota-update {} --share-groups {} --share-group-snapshots {}' - ).format( - self.project_id, - p_custom_quotas['share_groups'], - p_custom_quotas['share_group_snapshots'], - ) - self.admin_client.manila(cmd, microversion=microversion) - - # Verify quotas - self._verify_current_quotas_equal_to(p_custom_quotas, microversion) - - # Reset quotas - cmd = ( - f'quota-delete --tenant-id {self.project_id} ' - f'--share-type {self.st_id}' - ) - self.admin_client.manila(cmd, microversion=microversion) - - # Verify quotas after reset - self._verify_current_quotas_equal_to(default_quotas, microversion) - - # Return project quotas back - cmd = ( - 'quota-update {} --share-groups {} --share-group-snapshots {}' - ).format( - self.project_id, - p_quotas['share_groups'], - p_quotas['share_group_snapshots'], - ) - self.admin_client.manila(cmd, microversion=microversion) - - # Verify quotas after reset - self._verify_current_quotas_equal_to(p_quotas, microversion) - - @ddt.data('--share-groups', '--share-group-snapshots') - @utils.skip_if_microversion_not_supported("2.39") - def test_update_quotas_for_share_groups_using_too_old_microversion( - self, arg - ): - cmd = f'quota-update {self.project_id} {arg} 13' - self.assertRaises( - exceptions.CommandFailed, - self.admin_client.manila, - cmd, - microversion='2.39', - ) - - @ddt.data('--share-replicas', '--replica-gigabytes') - @utils.skip_if_microversion_not_supported("2.52") - def test_update_quotas_for_share_replicas_using_too_old_microversion( - self, arg - ): - cmd = f'quota-update {self.project_id} {arg} 10' - self.assertRaises( - exceptions.CommandFailed, - self.admin_client.manila, - cmd, - microversion='2.52', - ) - - @ddt.data('--share-groups', '--share-group-snapshots') - @utils.skip_if_microversion_not_supported("2.40") - def test_update_share_type_quotas_for_share_groups(self, arg): - cmd = ( - f'quota-update {self.project_id} --share-type {self.st_id} ' - f'{arg} 13' - ) - self.assertRaises( - exceptions.CommandFailed, - self.admin_client.manila, - cmd, - microversion='2.40', - ) - - @ddt.data( - *set( - [ - "2.39", - "2.40", - REPLICA_QUOTAS_MICROVERSION, - api_versions.MAX_VERSION, - ] - ) - ) - def test_update_share_type_quotas_positive(self, microversion): - if not utils.is_microversion_supported(microversion): - msg = f"Microversion '{microversion}' not supported." - raise self.skipException(msg) - - # Get project quotas - cmd = f'quota-show --tenant-id {self.project_id} ' - quotas_raw = self.admin_client.manila(cmd, microversion=microversion) - p_quotas = output_parser.details(quotas_raw) - - # Define share type quotas - st_custom_quotas = { - 'shares': _get_share_type_quota_values(p_quotas['shares']), - 'snapshots': _get_share_type_quota_values(p_quotas['snapshots']), - 'gigabytes': _get_share_type_quota_values(p_quotas['gigabytes']), - 'snapshot_gigabytes': _get_share_type_quota_values( - p_quotas['snapshot_gigabytes'] - ), - } - supports_share_replica_quotas = api_versions.APIVersion( - microversion - ) >= api_versions.APIVersion(REPLICA_QUOTAS_MICROVERSION) - - if supports_share_replica_quotas: - st_custom_quotas['share_replicas'] = _get_share_type_quota_values( - p_quotas['share_replicas'] - ) - st_custom_quotas['replica_gigabytes'] = ( - _get_share_type_quota_values(p_quotas['replica_gigabytes']) - ) - replica_params = ( - ' --share-replicas {} --replica-gigabytes {}' - ).format( - st_custom_quotas['share_replicas'], - st_custom_quotas['replica_gigabytes'], - ) - - # Update quotas for share type - cmd = ( - 'quota-update {} --share-type {} ' - '--shares {} --gigabytes {} --snapshots {} ' - '--snapshot-gigabytes {}' - ).format( - self.project_id, - self.st_id, - st_custom_quotas['shares'], - st_custom_quotas['gigabytes'], - st_custom_quotas['snapshots'], - st_custom_quotas['snapshot_gigabytes'], - ) - - if supports_share_replica_quotas: - cmd += replica_params - self.admin_client.manila(cmd, microversion=microversion) - - # Verify share type quotas - self._verify_current_st_quotas_equal_to(st_custom_quotas, microversion) - - # Reset share type quotas - cmd = ( - f'quota-delete --tenant-id {self.project_id} ' - f'--share-type {self.st_id}' - ) - self.admin_client.manila(cmd, microversion=microversion) - - # Verify share type quotas after reset - self._verify_current_st_quotas_equal_to(p_quotas, microversion) - - @utils.skip_if_microversion_not_supported("2.38") - def test_read_share_type_quotas_with_too_old_microversion(self): - cmd = ( - f'quota-show --tenant-id {self.project_id} ' - f'--share-type {self.st_id}' - ) - self.assertRaises( - exceptions.CommandFailed, - self.admin_client.manila, - cmd, - microversion='2.38', - ) - - @utils.skip_if_microversion_not_supported("2.38") - def test_update_share_type_quotas_with_too_old_microversion(self): - cmd = 'quota-update --tenant-id {} --share-type {} --shares {}'.format( - self.project_id, self.st_id, '0' - ) - self.assertRaises( - exceptions.CommandFailed, - self.admin_client.manila, - cmd, - microversion='2.38', - ) - - @utils.skip_if_microversion_not_supported("2.38") - def test_delete_share_type_quotas_with_too_old_microversion(self): - cmd = ( - f'quota-delete --tenant-id {self.project_id} ' - f'--share-type {self.st_id}' - ) - self.assertRaises( - exceptions.CommandFailed, - self.admin_client.manila, - cmd, - microversion='2.38', - ) - - -@ddt.ddt -class ManilaClientTestQuotasReadOnly(base.BaseTestCase): - def test_quota_class_show_by_admin(self): - roles = self.parser.listing( - self.clients['admin'].manila('quota-class-show', params='abc') - ) - self.assertTableStruct(roles, ('Property', 'Value')) - - def test_quota_class_show_by_user(self): - self.assertRaises( - exceptions.CommandFailed, - self.clients['user'].manila, - 'quota-class-show', - params='abc', - ) - - def _get_quotas(self, role, operation, microversion): - roles = self.parser.listing( - self.clients[role].manila( - f'quota-{operation}', microversion=microversion - ) - ) - self.assertTableStruct(roles, ('Property', 'Value')) - - @ddt.data('admin', 'user') - @utils.skip_if_microversion_not_supported("1.0") - def test_quota_defaults_api_1_0(self, role): - self._get_quotas(role, "defaults", "1.0") - - @ddt.data('admin', 'user') - @utils.skip_if_microversion_not_supported("2.0") - def test_quota_defaults_api_2_0(self, role): - self._get_quotas(role, "defaults", "2.0") - - @ddt.data('admin', 'user') - @utils.skip_if_microversion_not_supported("2.6") - def test_quota_defaults_api_2_6(self, role): - self._get_quotas(role, "defaults", "2.6") - - @ddt.data('admin', 'user') - @utils.skip_if_microversion_not_supported("2.7") - def test_quota_defaults_api_2_7(self, role): - self._get_quotas(role, "defaults", "2.7") - - @ddt.data('admin', 'user') - @utils.skip_if_microversion_not_supported("1.0") - def test_quota_show_api_1_0(self, role): - self._get_quotas(role, "show", "1.0") - - @ddt.data('admin', 'user') - @utils.skip_if_microversion_not_supported("2.0") - def test_quota_show_api_2_0(self, role): - self._get_quotas(role, "show", "2.0") - - @ddt.data('admin', 'user') - @utils.skip_if_microversion_not_supported("2.6") - def test_quota_show_api_2_6(self, role): - self._get_quotas(role, "show", "2.6") - - @ddt.data('admin', 'user') - @utils.skip_if_microversion_not_supported("2.7") - def test_quota_show_api_2_7(self, role): - self._get_quotas(role, "show", "2.7") - - @ddt.data('admin', 'user') - @utils.skip_if_microversion_not_supported("2.25") - def test_quota_show_api_2_25(self, role): - self._get_quotas(role, "show --detail", "2.25") diff --git a/manilaclient/tests/functional/test_scheduler_stats.py b/manilaclient/tests/functional/test_scheduler_stats.py deleted file mode 100644 index 117795db9..000000000 --- a/manilaclient/tests/functional/test_scheduler_stats.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2015 Clinton Knight. 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 tempest.lib.common.utils import data_utils -from tempest.lib import exceptions - -from manilaclient.tests.functional import base - - -class ManilaClientTestSchedulerStatsReadOnly(base.BaseTestCase): - def test_pools_list(self): - self.clients['admin'].manila('pool-list') - - def test_pools_list_with_debug_flag(self): - self.clients['admin'].manila('pool-list', flags='--debug') - - def test_pools_list_with_detail(self): - self.clients['admin'].manila('pool-list', params='--detail') - - def test_pools_list_with_share_type_filter(self): - share_type = self.create_share_type( - name=data_utils.rand_name('manilaclient_functional_test'), - snapshot_support=True, - ) - self.clients['admin'].manila( - 'pool-list', params='--share_type ' + share_type['ID'] - ) - - def test_pools_list_with_filters(self): - self.clients['admin'].manila( - 'pool-list', - params='--host myhost --backend mybackend --pool mypool', - ) - - def test_pools_list_by_user(self): - self.assertRaises( - exceptions.CommandFailed, self.clients['user'].manila, 'pool-list' - ) diff --git a/manilaclient/tests/functional/test_security_services.py b/manilaclient/tests/functional/test_security_services.py deleted file mode 100644 index e8a819b37..000000000 --- a/manilaclient/tests/functional/test_security_services.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright 2015 Mirantis Inc. -# 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. - -import ddt -from tempest.lib.common.utils import data_utils - -from manilaclient.tests.functional import base - - -@ddt.ddt -class SecurityServiceReadWriteTest(base.BaseTestCase): - def setUp(self): - super().setUp() - self.name = data_utils.rand_name('autotest') - self.description = 'fake_description' - self.user = 'fake_user' - self.password = 'fake_password' - self.server = 'fake_server' - self.domain = 'fake_domain' - self.dns_ip = '1.2.3.4' - self.ou = 'fake_ou' - self.default_ad_site = 'fake_default_ad_site' - - @ddt.data( - {'name': 'test_name'}, - {'description': 'test_description'}, - {'user': 'test_username'}, - {'password': 'test_password'}, - {'server': 'test_server'}, - {'default_ad_site': 'test_default_ad_site'}, - {'domain': 'test_domain'}, - {'dns_ip': 'test_dns_ip'}, - {'ou': 'test_ou'}, - {'name': '""'}, - {'description': '""'}, - {'user': '""'}, - {'password': '""'}, - {'server': '""'}, - {'default_ad_site': '""'}, - {'domain': '""'}, - {'dns_ip': '""'}, - {'ou': '""'}, - ) - def test_create_update_security_service(self, ss_data): - expected_data = { - 'name': self.name, - 'description': self.description, - 'user': self.user, - 'password': self.password, - 'server': self.server, - 'domain': self.domain, - 'dns_ip': self.dns_ip, - 'ou': self.ou, - } - - if 'default_ad_site' in ss_data: - expected_data.pop('server') - expected_data['default_ad_site'] = self.default_ad_site - - ss = self.create_security_service(**expected_data) - update = self.admin_client.update_security_service(ss['id'], **ss_data) - expected_data.update(ss_data) - - for k, v in expected_data.items(): - if v == '""': - self.assertEqual('None', update[k]) - else: - self.assertEqual(v, update[k]) diff --git a/manilaclient/tests/functional/test_services.py b/manilaclient/tests/functional/test_services.py deleted file mode 100644 index bdf424dbf..000000000 --- a/manilaclient/tests/functional/test_services.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2015 Mirantis Inc. -# 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. - -import ddt - -from manilaclient.tests.functional import base - - -@ddt.ddt -class ManilaClientTestServicesReadOnly(base.BaseTestCase): - @ddt.data("1.0", "2.0", "2.6", "2.7") - def test_services_list(self, microversion): - self.skip_if_microversion_not_supported(microversion) - self.admin_client.manila('service-list', microversion=microversion) - - def test_list_with_debug_flag(self): - self.clients['admin'].manila('service-list', flags='--debug') - - def test_shares_list_filter_by_host(self): - self.clients['admin'].manila('service-list', params='--host host') - - def test_shares_list_filter_by_binary(self): - self.clients['admin'].manila('service-list', params='--binary binary') - - def test_shares_list_filter_by_zone(self): - self.clients['admin'].manila('service-list', params='--zone zone') - - def test_shares_list_filter_by_status(self): - self.clients['admin'].manila('service-list', params='--status status') - - def test_shares_list_filter_by_state(self): - self.clients['admin'].manila('service-list', params='--state state') diff --git a/manilaclient/tests/functional/test_share_access.py b/manilaclient/tests/functional/test_share_access.py deleted file mode 100644 index be2682934..000000000 --- a/manilaclient/tests/functional/test_share_access.py +++ /dev/null @@ -1,384 +0,0 @@ -# Copyright 2015 Mirantis Inc. -# 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. - -import ast - -import ddt -from tempest.lib import exceptions as tempest_lib_exc - -from manilaclient import api_versions -from manilaclient import config -from manilaclient.tests.functional import base - -CONF = config.CONF - - -@ddt.ddt -class ShareAccessReadWriteBase(base.BaseTestCase): - protocol = None - access_level = None - - def setUp(self): - super().setUp() - if self.protocol not in CONF.enable_protocols: - message = f"{self.protocol} tests are disabled." - raise self.skipException(message) - if self.access_level not in CONF.access_levels_mapping.get( - self.protocol, '' - ).split(' '): - raise self.skipException( - f"{self.access_level} tests for {self.protocol} share " - "access are disabled." - ) - self.access_types = CONF.access_types_mapping.get( - self.protocol, '' - ).split(' ') - if not self.access_types: - raise self.skipException( - f"No access levels were provided for {self.protocol} " - "share access tests." - ) - - self.share = self.create_share( - share_protocol=self.protocol, public=True - ) - self.share_id = self.share['id'] - - # NOTE(vponomaryov): increase following int range when significant - # amount of new tests is added. - int_range = range(20, 50) - self.access_to = { - # NOTE(vponomaryov): list of unique values is required for ability - # to create lots of access rules for one share using different - # API microversions. - 'ip': [f'99.88.77.{i}' for i in int_range], - # NOTE(vponomaryov): following users are fakes and access rules - # that use it are expected to fail, but they are used only for - # API testing. - 'user': [f'foo_user_{i}' for i in int_range], - 'cert': [f'tenant_{i}.example.com' for i in int_range], - 'ipv6': [f'2001:db8::{i}' for i in int_range], - } - - def _test_create_list_access_rule_for_share( - self, microversion, metadata=None - ): - access_type = self.access_types[0] - - access = self.user_client.access_allow( - self.share['id'], - access_type, - self.access_to[access_type].pop(), - self.access_level, - metadata=metadata, - microversion=microversion, - ) - - return access - - @ddt.data( - *set( - [ - "1.0", - "2.0", - "2.6", - "2.7", - "2.21", - "2.33", - "2.44", - "2.45", - api_versions.MAX_VERSION, - ] - ) - ) - def test_create_list_access_rule_for_share(self, microversion): - self.skip_if_microversion_not_supported(microversion) - access = self._test_create_list_access_rule_for_share( - microversion=microversion - ) - access_list = self.user_client.list_access( - self.share['id'], microversion=microversion - ) - self.assertTrue( - any([item for item in access_list if access['id'] == item['id']]) - ) - self.assertTrue(any(a['access_type'] is not None for a in access_list)) - self.assertTrue(any(a['access_to'] is not None for a in access_list)) - self.assertTrue( - any(a['access_level'] is not None for a in access_list) - ) - if api_versions.APIVersion(microversion) >= api_versions.APIVersion( - "2.33" - ): - self.assertTrue( - all( - all( - key in access - for key in ('access_key', 'created_at', 'updated_at') - ) - for access in access_list - ) - ) - elif api_versions.APIVersion(microversion) >= api_versions.APIVersion( - "2.21" - ): - self.assertTrue(all('access_key' in a for a in access_list)) - else: - self.assertTrue(all('access_key' not in a for a in access_list)) - - @ddt.data("1.0", "2.0", "2.6", "2.7") - def test_create_list_access_rule_for_share_select_column( - self, microversion - ): - self.skip_if_microversion_not_supported(microversion) - self._test_create_list_access_rule_for_share(microversion=microversion) - access_list = self.user_client.list_access( - self.share['id'], - columns="access_type,access_to", - microversion=microversion, - ) - self.assertTrue(any(a['Access_Type'] is not None for a in access_list)) - self.assertTrue(any(a['Access_To'] is not None for a in access_list)) - self.assertTrue(all('Access_Level' not in a for a in access_list)) - self.assertTrue(all('access_level' not in a for a in access_list)) - - def _create_delete_access_rule( - self, share_id, access_type, access_to, microversion=None - ): - self.skip_if_microversion_not_supported(microversion) - if access_type not in self.access_types: - raise self.skipException( - f"'{access_type}' access rules is disabled for protocol " - f"'{self.protocol}'." - ) - - access = self.user_client.access_allow( - share_id, - access_type, - access_to, - self.access_level, - microversion=microversion, - ) - - self.assertEqual(share_id, access.get('share_id')) - self.assertEqual(access_type, access.get('access_type')) - self.assertEqual( - access_to.replace('\\\\', '\\'), access.get('access_to') - ) - self.assertEqual(self.access_level, access.get('access_level')) - if api_versions.APIVersion(microversion) >= api_versions.APIVersion( - "2.33" - ): - self.assertIn('access_key', access) - self.assertIn('created_at', access) - self.assertIn('updated_at', access) - elif api_versions.APIVersion(microversion) >= api_versions.APIVersion( - "2.21" - ): - self.assertIn('access_key', access) - else: - self.assertNotIn('access_key', access) - - self.user_client.wait_for_access_rule_status(share_id, access['id']) - self.user_client.access_deny(share_id, access['id']) - self.user_client.wait_for_access_rule_deletion(share_id, access['id']) - - self.assertRaises( - tempest_lib_exc.NotFound, - self.user_client.get_access, - share_id, - access['id'], - ) - - @ddt.data(*set(["2.45", api_versions.MAX_VERSION])) - def test_create_list_access_rule_with_metadata(self, microversion): - self.skip_if_microversion_not_supported(microversion) - - md1 = {"key1": "value1", "key2": "value2"} - md2 = {"key3": "value3", "key4": "value4"} - self._test_create_list_access_rule_for_share( - metadata=md1, microversion=microversion - ) - access = self._test_create_list_access_rule_for_share( - metadata=md2, microversion=microversion - ) - access_list = self.user_client.list_access( - self.share['id'], - metadata={"key4": "value4"}, - microversion=microversion, - ) - self.assertEqual(1, len(access_list)) - # Verify share metadata - get_access = self.user_client.access_show( - access_list[0]['id'], microversion=microversion - ) - metadata = ast.literal_eval(get_access['metadata']) - self.assertEqual(2, len(metadata)) - self.assertIn('key3', metadata) - self.assertIn('key4', metadata) - self.assertEqual(md2['key3'], metadata['key3']) - self.assertEqual(md2['key4'], metadata['key4']) - self.assertEqual(access['id'], access_list[0]['id']) - - self.user_client.access_deny(access['share_id'], access['id']) - self.user_client.wait_for_access_rule_deletion( - access['share_id'], access['id'] - ) - - @ddt.data(*set(["2.45", api_versions.MAX_VERSION])) - def test_create_update_show_access_rule_with_metadata(self, microversion): - self.skip_if_microversion_not_supported(microversion) - - md1 = {"key1": "value1", "key2": "value2"} - md2 = {"key3": "value3", "key2": "value4"} - # create a access rule with metadata - access = self._test_create_list_access_rule_for_share( - metadata=md1, microversion=microversion - ) - # get the access rule - get_access = self.user_client.access_show( - access['id'], microversion=microversion - ) - # verify access rule - self.assertEqual(access['id'], get_access['id']) - self.assertEqual(md1, ast.literal_eval(get_access['metadata'])) - - # update access rule metadata - self.user_client.access_set_metadata( - access['id'], metadata=md2, microversion=microversion - ) - get_access = self.user_client.access_show( - access['id'], microversion=microversion - ) - - # verify access rule after update access rule metadata - self.assertEqual( - {"key1": "value1", "key2": "value4", "key3": "value3"}, - ast.literal_eval(get_access['metadata']), - ) - self.assertEqual(access['id'], get_access['id']) - - @ddt.data(*set(["2.45", api_versions.MAX_VERSION])) - def test_delete_access_rule_metadata(self, microversion): - self.skip_if_microversion_not_supported(microversion) - - md = {"key1": "value1", "key2": "value2"} - # create a access rule with metadata - access = self._test_create_list_access_rule_for_share( - metadata=md, microversion=microversion - ) - # get the access rule - get_access = self.user_client.access_show( - access['id'], microversion=microversion - ) - - # verify access rule - self.assertEqual(access['id'], get_access['id']) - self.assertEqual(md, ast.literal_eval(get_access['metadata'])) - - # delete access rule metadata - self.user_client.access_unset_metadata( - access['id'], keys=["key1", "key2"], microversion=microversion - ) - get_access = self.user_client.access_show( - access['id'], microversion=microversion - ) - - # verify access rule after delete access rule metadata - self.assertEqual({}, ast.literal_eval(get_access['metadata'])) - self.assertEqual(access['id'], get_access['id']) - - @ddt.data("1.0", "2.0", "2.6", "2.7", "2.21", "2.33") - def test_create_delete_ip_access_rule(self, microversion): - self._create_delete_access_rule( - self.share_id, 'ip', self.access_to['ip'].pop(), microversion - ) - - @ddt.data("1.0", "2.0", "2.6", "2.7", "2.21", "2.33") - def test_create_delete_user_access_rule(self, microversion): - self._create_delete_access_rule( - self.share_id, 'user', CONF.username_for_user_rules, microversion - ) - - @ddt.data("1.0", "2.0", "2.6", "2.7", "2.21", "2.33") - def test_create_delete_cert_access_rule(self, microversion): - self._create_delete_access_rule( - self.share_id, 'cert', self.access_to['cert'].pop(), microversion - ) - - @ddt.data("2.38", api_versions.MAX_VERSION) - def test_create_delete_ipv6_access_rule(self, microversion): - self._create_delete_access_rule( - self.share_id, 'ip', self.access_to['ipv6'].pop(), microversion - ) - - -class NFSShareRWAccessReadWriteTest(ShareAccessReadWriteBase): - protocol = 'nfs' - access_level = 'rw' - - -class NFSShareROAccessReadWriteTest(ShareAccessReadWriteBase): - protocol = 'nfs' - access_level = 'ro' - - -class CIFSShareRWAccessReadWriteTest(ShareAccessReadWriteBase): - protocol = 'cifs' - access_level = 'rw' - - -class CIFSShareROAccessReadWriteTest(ShareAccessReadWriteBase): - protocol = 'cifs' - access_level = 'ro' - - -class GlusterFSShareRWAccessReadWriteTest(ShareAccessReadWriteBase): - protocol = 'glusterfs' - access_level = 'rw' - - -class GlusterFSShareROAccessReadWriteTest(ShareAccessReadWriteBase): - protocol = 'glusterfs' - access_level = 'ro' - - -class HDFSShareRWAccessReadWriteTest(ShareAccessReadWriteBase): - protocol = 'hdfs' - access_level = 'rw' - - -class HDFSShareROAccessReadWriteTest(ShareAccessReadWriteBase): - protocol = 'hdfs' - access_level = 'ro' - - -class MAPRFSShareRWAccessReadWriteTest(ShareAccessReadWriteBase): - protocol = 'maprfs' - access_level = 'rw' - - -class MAPRFSShareROAccessReadWriteTest(ShareAccessReadWriteBase): - protocol = 'maprfs' - access_level = 'ro' - - -def load_tests(loader, tests, _): - result = [] - for test_case in tests: - if type(test_case._tests[0]) is ShareAccessReadWriteBase: - continue - result.append(test_case) - return loader.suiteClass(result) diff --git a/manilaclient/tests/functional/test_share_network_subnets.py b/manilaclient/tests/functional/test_share_network_subnets.py deleted file mode 100644 index 26c43330b..000000000 --- a/manilaclient/tests/functional/test_share_network_subnets.py +++ /dev/null @@ -1,129 +0,0 @@ -# Copyright 2019 NetApp -# 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. - -import ddt -from manilaclient.tests.functional import base -from manilaclient.tests.functional import utils -from tempest.lib.common.utils import data_utils -from tempest.lib import exceptions - - -@ddt.ddt -@utils.skip_if_microversion_not_supported('2.51') -class ShareNetworkSubnetsReadWriteTest(base.BaseTestCase): - def setUp(self): - super().setUp() - self.name = data_utils.rand_name('autotest') - self.description = 'fake_description' - self.neutron_net_id = 'fake_neutron_net_id' - self.neutron_subnet_id = 'fake_neutron_subnet_id' - - self.sn = self.create_share_network( - name=self.name, - description=self.description, - neutron_net_id=self.neutron_net_id, - neutron_subnet_id=self.neutron_subnet_id, - ) - - def test_get_share_network_subnet(self): - default_subnet = utils.get_default_subnet( - self.user_client, self.sn['id'] - ) - - subnet = self.user_client.get_share_network_subnet( - self.sn['id'], default_subnet['id'] - ) - - self.assertEqual(self.neutron_net_id, subnet['neutron_net_id']) - self.assertEqual(self.neutron_subnet_id, subnet['neutron_subnet_id']) - - def test_get_invalid_share_network_subnet(self): - self.assertRaises( - exceptions.CommandFailed, - self.user_client.get_share_network_subnet, - self.sn['id'], - 'invalid_subnet_id', - ) - - def _get_availability_zone(self): - availability_zones = self.user_client.list_availability_zones() - return availability_zones[0]['Name'] - - def test_add_share_network_subnet_to_share_network(self): - neutron_net_id = 'new_neutron_net_id' - neutron_subnet_id = 'new_neutron_subnet_id' - availability_zone = self._get_availability_zone() - - subnet = self.add_share_network_subnet( - self.sn['id'], - neutron_net_id, - neutron_subnet_id, - availability_zone, - cleanup_in_class=False, - ) - - self.assertEqual(neutron_net_id, subnet['neutron_net_id']) - self.assertEqual(neutron_subnet_id, subnet['neutron_subnet_id']) - self.assertEqual(availability_zone, subnet['availability_zone']) - - @ddt.data( - {'neutron_net_id': None, 'neutron_subnet_id': 'fake_subnet_id'}, - {'neutron_net_id': 'fake_net_id', 'neutron_subnet_id': None}, - {'availability_zone': 'invalid_availability_zone'}, - ) - def test_add_invalid_share_network_subnet_to_share_network(self, params): - self.assertRaises( - exceptions.CommandFailed, - self.add_share_network_subnet, - self.sn['id'], - **params, - ) - - def test_add_share_network_subnet_to_invalid_share_network(self): - self.assertRaises( - exceptions.CommandFailed, - self.add_share_network_subnet, - 'invalid_share_network', - self.neutron_net_id, - self.neutron_subnet_id, - ) - - def test_add_delete_share_network_subnet_from_share_network(self): - neutron_net_id = 'new_neutron_net_id' - neutron_subnet_id = 'new_neutron_subnet_id' - availability_zone = self._get_availability_zone() - - subnet = self.add_share_network_subnet( - self.sn['id'], - neutron_net_id, - neutron_subnet_id, - availability_zone, - cleanup_in_class=False, - ) - self.user_client.delete_share_network_subnet( - share_network_subnet=subnet['id'], share_network=self.sn['id'] - ) - - self.user_client.wait_for_share_network_subnet_deletion( - share_network_subnet=subnet['id'], share_network=self.sn['id'] - ) - - def test_delete_invalid_share_network_subnet(self): - self.assertRaises( - exceptions.NotFound, - self.user_client.delete_share_network_subnet, - share_network_subnet='invalid_subnet_id', - share_network=self.sn['id'], - ) diff --git a/manilaclient/tests/functional/test_share_networks.py b/manilaclient/tests/functional/test_share_networks.py deleted file mode 100644 index 3a3dacb8c..000000000 --- a/manilaclient/tests/functional/test_share_networks.py +++ /dev/null @@ -1,695 +0,0 @@ -# Copyright 2015 Mirantis Inc. -# 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. - -import ast -import ddt -from tempest.lib.common.utils import data_utils -from tempest.lib import exceptions as tempest_lib_exc -import time - -from manilaclient import config -from manilaclient import exceptions -from manilaclient.tests.functional import base -from manilaclient.tests.functional import utils - -SECURITY_SERVICE_UPDATE_VERSION = '2.63' -CONF = config.CONF - - -@ddt.ddt -class ShareNetworksReadWriteTest(base.BaseTestCase): - def setUp(self): - super().setUp() - self.name = data_utils.rand_name('autotest') - self.description = 'fake_description' - self.neutron_net_id = 'fake_neutron_net_id' - self.neutron_subnet_id = 'fake_neutron_subnet_id' - - self.sn = self.create_share_network( - name=self.name, - description=self.description, - neutron_net_id=self.neutron_net_id, - neutron_subnet_id=self.neutron_subnet_id, - ) - - @ddt.data( - {'name': data_utils.rand_name('autotest_share_network_name')}, - {'description': 'fake_description'}, - { - 'neutron_net_id': 'fake_neutron_net_id', - 'neutron_subnet_id': 'fake_neutron_subnet_id', - }, - ) - def test_create_delete_share_network(self, net_data): - share_subnet_support = utils.share_network_subnets_are_supported() - share_subnet_fields = ( - ['neutron_net_id', 'neutron_subnet_id', 'availability_zone'] - if share_subnet_support - else [] - ) - sn = self.create_share_network(cleanup_in_class=False, **net_data) - default_subnet = ( - utils.get_default_subnet(self.user_client, sn['id']) - if share_subnet_support - else None - ) - - expected_data = { - 'name': 'None', - 'description': 'None', - 'neutron_net_id': 'None', - 'neutron_subnet_id': 'None', - } - expected_data.update(net_data) - share_network_expected_data = [ - (k, v) - for k, v in expected_data.items() - if k not in share_subnet_fields - ] - share_subnet_expected_data = [ - (k, v) - for k, v in expected_data.items() - if k in share_subnet_fields - ] - - for k, v in share_network_expected_data: - self.assertEqual(v, sn[k]) - for k, v in share_subnet_expected_data: - self.assertEqual(v, default_subnet[k]) - - self.admin_client.delete_share_network(sn['id']) - self.admin_client.wait_for_share_network_deletion(sn['id']) - - @utils.skip_if_microversion_not_supported('2.51') - def test_create_delete_share_network_with_az(self): - share_subnet_fields = [ - 'neutron_net_id', - 'neutron_subnet_id', - 'availability_zone', - ] - az = self.user_client.list_availability_zones()[0] - net_data = { - 'neutron_net_id': 'fake_neutron_net_id', - 'neutron_subnet_id': 'fake_neutron_subnet_id', - 'availability_zone': az['Name'], - } - sn = self.create_share_network(cleanup_in_class=False, **net_data) - default_subnet = utils.get_subnet_by_availability_zone_name( - self.user_client, sn['id'], az['Name'] - ) - - expected_data = { - 'name': 'None', - 'description': 'None', - 'neutron_net_id': 'None', - 'neutron_subnet_id': 'None', - 'availability_zone': 'None', - } - expected_data.update(net_data) - share_network_expected_data = [ - (k, v) - for k, v in expected_data.items() - if k not in share_subnet_fields - ] - share_subnet_expected_data = [ - (k, v) - for k, v in expected_data.items() - if k in share_subnet_fields - ] - - for k, v in share_network_expected_data: - self.assertEqual(v, sn[k]) - for k, v in share_subnet_expected_data: - self.assertEqual(v, default_subnet[k]) - - self.admin_client.delete_share_network(sn['id']) - self.admin_client.wait_for_share_network_deletion(sn['id']) - - def test_get_share_network_with_neutron_data(self): - get = self.admin_client.get_share_network(self.sn['id']) - - self.assertEqual(self.name, get['name']) - self.assertEqual(self.description, get['description']) - if not utils.share_network_subnets_are_supported(): - self.assertEqual(self.neutron_net_id, get['neutron_net_id']) - self.assertEqual(self.neutron_subnet_id, get['neutron_subnet_id']) - - def _get_expected_update_data(self, net_data, net_creation_data): - # NOTE(dviroel): When subnets are supported, the outputs are converted - # from string to literal structures in order to process the content of - # 'share_network_subnets' field. - default_return_value = ( - None if utils.share_network_subnets_are_supported() else 'None' - ) - - expected_nn_id = ( - default_return_value - if net_data.get('neutron_net_id') - else net_creation_data.get('neutron_net_id', default_return_value) - ) - expected_nsn_id = ( - default_return_value - if net_data.get('neutron_subnet_id') - else net_creation_data.get( - 'neutron_subnet_id', default_return_value - ) - ) - return expected_nn_id, expected_nsn_id - - @ddt.data( - ({'name': data_utils.rand_name('autotest_share_network_name')}, {}), - ({'description': 'fake_description'}, {}), - ( - { - 'neutron_net_id': 'fake_neutron_net_id', - 'neutron_subnet_id': 'fake_neutron_subnet_id', - }, - {}, - ), - ({'name': '""'}, {}), - ({'description': '""'}, {}), - ( - {'neutron_net_id': '""'}, - { - 'neutron_net_id': 'fake_nn_id', - 'neutron_subnet_id': 'fake_nsn_id', - }, - ), - ( - {'neutron_subnet_id': '""'}, - { - 'neutron_net_id': 'fake_nn_id', - 'neutron_subnet_id': 'fake_nsn_id', - }, - ), - ) - @ddt.unpack - def test_create_update_share_network(self, net_data, net_creation_data): - sn = self.create_share_network( - cleanup_in_class=False, **net_creation_data - ) - - update = self.admin_client.update_share_network(sn['id'], **net_data) - - expected_nn_id, expected_nsn_id = self._get_expected_update_data( - net_data, net_creation_data - ) - - expected_data = { - 'name': 'None', - 'description': 'None', - 'neutron_net_id': expected_nn_id, - 'neutron_subnet_id': expected_nsn_id, - } - subnet_keys = [] - if utils.share_network_subnets_are_supported(): - subnet_keys = ['neutron_net_id', 'neutron_subnet_id'] - subnet = ast.literal_eval(update['share_network_subnets']) - - update_values = dict( - [(k, v) for k, v in net_data.items() if v != '""'] - ) - expected_data.update(update_values) - - for k, v in expected_data.items(): - if k in subnet_keys: - self.assertEqual(v, subnet[0][k]) - else: - self.assertEqual(v, update[k]) - - self.admin_client.delete_share_network(sn['id']) - self.admin_client.wait_for_share_network_deletion(sn['id']) - - @ddt.data(True, False) - def test_list_share_networks(self, all_tenants): - share_networks = self.admin_client.list_share_networks(all_tenants) - - self.assertTrue( - any(self.sn['id'] == sn['id'] for sn in share_networks) - ) - for sn in share_networks: - self.assertEqual(2, len(sn)) - self.assertIn('id', sn) - self.assertIn('name', sn) - - def test_list_share_networks_select_column(self): - share_networks = self.admin_client.list_share_networks(columns="id") - self.assertTrue(any(s['Id'] is not None for s in share_networks)) - self.assertTrue(all('Name' not in s for s in share_networks)) - self.assertTrue(all('name' not in s for s in share_networks)) - - def _list_share_networks_with_filters(self, filters): - assert_subnet_fields = utils.share_network_subnets_are_supported() - share_subnet_fields = ( - ['neutron_subnet_id', 'neutron_net_id'] - if assert_subnet_fields - else [] - ) - share_network_filters = [ - (k, v) for k, v in filters.items() if k not in share_subnet_fields - ] - share_network_subnet_filters = [ - (k, v) for k, v in filters.items() if k in share_subnet_fields - ] - share_networks = self.admin_client.list_share_networks(filters=filters) - - self.assertGreater(len(share_networks), 0) - self.assertTrue( - any(self.sn['id'] == sn['id'] for sn in share_networks) - ) - for sn in share_networks: - try: - share_network = self.admin_client.get_share_network(sn['id']) - default_subnet = ( - utils.get_default_subnet(self.user_client, sn['id']) - if assert_subnet_fields - else None - ) - except tempest_lib_exc.NotFound: - # NOTE(vponomaryov): Case when some share network was deleted - # between our 'list' and 'get' requests. Skip such case. - continue - for k, v in share_network_filters: - self.assertIn(k, share_network) - self.assertEqual(v, share_network[k]) - for k, v in share_network_subnet_filters: - self.assertIn(k, default_subnet) - self.assertEqual(v, default_subnet[k]) - - def test_list_share_networks_filter_by_project_id(self): - project_id = self.admin_client.get_project_id( - self.admin_client.tenant_name - ) - filters = {'project_id': project_id} - self._list_share_networks_with_filters(filters) - - def test_list_share_networks_filter_by_name(self): - filters = {'name': self.name} - self._list_share_networks_with_filters(filters) - - def test_list_share_networks_filter_by_description(self): - filters = {'description': self.description} - self._list_share_networks_with_filters(filters) - - def test_list_share_networks_filter_by_neutron_net_id(self): - filters = {'neutron_net_id': self.neutron_net_id} - self._list_share_networks_with_filters(filters) - - def test_list_share_networks_filter_by_neutron_subnet_id(self): - filters = {'neutron_subnet_id': self.neutron_subnet_id} - self._list_share_networks_with_filters(filters) - - @ddt.data('name', 'description') - def test_list_share_networks_filter_by_inexact(self, option): - self.create_share_network( - name=data_utils.rand_name('autotest_inexact'), - description='fake_description_inexact', - neutron_net_id='fake_neutron_net_id', - neutron_subnet_id='fake_neutron_subnet_id', - ) - - filters = {option + '~': 'inexact'} - share_networks = self.admin_client.list_share_networks(filters=filters) - - self.assertGreater(len(share_networks), 0) - - def test_list_share_networks_by_inexact_unicode_option(self): - self.create_share_network( - name='网络名称', - description='网络描述', - neutron_net_id='fake_neutron_net_id', - neutron_subnet_id='fake_neutron_subnet_id', - ) - - filters = {'name~': '名称'} - share_networks = self.admin_client.list_share_networks(filters=filters) - - self.assertGreater(len(share_networks), 0) - - filters = {'description~': '描述'} - share_networks = self.admin_client.list_share_networks(filters=filters) - - self.assertGreater(len(share_networks), 0) - - def test_share_network_reset_status(self): - share_network = self.create_share_network( - client=self.user_client, - name='cool_net_name', - description='fakedescription', - neutron_net_id='fake_neutron_net_id', - neutron_subnet_id='fake_neutron_subnet_id', - ) - - # Admin operation - self.admin_client.share_network_reset_state( - share_network['id'], - 'error', - microversion=SECURITY_SERVICE_UPDATE_VERSION, - ) - - self.user_client.wait_for_resource_status( - share_network['id'], - 'error', - microversion=SECURITY_SERVICE_UPDATE_VERSION, - resource_type="share_network", - ) - - def test_share_network_security_service_add(self): - share_network = self.create_share_network( - client=self.user_client, - name='cool_net_name', - description='fakedescription', - neutron_net_id='fake_neutron_net_id', - neutron_subnet_id='fake_neutron_subnet_id', - ) - new_security_service = self.create_security_service( - client=self.user_client - ) - - check_result = ( - self.user_client.share_network_security_service_add_check( - share_network['id'], - security_service_id=new_security_service['id'], - ) - ) - - self.assertEqual(check_result['compatible'], 'True') - - self.user_client.share_network_security_service_add( - share_network['id'], new_security_service['id'] - ) - - network_services = ( - self.user_client.share_network_security_service_list( - share_network['id'] - ) - ) - - self.assertEqual(len(network_services), 1) - self.assertEqual(network_services[0]['id'], new_security_service['id']) - - def test_share_network_security_service_update(self): - share_network = self.create_share_network( - client=self.user_client, - name='cool_net_name', - description='fakedescription', - neutron_net_id='fake_neutron_net_id', - neutron_subnet_id='fake_neutron_subnet_id', - ) - current_name = 'current' - new_name = 'new' - current_security_service = self.create_security_service( - client=self.user_client, name=current_name - ) - new_security_service = self.create_security_service( - client=self.user_client, name=new_name - ) - - check_result = ( - self.user_client.share_network_security_service_add_check( - share_network['id'], current_security_service['id'] - ) - ) - - self.assertEqual(check_result['compatible'], 'True') - - self.user_client.share_network_security_service_add( - share_network['id'], current_security_service['id'] - ) - - network_services = ( - self.user_client.share_network_security_service_list( - share_network['id'] - ) - ) - - self.assertEqual(len(network_services), 1) - self.assertEqual(network_services[0]['name'], current_name) - - check_result = ( - self.user_client.share_network_security_service_update_check( - share_network['id'], - current_security_service['id'], - new_security_service['id'], - ) - ) - - self.assertEqual(check_result['compatible'], 'True') - - self.user_client.share_network_security_service_update( - share_network['id'], - current_security_service['id'], - new_security_service['id'], - ) - - network_services = ( - self.user_client.share_network_security_service_list( - share_network['id'] - ) - ) - - self.assertEqual(len(network_services), 1) - self.assertEqual(network_services[0]['name'], new_name) - - def test_share_network_subnet_create_check(self): - share_network = self.create_share_network( - client=self.user_client, - description='fakedescription', - ) - - check_result = self.user_client.share_network_subnet_create_check( - share_network['id'], - neutron_net_id='fake_neutron_net_id', - neutron_subnet_id='fake_neutron_subnet_id', - ) - - self.assertEqual(check_result['compatible'], 'True') - - @ddt.data( - {'neutron_net_id': None, 'neutron_subnet_id': 'fake_subnet_id'}, - {'neutron_net_id': 'fake_net_id', 'neutron_subnet_id': None}, - {'availability_zone': 'invalid_availability_zone'}, - ) - def test_check_add_share_network_subnet_with_invalid_params(self, params): - self.assertRaises( - tempest_lib_exc.CommandFailed, - self.user_client.share_network_subnet_create_check, - self.sn['id'], - **params, - ) - - def test_check_add_share_network_subnet_to_invalid_share_network(self): - self.assertRaises( - tempest_lib_exc.CommandFailed, - self.user_client.share_network_subnet_create_check, - 'invalid_share_network', - self.neutron_net_id, - self.neutron_subnet_id, - ) - - -class ShareNetworkSecurityServiceCheckReadWriteTests(base.BaseTestCase): - protocol = None - - def setUp(self): - super().setUp() - if self.protocol not in CONF.enable_protocols: - message = f"{self.protocol} tests are disabled." - raise self.skipException(message) - self.client = self.get_user_client() - if not self.client.share_network: - message = "Can run only with DHSS=True mode" - raise self.skipException(message) - - def _wait_for_update_security_service_compatible_result( - self, - share_network, - current_security_service, - new_security_service=None, - ): - compatible_expected_result = 'True' - check_is_compatible = 'None' - tentatives = 0 - - # There might be a delay from the time the check is requested until - # the backend has performed all checks - while check_is_compatible != compatible_expected_result: - tentatives += 1 - if not new_security_service: - check_is_compatible = ( - self.user_client.share_network_security_service_add_check( - share_network['id'], current_security_service['id'] - ) - )['compatible'] - else: - check_is_compatible = ( - self.user_client.share_network_security_service_update_check( - share_network['id'], - current_security_service['id'], - new_security_service['id'], - ) - )['compatible'] - if tentatives > 3: - timeout_message = ( - "Share network security service add/update check did not " - "reach 'compatible=True' within 15 seconds." - ) - raise exceptions.TimeoutException(message=timeout_message) - time.sleep(5) - - def test_check_if_security_service_can_be_added_to_share_network_in_use( - self, - ): - share_network = self.create_share_network( - client=self.user_client, - description='fakedescription', - neutron_net_id='fake_neutron_net_id', - neutron_subnet_id='fake_neutron_subnet_id', - ) - # Create a share so we can be sure that a share server will exist and - # the check will be performed in the backends - self.create_share( - self.protocol, - client=self.user_client, - share_network=share_network['id'], - ) - - current_security_service = self.create_security_service( - client=self.user_client - ) - - check_result = ( - self.user_client.share_network_security_service_add_check( - share_network['id'], current_security_service['id'] - ) - ) - - self.assertEqual(check_result['compatible'], 'None') - - self._wait_for_update_security_service_compatible_result( - share_network, current_security_service - ) - - def test_add_and_update_security_service_when_share_network_is_in_use( - self, - ): - share_network = self.create_share_network( - client=self.user_client, - name='cool_net_name', - description='fakedescription', - neutron_net_id='fake_neutron_net_id', - neutron_subnet_id='fake_neutron_subnet_id', - ) - - # Create a share so we can be sure that a share server will exist and - # the check will be performed in the backends - self.create_share( - self.protocol, - name='fake_share_name', - share_network=share_network['id'], - client=self.user_client, - ) - - current_security_service = self.create_security_service( - client=self.user_client, name='current_security_service' - ) - new_security_service = self.create_security_service( - client=self.user_client, name='new_security_service' - ) - - check_result = ( - self.user_client.share_network_security_service_add_check( - share_network['id'], current_security_service['id'] - ) - ) - - self.assertEqual(check_result['compatible'], 'None') - - self._wait_for_update_security_service_compatible_result( - share_network, current_security_service - ) - - self.user_client.share_network_security_service_add( - share_network['id'], current_security_service['id'] - ) - - network_services = ( - self.user_client.share_network_security_service_list( - share_network['id'] - ) - ) - - self.assertEqual(len(network_services), 1) - self.assertEqual( - network_services[0]['name'], current_security_service['name'] - ) - - self.user_client.wait_for_resource_status( - share_network['id'], - 'active', - microversion=SECURITY_SERVICE_UPDATE_VERSION, - resource_type="share_network", - ) - - check_result = ( - self.user_client.share_network_security_service_update_check( - share_network['id'], - current_security_service['id'], - new_security_service['id'], - ) - ) - - self.assertEqual(check_result['compatible'], 'None') - - self._wait_for_update_security_service_compatible_result( - share_network, - current_security_service, - new_security_service=new_security_service, - ) - - self.user_client.share_network_security_service_update( - share_network['id'], - current_security_service['id'], - new_security_service['id'], - ) - - network_services = ( - self.user_client.share_network_security_service_list( - share_network['id'] - ) - ) - - self.assertEqual(len(network_services), 1) - self.assertEqual( - network_services[0]['name'], new_security_service['name'] - ) - - self.user_client.wait_for_resource_status( - share_network['id'], - 'active', - microversion=SECURITY_SERVICE_UPDATE_VERSION, - resource_type="share_network", - ) - - -base_security_service_check = ShareNetworkSecurityServiceCheckReadWriteTests - - -class ShareNetworkSecServiceCheckRWNFSTest(base_security_service_check): - protocol = 'nfs' - - -class ShareNetworkSecServiceCheckRWTestsCIFSTest(base_security_service_check): - protocol = 'cifs' diff --git a/manilaclient/tests/functional/test_share_replica_export_locations.py b/manilaclient/tests/functional/test_share_replica_export_locations.py deleted file mode 100644 index 219a92003..000000000 --- a/manilaclient/tests/functional/test_share_replica_export_locations.py +++ /dev/null @@ -1,122 +0,0 @@ -# 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. - -import ddt -from oslo_utils import uuidutils -import testtools - -from manilaclient import config -from manilaclient.tests.functional import base -from manilaclient.tests.functional import utils - -CONF = config.CONF - - -@ddt.ddt -@testtools.skipUnless( - CONF.run_replication_tests, "Replication tests are disabled." -) -@utils.skip_if_microversion_not_supported('2.47') -class ShareReplicaExportLocationsTest(base.BaseTestCase): - def _create_share_and_replica(self): - replication_type = CONF.replication_type - share_type = self.create_share_type( - driver_handles_share_servers=False, - extra_specs={'replication_type': replication_type}, - ) - share = self.create_share( - share_type=share_type['ID'], client=self.get_user_client() - ) - share_replica = self.create_share_replica(share['id']) - return share, share_replica - - @ddt.data('admin', 'user') - def test_list_share_export_locations(self, role): - share, share_replica = self._create_share_and_replica() - client = self.admin_client if role == 'admin' else self.user_client - export_locations = client.list_share_replica_export_locations( - share_replica['id'] - ) - - self.assertGreater(len(export_locations), 0) - expected_keys = [ - 'ID', - 'Path', - 'Preferred', - 'Replica State', - 'Availability Zone', - ] - - for el in export_locations: - for key in expected_keys: - self.assertIn(key, el) - self.assertTrue(uuidutils.is_uuid_like(el['ID'])) - self.assertIn(el['Preferred'], ('True', 'False')) - - @ddt.data('admin', 'user') - def test_list_share_export_locations_with_columns(self, role): - share, share_replica = self._create_share_and_replica() - client = self.admin_client if role == 'admin' else self.user_client - export_locations = client.list_share_replica_export_locations( - share_replica['id'], columns='id,path' - ) - - self.assertGreater(len(export_locations), 0) - expected_keys = ('Id', 'Path') - unexpected_keys = ('Updated At', 'Created At') - for el in export_locations: - for key in expected_keys: - self.assertIn(key, el) - for key in unexpected_keys: - self.assertNotIn(key, el) - self.assertTrue(uuidutils.is_uuid_like(el['Id'])) - - @ddt.data('admin', 'user') - def test_get_share_replica_export_location(self, role): - share, share_replica = self._create_share_and_replica() - client = self.admin_client if role == 'admin' else self.user_client - export_locations = client.list_share_replica_export_locations( - share_replica['id'] - ) - - el = client.get_share_replica_export_location( - share_replica['id'], export_locations[0]['ID'] - ) - - expected_keys = [ - 'path', - 'updated_at', - 'created_at', - 'id', - 'preferred', - 'replica_state', - 'availability_zone', - ] - if role == 'admin': - expected_keys.extend(['is_admin_only', 'share_instance_id']) - for key in expected_keys: - self.assertIn(key, el) - if role == 'admin': - self.assertTrue(uuidutils.is_uuid_like(el['share_instance_id'])) - self.assertIn(el['is_admin_only'], ('True', 'False')) - self.assertTrue(uuidutils.is_uuid_like(el['id'])) - self.assertIn(el['preferred'], ('True', 'False')) - for list_k, get_k in ( - ('ID', 'id'), - ('Path', 'path'), - ('Preferred', 'preferred'), - ('Replica State', 'replica_state'), - ('Availability Zone', 'availability_zone'), - ): - self.assertEqual(export_locations[0][list_k], el[get_k]) diff --git a/manilaclient/tests/functional/test_share_replicas.py b/manilaclient/tests/functional/test_share_replicas.py deleted file mode 100644 index f7465688e..000000000 --- a/manilaclient/tests/functional/test_share_replicas.py +++ /dev/null @@ -1,51 +0,0 @@ -# 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 manilaclient import config -from manilaclient.tests.functional import base -from manilaclient.tests.functional import utils - -CONF = config.CONF - - -@utils.skip_if_microversion_not_supported('2.72') -class ShareReplicasTest(base.BaseTestCase): - def _create_share_and_replica(self): - replication_type = CONF.replication_type - share_type = self.create_share_type( - driver_handles_share_servers=True, - extra_specs={'replication_type': replication_type}, - ) - share_network = self.create_share_network() - share = self.create_share( - share_type=share_type['ID'], - share_network=share_network['id'], - client=self.get_user_client(), - ) - share_replica = self.create_share_replica( - share['id'], - share_network=share_network['id'], - wait_for_creation=True, - client=self.get_user_client(), - ) - return share, share_replica - - def test_share_replica_create(self): - share, share_replica = self._create_share_and_replica() - self.assertEqual(share['id'], share_replica['share_id']) - - def test_share_replica_delete(self): - share, share_replica = self._create_share_and_replica() - self.user_client.delete_share_replica(share_replica['id']) - self.user_client.wait_for_share_replica_deletion(share_replica['id']) diff --git a/manilaclient/tests/functional/test_share_servers.py b/manilaclient/tests/functional/test_share_servers.py deleted file mode 100644 index 63e4c19c9..000000000 --- a/manilaclient/tests/functional/test_share_servers.py +++ /dev/null @@ -1,405 +0,0 @@ -# Copyright 2015 Mirantis Inc. -# 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. - -import ast -import ddt -import testtools - -from tempest.lib.common.utils import data_utils -from tempest.lib import exceptions - -from manilaclient.common import constants -from manilaclient import config -from manilaclient.tests.functional import base -from manilaclient.tests.functional import utils - -CONF = config.CONF - - -@ddt.ddt -class ShareServersReadOnlyTest(base.BaseTestCase): - def setUp(self): - super().setUp() - self.client = self.get_admin_client() - - def test_share_server_list(self): - self.client.list_share_servers() - - def test_share_server_list_with_host_param(self): - self.client.list_share_servers(filters={'host': 'fake_host'}) - - def test_share_server_list_with_status_param(self): - self.client.list_share_servers(filters={'status': 'fake_status'}) - - def test_share_server_list_with_share_network_param(self): - self.client.list_share_servers(filters={'share_network': 'fake_sn'}) - - def test_share_server_list_with_project_id_param(self): - self.client.list_share_servers( - filters={'project_id': 'fake_project_id'} - ) - - @ddt.data( - 'host', - 'status', - 'project_id', - 'share_network', - 'host,status,project_id,share_network', - ) - def test_share_server_list_with_specified_columns(self, columns): - self.client.list_share_servers(columns=columns) - - def test_share_server_list_by_user(self): - self.assertRaises( - exceptions.CommandFailed, self.user_client.list_share_servers - ) - - -@ddt.ddt -class ShareServersReadWriteBase(base.BaseTestCase): - protocol = None - - def setUp(self): - super().setUp() - if not CONF.run_share_servers_tests: - message = "share-server tests are disabled." - raise self.skipException(message) - if self.protocol not in CONF.enable_protocols: - message = f"{self.protocol} tests are disabled." - raise self.skipException(message) - self.client = self.get_admin_client() - if not self.client.share_network: - message = "Can run only with DHSS=True mode" - raise self.skipException(message) - - def _create_share_and_share_network(self): - name = data_utils.rand_name('autotest_share_name') - description = data_utils.rand_name('autotest_share_description') - - common_share_network = self.client.get_share_network( - self.client.share_network - ) - share_net_info = ( - utils.get_default_subnet( - self.user_client, common_share_network['id'] - ) - if utils.share_network_subnets_are_supported() - else common_share_network - ) - neutron_net_id = ( - share_net_info['neutron_net_id'] - if 'none' not in share_net_info['neutron_net_id'].lower() - else None - ) - neutron_subnet_id = ( - share_net_info['neutron_subnet_id'] - if 'none' not in share_net_info['neutron_subnet_id'].lower() - else None - ) - share_network = self.client.create_share_network( - neutron_net_id=neutron_net_id, - neutron_subnet_id=neutron_subnet_id, - ) - - self.share = self.create_share( - share_protocol=self.protocol, - size=1, - name=name, - description=description, - share_network=share_network['id'], - client=self.client, - wait_for_creation=True, - ) - self.share = self.client.get_share(self.share['id']) - return self.share, share_network - - def _delete_share_and_share_server(self, share_id, share_server_id): - # Delete share - self.client.delete_share(share_id) - self.client.wait_for_share_deletion(share_id) - - # Delete share server - self.client.delete_share_server(share_server_id) - self.client.wait_for_share_server_deletion(share_server_id) - - def test_get_and_delete_share_server(self): - self.share, share_network = self._create_share_and_share_network() - share_server_id = self.client.get_share(self.share['id'])[ - 'share_server_id' - ] - - # Get share server - server = self.client.get_share_server(share_server_id) - expected_keys = ( - 'id', - 'host', - 'status', - 'created_at', - 'updated_at', - 'share_network_id', - 'share_network_name', - 'project_id', - ) - - if utils.is_microversion_supported('2.49'): - expected_keys += ('identifier', 'is_auto_deletable') - - for key in expected_keys: - self.assertIn(key, server) - - self._delete_share_and_share_server(self.share['id'], share_server_id) - self.client.delete_share_network(share_network['id']) - - @testtools.skipUnless( - CONF.run_manage_tests, 'Share Manage/Unmanage tests are disabled.' - ) - @utils.skip_if_microversion_not_supported('2.49') - def test_manage_and_unmanage_share_server(self): - share, share_network = self._create_share_and_share_network() - share_server_id = self.client.get_share(self.share['id'])[ - 'share_server_id' - ] - server = self.client.get_share_server(share_server_id) - server_host = server['host'] - export_location = self.client.list_share_export_locations( - self.share['id'] - )[0]['Path'] - share_host = share['host'] - identifier = server['identifier'] - - self.assertEqual('True', server['is_auto_deletable']) - - # Unmanages share - self.client.unmanage_share(share['id']) - self.client.wait_for_share_deletion(share['id']) - - server = self.client.get_share_server(share_server_id) - self.assertEqual('False', server['is_auto_deletable']) - - # Unmanages share server - self.client.unmanage_server(share_server_id) - self.client.wait_for_share_server_deletion(share_server_id) - - # Manage share server - managed_share_server_id = self.client.share_server_manage( - server_host, share_network['id'], identifier - ) - self.client.wait_for_resource_status( - managed_share_server_id, - constants.STATUS_ACTIVE, - resource_type='share_server', - ) - - managed_server = self.client.get_share_server(managed_share_server_id) - self.assertEqual('False', managed_server['is_auto_deletable']) - - # Manage share - managed_share_id = self.client.manage_share( - share_host, self.protocol, export_location, managed_share_server_id - ) - self.client.wait_for_resource_status( - managed_share_id, constants.STATUS_AVAILABLE - ) - - self._delete_share_and_share_server( - managed_share_id, managed_share_server_id - ) - self.client.delete_share_network(share_network['id']) - - -class ShareServersReadWriteNFSTest(ShareServersReadWriteBase): - protocol = 'nfs' - - -class ShareServersReadWriteCIFSTest(ShareServersReadWriteBase): - protocol = 'cifs' - - -@ddt.ddt -@utils.skip_if_microversion_not_supported('2.57') -class ShareServersMigrationBase(base.BaseTestCase): - protocol = None - - def setUp(self): - super().setUp() - if not CONF.run_share_servers_tests: - message = "Share-server tests are disabled." - raise self.skipException(message) - if self.protocol not in CONF.enable_protocols: - message = f"{self.protocol} tests are disabled." - raise self.skipException(message) - self.client = self.get_admin_client() - if not self.client.share_network: - message = "Can run only with DHSS=True mode" - raise self.skipException(message) - if not CONF.run_share_servers_migration_tests: - message = "Share server migration tests are disabled." - raise self.skipException(message) - - def _create_share_and_share_network(self): - name = data_utils.rand_name('autotest_share_name') - description = data_utils.rand_name('autotest_share_description') - - common_share_network = self.client.get_share_network( - self.client.share_network - ) - share_net_info = utils.get_default_subnet( - self.client, common_share_network['id'] - ) - - neutron_net_id = ( - share_net_info['neutron_net_id'] - if 'none' not in share_net_info['neutron_net_id'].lower() - else None - ) - neutron_subnet_id = ( - share_net_info['neutron_subnet_id'] - if 'none' not in share_net_info['neutron_subnet_id'].lower() - else None - ) - share_network = self.client.create_share_network( - neutron_net_id=neutron_net_id, - neutron_subnet_id=neutron_subnet_id, - ) - share_type = self.create_share_type( - data_utils.rand_name('test_share_type'), - driver_handles_share_servers=True, - ) - - share = self.create_share( - share_protocol=self.protocol, - size=1, - name=name, - description=description, - share_type=share_type['ID'], - share_network=share_network['id'], - client=self.client, - wait_for_creation=True, - ) - share = self.client.get_share(share['id']) - return share, share_network - - @ddt.data('cancel', 'complete') - def test_share_server_migration(self, operation): - # Create a share and share network to be used in the tests. - share, share_network = self._create_share_and_share_network() - share_server_id = share['share_server_id'] - src_host = share['host'].split('#')[0] - pools = self.admin_client.pool_list(detail=True) - - host_list = list() - # Filter the backends DHSS True and different - # than the source host. - for hosts in pools: - host_name = hosts['Name'].split('#')[0] - if ( - ast.literal_eval(hosts['Capabilities']).get( - 'driver_handles_share_servers' - ) - and host_name != src_host - ): - host_list.append(host_name) - - host_list = list(set(host_list)) - # If not found any host we need skip the test. - if len(host_list) == 0: - raise self.skipException( - "No hosts available for share server migration." - ) - - dest_backend = None - # If found at least one host, we still need to verify the - # share server migration compatibility with the destination host. - for host in host_list: - compatibility = self.admin_client.share_server_migration_check( - server_id=share_server_id, - dest_host=host, - writable=False, - nondisruptive=False, - preserve_snapshots=False, - new_share_network=None, - ) - # If found at least one compatible host, we will use it. - if compatibility['compatible']: - dest_host = host - # If not found, we need skip the test. - if dest_backend is not None: - raise self.skipException( - "No hosts compatible to perform a share server migration." - ) - - # Start the share server migration - self.admin_client.share_server_migration_start( - share_server_id, dest_host - ) - - server = self.admin_client.get_share_server(share_server_id) - share = self.admin_client.get_share(share['id']) - self.assertEqual(constants.STATUS_SERVER_MIGRATING, share['status']) - - # Wait for the share server migration driver phase 1 done. - task_state = constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE - server = self.admin_client.wait_for_server_migration_task_state( - share_server_id, dest_host, task_state - ) - migration_progress = ( - self.admin_client.share_server_migration_get_progress( - share_server_id - ) - ) - dest_share_server_id = migration_progress.get( - 'destination_share_server_id' - ) - - # Call share server migration complete or cancel operations - # according the ddt. - if operation == 'complete': - task_state = constants.TASK_STATE_MIGRATION_SUCCESS - self.admin_client.share_server_migration_complete(share_server_id) - server = self.admin_client.wait_for_server_migration_task_state( - dest_share_server_id, dest_host, task_state - ) - - self.admin_client.wait_for_share_server_deletion(share_server_id) - else: - self.admin_client.share_server_migration_cancel(server['id']) - task_state = constants.TASK_STATE_MIGRATION_CANCELLED - # Wait for the respectives task state for each operation above. - server = self.admin_client.wait_for_server_migration_task_state( - server['id'], dest_host, task_state - ) - - # Check if the share is available again. - share = self.admin_client.get_share(share['id']) - self.assertEqual('available', share['status']) - - -class ShareServersMigrationNFSTest(ShareServersMigrationBase): - protocol = 'nfs' - - -class ShareServersMigrationCIFSTest(ShareServersMigrationBase): - protocol = 'cifs' - - -def load_tests(loader, tests, _): - result = [] - for test_case in tests: - if type(test_case._tests[0]) is ShareServersReadWriteBase: - continue - if type(test_case._tests[0]) is ShareServersMigrationBase: - continue - result.append(test_case) - return loader.suiteClass(result) diff --git a/manilaclient/tests/functional/test_share_transfers.py b/manilaclient/tests/functional/test_share_transfers.py deleted file mode 100644 index 75ca44200..000000000 --- a/manilaclient/tests/functional/test_share_transfers.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (c) 2022 China Telecom Digital Intelligence. -# 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 tempest.lib.common.utils import data_utils - -from manilaclient.tests.functional import base - - -class ShareTransferTests(base.BaseTestCase): - """Check of base share transfers command""" - - def setUp(self): - super().setUp() - self.share_type = self.create_share_type( - name=data_utils.rand_name('test_share_type'), - driver_handles_share_servers=False, - ) - - def test_transfer_create_list_show_delete(self): - """Create, list, show and delete a share transfer""" - self.skip_if_microversion_not_supported('2.77') - share = self.create_share( - share_protocol='nfs', - size=1, - name=data_utils.rand_name('autotest_share_name'), - client=self.user_client, - share_type=self.share_type['ID'], - use_wait_option=True, - ) - self.assertEqual("available", share['status']) - # create share transfer - transfer = self.create_share_transfer( - share['id'], name='test_share_transfer' - ) - self.assertIn('auth_key', transfer) - - # list share transfers - transfers = self.list_share_transfer() - # We must have at least one transfer - self.assertTrue(len(transfers) > 0) - - # show the share transfer - transfer_show = self.get_share_transfer(transfer['id']) - self.assertEqual(transfer_show['name'], transfer['name']) - self.assertNotIn('auth_key', transfer_show) - - # delete share transfer - self.delete_share_transfer(transfer['id']) - self.user_client.wait_for_transfer_deletion(transfer['id']) - share = self.user_client.get_share(share['id']) - self.assertEqual("available", share['status']) - # finally delete the share - self.user_client.delete_share(share['id']) - self.user_client.wait_for_share_deletion(share['id']) - - def test_transfer_accept(self): - """Show share transfer accept""" - self.skip_if_microversion_not_supported('2.77') - share = self.create_share( - share_protocol='nfs', - size=1, - name=data_utils.rand_name('autotest_share_name'), - client=self.user_client, - share_type=self.share_type['ID'], - use_wait_option=True, - ) - self.assertEqual("available", share['status']) - # create share transfer - transfer = self.create_share_transfer( - share['id'], name='test_share_transfer' - ) - share = self.user_client.get_share(share['id']) - transfer_id = transfer['id'] - auth_key = transfer['auth_key'] - self.assertEqual("share", transfer['resource_type']) - self.assertEqual('test_share_transfer', transfer['name']) - self.assertEqual("awaiting_transfer", share['status']) - - # accept the share transfer - self.accept_share_transfer(transfer_id, auth_key) - # after accept complete, the transfer will be deleted. - self.user_client.wait_for_transfer_deletion(transfer_id) - share = self.user_client.get_share(share['id']) - # check share status roll back to available - self.assertEqual("available", share['status']) - # finally delete the share - self.user_client.delete_share(share['id']) - self.user_client.wait_for_share_deletion(share['id']) diff --git a/manilaclient/tests/functional/test_share_types.py b/manilaclient/tests/functional/test_share_types.py deleted file mode 100644 index eaa9f6687..000000000 --- a/manilaclient/tests/functional/test_share_types.py +++ /dev/null @@ -1,627 +0,0 @@ -# Copyright 2015 Mirantis Inc. -# 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. - -import ddt -from tempest.lib.common.utils import data_utils - -from manilaclient import api_versions -from manilaclient.tests.functional import base -from manilaclient.tests.unit.v2 import test_types as unit_test_types - - -@ddt.ddt -class ShareTypesReadOnlyTest(base.BaseTestCase): - @ddt.data( - ("admin", "1.0"), - ("admin", "2.0"), - ("admin", "2.6"), - ("admin", "2.7"), - ("user", "1.0"), - ("user", "2.0"), - ("user", "2.6"), - ("user", "2.7"), - ) - @ddt.unpack - def test_share_type_list(self, role, microversion): - self.skip_if_microversion_not_supported(microversion) - self.clients[role].manila("type-list", microversion=microversion) - - @ddt.data("1.0", "2.0", "2.6", "2.7") - def test_extra_specs_list(self, microversion): - self.skip_if_microversion_not_supported(microversion) - self.admin_client.manila("extra-specs-list", microversion=microversion) - - -@ddt.ddt -class ShareTypesReadWriteTest(base.BaseTestCase): - create_keys = ( - 'ID', - 'Name', - 'Visibility', - 'is_default', - 'required_extra_specs', - 'optional_extra_specs', - ) - - def _share_type_listed_by( - self, share_type_id, by_admin=False, list_all=False, microversion=None - ): - client = self.admin_client if by_admin else self.user_client - share_types = client.list_share_types( - list_all=list_all, microversion=microversion - ) - return any(share_type_id == st['ID'] for st in share_types) - - def _verify_access(self, share_type_id, is_public, microversion=None): - if is_public: - # Verify that it is listed with common 'type-list' operation. - share_types = self.admin_client.list_share_types( - list_all=False, microversion=microversion - ) - self.assertTrue( - any(share_type_id == st['ID'] for st in share_types) - ) - else: - # Verify that it is not listed for user - self.assertFalse( - self._share_type_listed_by( - share_type_id=share_type_id, - by_admin=False, - list_all=True, - microversion=microversion, - ) - ) - - # Verify it is listed for admin - self.assertTrue( - self._share_type_listed_by( - share_type_id=share_type_id, - by_admin=True, - list_all=True, - microversion=microversion, - ) - ) - - # Verify it is not listed by default - self.assertFalse( - self._share_type_listed_by( - share_type_id=share_type_id, - by_admin=True, - list_all=False, - microversion=microversion, - ) - ) - - @ddt.data(*unit_test_types.get_valid_type_create_data_2_0()) - @ddt.unpack - def test_create_delete_share_type( - self, is_public, dhss, spec_snapshot_support, extra_specs - ): - self.skip_if_microversion_not_supported('2.0') - self._test_create_delete_share_type( - '2.0', - is_public, - dhss, - spec_snapshot_support, - None, - None, - None, - extra_specs, - ) - - @ddt.data(*unit_test_types.get_valid_type_create_data_2_24()) - @ddt.unpack - def test_create_delete_share_type_2_24( - self, - is_public, - dhss, - spec_snapshot_support, - spec_create_share_from_snapshot, - extra_specs, - ): - self.skip_if_microversion_not_supported('2.24') - self._test_create_delete_share_type( - '2.24', - is_public, - dhss, - spec_snapshot_support, - spec_create_share_from_snapshot, - None, - None, - extra_specs, - ) - - @ddt.data(*unit_test_types.get_valid_type_create_data_2_27()) - @ddt.unpack - def test_create_delete_share_type_2_27( - self, - is_public, - dhss, - spec_snapshot_support, - spec_create_share_from_snapshot, - spec_revert_to_snapshot_support, - extra_specs, - ): - self.skip_if_microversion_not_supported('2.27') - self._test_create_delete_share_type( - '2.27', - is_public, - dhss, - spec_snapshot_support, - spec_create_share_from_snapshot, - spec_revert_to_snapshot_support, - None, - extra_specs, - ) - - def test_create_delete_share_type_with_description(self): - self.skip_if_microversion_not_supported('2.41') - self._test_create_delete_share_type( - '2.41', - True, - False, - None, - None, - None, - None, - None, - description=data_utils.rand_name('test_share_type_description'), - ) - - @ddt.data( - ('name_updated_1', 'description_updated', True), - ('name_updated_2', 'description_updated', False), - ('name_updated_3', None, None), - (None, 'description_updated', None), - (None, None, True), - (None, None, False), - ) - @ddt.unpack - def test_create_update_delete_share_type_2_50( - self, new_name, new_description, new_is_public - ): - self.skip_if_microversion_not_supported('2.50') - microversion = '2.50' - share_type_name = data_utils.rand_name('share_type_update_test') - - # Create share type - share_type = self.create_share_type( - name=share_type_name, - driver_handles_share_servers=False, - snapshot_support=None, - create_share_from_snapshot=None, - revert_to_snapshot=None, - mount_snapshot=None, - is_public=True, - microversion=microversion, - extra_specs={}, - description="share_type_description", - ) - - st_id = share_type['ID'] - - # Update share type - st_updated = self.update_share_type( - st_id, - name=new_name, - description=new_description, - is_public=new_is_public, - microversion=microversion, - ) - # Verify type name - if new_name: - self.assertEqual(new_name, st_updated['Name']) - - # Verify type description - if new_description: - self.assertEqual(new_description, st_updated['Description']) - - # Verify public - if new_is_public is not None: - self.assertEqual( - 'public' if new_is_public else 'private', - st_updated['Visibility'].lower(), - ) - - # Delete share type - self.admin_client.delete_share_type(st_id, microversion=microversion) - - # Wait for share type deletion - self.admin_client.wait_for_share_type_deletion( - st_id, microversion=microversion - ) - - # Verify that it is not listed with common 'type-list' operation. - share_types = self.admin_client.list_share_types( - list_all=False, microversion=microversion - ) - self.assertFalse(any(st_id == st['ID'] for st in share_types)) - - def test_unset_share_type_description_2_50(self): - self.skip_if_microversion_not_supported('2.50') - microversion = '2.50' - share_type_name = data_utils.rand_name('share_type_update_test') - - # Create share type - share_type = self.create_share_type( - name=share_type_name, - driver_handles_share_servers=False, - snapshot_support=None, - create_share_from_snapshot=None, - revert_to_snapshot=None, - mount_snapshot=None, - is_public=True, - microversion=microversion, - extra_specs={}, - description="share_type_description", - ) - - st_id = share_type['ID'] - - # Update share type - new_description = "" - st_updated = self.update_share_type( - st_id, description=new_description, microversion=microversion - ) - - # Verify type description - self.assertEqual('None', st_updated['Description']) - - # Delete share type - self.admin_client.delete_share_type(st_id, microversion=microversion) - - # Wait for share type deletion - self.admin_client.wait_for_share_type_deletion( - st_id, microversion=microversion - ) - - # Verify that it is not listed with common 'type-list' operation. - share_types = self.admin_client.list_share_types( - list_all=False, microversion=microversion - ) - self.assertFalse(any(st_id == st['ID'] for st in share_types)) - - def _test_create_delete_share_type( - self, - microversion, - is_public, - dhss, - spec_snapshot_support, - spec_create_share_from_snapshot, - spec_revert_to_snapshot_support, - spec_mount_snapshot_support, - extra_specs, - description=None, - ): - share_type_name = data_utils.rand_name('manilaclient_functional_test') - - if extra_specs is None: - extra_specs = {} - - # Create share type - share_type = self.create_share_type( - name=share_type_name, - driver_handles_share_servers=dhss, - snapshot_support=spec_snapshot_support, - create_share_from_snapshot=spec_create_share_from_snapshot, - revert_to_snapshot=spec_revert_to_snapshot_support, - mount_snapshot=spec_mount_snapshot_support, - is_public=is_public, - microversion=microversion, - extra_specs=extra_specs, - description=description, - ) - - # Verify response body - for key in self.create_keys: - self.assertIn(key, share_type) - - # Verify type name - self.assertEqual(share_type_name, share_type['Name']) - - # Verify type description - if api_versions.APIVersion(microversion) >= api_versions.APIVersion( - '2.41' - ): - self.assertEqual(description, share_type['Description']) - else: - self.assertNotIn('description', share_type) - - # Verify required DHSS extra spec - dhss_expected = f'driver_handles_share_servers : {dhss}' - self.assertEqual(dhss_expected, share_type['required_extra_specs']) - - # Determine expected extra specs. Note that prior to 2.24, - # the standard 'snapshot_support' extra spec was required. - expected_extra_specs = [] - for key, val in extra_specs.items(): - expected_extra_specs.append((f'{key} : {val}').strip()) - - if api_versions.APIVersion(microversion) < api_versions.APIVersion( - '2.24' - ): - if 'snapshot_support' not in extra_specs: - if spec_snapshot_support is None: - expected_extra_specs.append( - ('{} : {}'.format('snapshot_support', True)).strip() - ) - else: - expected_extra_specs.append( - ( - '{} : {}'.format( - 'snapshot_support', spec_snapshot_support - ) - ).strip() - ) - else: - if spec_snapshot_support is not None: - expected_extra_specs.append( - ( - '{} : {}'.format( - 'snapshot_support', spec_snapshot_support - ) - ).strip() - ) - - if spec_create_share_from_snapshot is not None: - expected_extra_specs.append( - ( - '{} : {}'.format( - 'create_share_from_snapshot_support', - spec_create_share_from_snapshot, - ) - ).strip() - ) - if spec_revert_to_snapshot_support is not None: - expected_extra_specs.append( - ( - '{} : {}'.format( - 'revert_to_snapshot_support', - spec_revert_to_snapshot_support, - ) - ).strip() - ) - if spec_mount_snapshot_support is not None: - expected_extra_specs.append( - ( - '{} : {}'.format( - 'mount_snapshot_support', spec_mount_snapshot_support - ) - ).strip() - ) - - # Verify optional extra specs - optional_extra_specs = share_type['optional_extra_specs'] - if optional_extra_specs == '': - optional_extra_specs = [] - elif not isinstance(optional_extra_specs, list): - optional_extra_specs = [optional_extra_specs] - - self.assertEqual(len(expected_extra_specs), len(optional_extra_specs)) - for e in optional_extra_specs: - self.assertIn(e.strip(), expected_extra_specs) - - # Verify public & default attributes - self.assertEqual( - 'public' if is_public else 'private', - share_type['Visibility'].lower(), - ) - self.assertEqual('-', share_type['is_default']) - - # Verify its access - st_id = share_type['ID'] - self._verify_access( - share_type_id=st_id, is_public=is_public, microversion=microversion - ) - - # Delete share type - self.admin_client.delete_share_type(st_id, microversion=microversion) - - # Wait for share type deletion - self.admin_client.wait_for_share_type_deletion( - st_id, microversion=microversion - ) - - # Verify that it is not listed with common 'type-list' operation. - share_types = self.admin_client.list_share_types( - list_all=False, microversion=microversion - ) - self.assertFalse(any(st_id == st['ID'] for st in share_types)) - - @ddt.data("2.6", "2.7") - def test_add_remove_access_to_private_share_type(self, microversion): - self.skip_if_microversion_not_supported(microversion) - - share_type_name = data_utils.rand_name('manilaclient_functional_test') - is_public = False - - # Create share type - share_type = self.create_share_type( - name=share_type_name, - driver_handles_share_servers='False', - is_public=is_public, - microversion=microversion, - ) - - st_id = share_type['ID'] - user_project_id = self.admin_client.get_project_id( - self.user_client.tenant_name - ) - - self._verify_access( - share_type_id=st_id, - is_public=is_public, - microversion=microversion, - ) - - # Project ID is in access list - false - st_access_list = self.admin_client.list_share_type_access( - st_id, microversion=microversion - ) - self.assertNotIn(user_project_id, st_access_list) - - # Add access for project of user - self.admin_client.add_share_type_access( - st_id, user_project_id, microversion=microversion - ) - - # Verify it is listed for user as well as for admin - self.assertTrue( - self._share_type_listed_by( - share_type_id=st_id, by_admin=False, list_all=True - ) - ) - self.assertTrue( - self._share_type_listed_by( - share_type_id=st_id, by_admin=True, list_all=True - ) - ) - - # Project ID is in access list - true - st_access_list = self.admin_client.list_share_type_access( - st_id, microversion=microversion - ) - self.assertIn(user_project_id, st_access_list) - - # Remove access - self.admin_client.remove_share_type_access( - st_id, user_project_id, microversion=microversion - ) - - self._verify_access( - share_type_id=st_id, - is_public=is_public, - microversion=microversion, - ) - - # Project ID is in access list - false - st_access_list = self.admin_client.list_share_type_access( - st_id, microversion=microversion - ) - self.assertNotIn(user_project_id, st_access_list) - - @ddt.data("2.6", "2.7") - def test_list_share_type(self, microversion): - share_type_name = data_utils.rand_name('manilaclient_functional_test') - - # Create share type - self.create_share_type( - name=share_type_name, driver_handles_share_servers='False' - ) - share_types = self.admin_client.list_share_types( - list_all=True, microversion=microversion - ) - self.assertTrue(any(s['ID'] is not None for s in share_types)) - self.assertTrue(any(s['Name'] is not None for s in share_types)) - self.assertTrue(any(s['visibility'] is not None for s in share_types)) - - @ddt.data("2.6", "2.7") - def test_list_share_type_select_column(self, microversion): - share_type_name = data_utils.rand_name('manilaclient_functional_test') - - # Create share type - self.create_share_type( - name=share_type_name, driver_handles_share_servers='False' - ) - share_types = self.admin_client.list_share_types( - list_all=True, columns="id,name", microversion=microversion - ) - self.assertTrue(any(s['id'] is not None for s in share_types)) - self.assertTrue(any(s['name'] is not None for s in share_types)) - self.assertTrue(all('visibility' not in s for s in share_types)) - self.assertTrue(all('Visibility' not in s for s in share_types)) - - def test_list_share_type_filter_search(self): - # Fake extra spec and type name - extra_specs = {'aaaa': 'bbbb'} - # Create share type - name1 = data_utils.rand_name('manilaclient_functional_test1') - self.create_share_type( - name=name1, driver_handles_share_servers='False' - ) - # Create share type - name2 = data_utils.rand_name('manilaclient_functional_test2') - self.create_share_type( - name=name2, - extra_specs=extra_specs, - driver_handles_share_servers='True', - ) - - # List type by extra_specs - list_all = False - search_opts = {'extra_specs': extra_specs} - share_types = self.admin_client.list_share_types( - list_all=list_all, search_opts=search_opts, microversion='2.43' - ) - self.assertTrue(share_types is not None) - - expect = 'aaaa : bbbb' - self.assertTrue(len(share_types) == 1) - self.assertTrue(all('optional_extra_specs' in s for s in share_types)) - self.assertTrue(all(s['Name'] == name2 for s in share_types)) - self.assertTrue( - all(s['optional_extra_specs'] == expect for s in share_types) - ) - - -@ddt.ddt -class ShareTypeExtraSpecsReadWriteTest(base.BaseTestCase): - @ddt.data( - (True, False), - (True, True), - (False, True), - (False, False), - (False, False, "2.6"), - (False, False, "2.7"), - ) - @ddt.unpack - def test_share_type_extra_specs_life_cycle( - self, is_public, dhss, microversion=None - ): - if microversion: - self.skip_if_microversion_not_supported(microversion) - - # Create share type - st = self.create_share_type( - driver_handles_share_servers=dhss, - is_public=is_public, - microversion=microversion, - ) - - # Add extra specs to share type - st_extra_specs = dict(foo_key='foo_value', bar_key='bar_value') - self.admin_client.set_share_type_extra_specs( - st['ID'], st_extra_specs, microversion=microversion - ) - - # View list of extra specs - extra_specs = self.admin_client.list_share_type_extra_specs( - st['ID'], microversion=microversion - ) - for k, v in st_extra_specs.items(): - self.assertIn(f'{k} : {v}', extra_specs) - - # Remove one extra spec - self.admin_client.unset_share_type_extra_specs( - st['ID'], ('foo_key',), microversion=microversion - ) - - # Verify that removed extra spec is absent - extra_specs = self.admin_client.list_share_type_extra_specs( - st['ID'], microversion=microversion - ) - self.assertNotIn('foo_key : foo_value', extra_specs) - self.assertIn('bar_key : bar_value', extra_specs) - self.assertIn(f'driver_handles_share_servers : {dhss}', extra_specs) diff --git a/manilaclient/tests/functional/test_shares.py b/manilaclient/tests/functional/test_shares.py deleted file mode 100644 index 7b5896127..000000000 --- a/manilaclient/tests/functional/test_shares.py +++ /dev/null @@ -1,339 +0,0 @@ -# Copyright 2015 Mirantis Inc. -# 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. - -import ddt -import testtools - -from tempest.lib.common.utils import data_utils -from tempest.lib import exceptions - -from manilaclient.common import constants -from manilaclient import config -from manilaclient.tests.functional import base -from manilaclient.tests.functional import utils - -CONF = config.CONF - - -@ddt.ddt -class SharesReadWriteBase(base.BaseTestCase): - protocol = None - - def setUp(self): - super().setUp() - if self.protocol not in CONF.enable_protocols: - message = f"{self.protocol} tests are disabled" - raise self.skipException(message) - self.name = data_utils.rand_name('autotest_share_name') - self.description = data_utils.rand_name('autotest_share_description') - - # NOTE(vponomaryov): following share is used only in one test - # until tests for snapshots appear. - self.share = self.create_share( - share_protocol=self.protocol, - size=1, - name=self.name, - description=self.description, - client=self.get_user_client(), - ) - - def test_create_delete_share(self): - name = data_utils.rand_name('autotest_share_name') - - create = self.create_share( - self.protocol, name=name, client=self.user_client - ) - - self.assertEqual("creating", create['status']) - self.assertEqual(name, create['name']) - self.assertEqual('1', create['size']) - self.assertEqual(self.protocol.upper(), create['share_proto']) - - self.user_client.delete_share(create['id']) - - self.user_client.wait_for_share_deletion(create['id']) - - def test_create_update_share(self): - name = data_utils.rand_name('autotest_share_name') - new_name = 'new_' + name - description = data_utils.rand_name('autotest_share_description') - new_description = 'new_' + description - - create = self.create_share( - self.protocol, - name=name, - description=description, - client=self.user_client, - ) - - self.assertEqual(name, create['name']) - self.assertEqual(description, create['description']) - self.assertEqual('False', create['is_public']) - - self.user_client.update_share( - create['id'], name=new_name, description=new_description - ) - get = self.user_client.get_share(create['id']) - - self.assertEqual(new_name, get['name']) - self.assertEqual(new_description, get['description']) - self.assertEqual('False', get['is_public']) - - # only admins operating at system scope can set a share to be public - self.admin_client.update_share(create['id'], is_public=True) - get = self.user_client.get_share(create['id']) - - self.assertEqual(new_name, get['name']) - self.assertEqual(new_description, get['description']) - self.assertEqual('True', get['is_public']) - - def test_get_share(self): - get = self.user_client.get_share(self.share['id']) - - self.assertEqual(self.name, get['name']) - self.assertEqual(self.description, get['description']) - self.assertEqual('1', get['size']) - self.assertEqual(self.protocol.upper(), get['share_proto']) - - def test_create_delete_with_wait(self): - name = data_utils.rand_name('share-with-wait-%s') - description = data_utils.rand_name('we-wait-until-share-is-ready') - - share_1, share_2 = ( - self.create_share( - self.protocol, - name=(name % num), - description=description, - use_wait_option=True, - client=self.user_client, - ) - for num in range(0, 2) - ) - - self.assertEqual("available", share_1['status']) - self.assertEqual("available", share_2['status']) - - self.delete_share( - [share_1['id'], share_2['id']], wait=True, client=self.user_client - ) - - for share in (share_1, share_2): - self.assertRaises( - exceptions.NotFound, self.user_client.get_share, share['id'] - ) - - def test_create_soft_delete_and_restore_share(self): - self.skip_if_microversion_not_supported('2.69') - microversion = '2.69' - description = data_utils.rand_name('we-wait-until-share-is-ready') - - share = self.create_share( - self.protocol, - name='share_name', - description=description, - use_wait_option=True, - client=self.user_client, - ) - - self.assertEqual("available", share['status']) - - # soft delete the share to recycle bin - self.soft_delete_share( - [share['id']], client=self.user_client, microversion=microversion - ) - self.user_client.wait_for_share_soft_deletion(share['id']) - - # get shares list in recycle bin - result = self.user_client.list_shares( - is_soft_deleted=True, microversion=microversion - ) - share_ids = [sh['ID'] for sh in result] - # check share is in recycle bin - self.assertIn(share['id'], share_ids) - - # restore the share from recycle bin - self.restore_share( - [share['id']], client=self.user_client, microversion=microversion - ) - self.user_client.wait_for_share_restore(share['id']) - - result1 = self.user_client.list_shares( - is_soft_deleted=True, microversion=microversion - ) - share_ids1 = [sh['ID'] for sh in result1] - # check share not in recycle bin - self.assertNotIn(share['id'], share_ids1) - - -@ddt.ddt -class SharesTestMigration(base.BaseTestCase): - def setUp(self): - super().setUp() - - self.old_type = self.create_share_type( - data_utils.rand_name('test_share_type'), - driver_handles_share_servers=True, - ) - self.new_type = self.create_share_type( - data_utils.rand_name('test_share_type'), - driver_handles_share_servers=True, - ) - self.error_type = self.create_share_type( - data_utils.rand_name('test_share_type'), - driver_handles_share_servers=True, - extra_specs={'cause_error': 'no_valid_host'}, - ) - - self.old_share_net = self.get_user_client().get_share_network( - self.get_user_client().share_network - ) - share_net_info = ( - utils.get_default_subnet( - self.get_user_client(), self.old_share_net['id'] - ) - if utils.share_network_subnets_are_supported() - else self.old_share_net - ) - self.new_share_net = self.create_share_network( - neutron_net_id=share_net_info['neutron_net_id'], - neutron_subnet_id=share_net_info['neutron_subnet_id'], - ) - - @utils.skip_if_microversion_not_supported('2.22') - @ddt.data('migration_error', 'migration_success', 'None') - def test_reset_task_state(self, state): - share = self.create_share( - share_protocol='nfs', - size=1, - name=data_utils.rand_name('autotest_share_name'), - client=self.get_user_client(), - share_type=self.old_type['ID'], - share_network=self.old_share_net['id'], - wait_for_creation=True, - ) - share = self.user_client.get_share(share['id']) - - self.admin_client.reset_task_state(share['id'], state) - share = self.user_client.get_share(share['id']) - self.assertEqual(state, share['task_state']) - - @utils.skip_if_microversion_not_supported('2.29') - @testtools.skipUnless( - CONF.run_migration_tests, 'Share migration tests are disabled.' - ) - @ddt.data('cancel', 'success', 'error') - def test_full_migration(self, test_type): - # We are testing with DHSS=True only because it allows us to specify - # new_share_network. - - share = self.create_share( - share_protocol='nfs', - size=1, - name=data_utils.rand_name('autotest_share_name'), - client=self.get_user_client(), - share_type=self.old_type['ID'], - share_network=self.old_share_net['id'], - wait_for_creation=True, - ) - share = self.admin_client.get_share(share['id']) - - pools = self.admin_client.pool_list(detail=True) - - dest_pool = utils.choose_matching_backend(share, pools, self.new_type) - - self.assertIsNotNone(dest_pool) - - source_pool = share['host'] - - new_type = self.new_type - if test_type == 'error': - statuses = constants.TASK_STATE_MIGRATION_ERROR - new_type = self.error_type - else: - statuses = ( - constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE, - constants.TASK_STATE_DATA_COPYING_COMPLETED, - ) - - self.admin_client.migration_start( - share['id'], - dest_pool, - writable=True, - nondisruptive=False, - preserve_metadata=True, - preserve_snapshots=True, - force_host_assisted_migration=False, - new_share_network=self.new_share_net['id'], - new_share_type=new_type['ID'], - ) - - share = self.admin_client.wait_for_migration_task_state( - share['id'], dest_pool, statuses - ) - - progress = self.admin_client.migration_get_progress(share['id']) - self.assertEqual('100', progress['total_progress']) - - self.assertEqual(source_pool, share['host']) - self.assertEqual(self.old_type['ID'], share['share_type']) - self.assertEqual(self.old_share_net['id'], share['share_network_id']) - - if test_type == 'error': - self.assertEqual(statuses, progress['task_state']) - else: - if test_type == 'success': - self.admin_client.migration_complete(share['id']) - statuses = constants.TASK_STATE_MIGRATION_SUCCESS - elif test_type == 'cancel': - self.admin_client.migration_cancel(share['id']) - statuses = constants.TASK_STATE_MIGRATION_CANCELLED - - share = self.admin_client.wait_for_migration_task_state( - share['id'], dest_pool, statuses - ) - progress = self.admin_client.migration_get_progress(share['id']) - self.assertEqual(statuses, progress['task_state']) - if test_type == 'success': - self.assertEqual(dest_pool, share['host']) - self.assertEqual(new_type['ID'], share['share_type']) - self.assertEqual( - self.new_share_net['id'], share['share_network_id'] - ) - else: - self.assertEqual(source_pool, share['host']) - self.assertEqual(self.old_type['ID'], share['share_type']) - self.assertEqual( - self.old_share_net['id'], share['share_network_id'] - ) - - -class NFSSharesReadWriteTest(SharesReadWriteBase): - protocol = 'nfs' - - -class CIFSSharesReadWriteTest(SharesReadWriteBase): - protocol = 'cifs' - - -class GlusterFSSharesReadWriteTest(SharesReadWriteBase): - protocol = 'glusterfs' - - -class HDFSSharesReadWriteTest(SharesReadWriteBase): - protocol = 'hdfs' - - -class MAPRFSSharesReadWriteTest(SharesReadWriteBase): - protocol = 'maprfs' diff --git a/manilaclient/tests/functional/test_shares_listing.py b/manilaclient/tests/functional/test_shares_listing.py deleted file mode 100644 index 12f973131..000000000 --- a/manilaclient/tests/functional/test_shares_listing.py +++ /dev/null @@ -1,414 +0,0 @@ -# Copyright 2015 Mirantis Inc. -# 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. - -import ddt -from tempest.lib.common.utils import data_utils -from tempest.lib import exceptions -import testtools - -from manilaclient.common import constants -from manilaclient import config -from manilaclient.tests.functional import base - -CONF = config.CONF - - -@ddt.ddt -class SharesListReadOnlyTest(base.BaseTestCase): - @ddt.data('admin', 'user') - def test_shares_list(self, role): - self.clients[role].manila('list') - - @ddt.data('admin', 'user') - def test_list_with_debug_flag(self, role): - self.clients[role].manila('list', flags='--debug') - - @ddt.data('admin', 'user') - def test_shares_list_all_tenants(self, role): - self.clients[role].manila('list', params='--all-tenants') - - @ddt.data('admin', 'user') - def test_shares_list_filter_by_name(self, role): - self.clients[role].manila('list', params='--name name') - - @ddt.data('admin', 'user') - def test_shares_list_filter_by_export_location(self, role): - self.clients[role].manila('list', params='--export_location fake') - - @ddt.data('admin', 'user') - def test_shares_list_filter_by_inexact_name(self, role): - self.clients[role].manila('list', params='--name~ na') - - @ddt.data('admin', 'user') - def test_shares_list_filter_by_inexact_description(self, role): - self.clients[role].manila('list', params='--description~ des') - - @ddt.data('admin', 'user') - def test_shares_list_filter_by_status(self, role): - self.clients[role].manila('list', params='--status status') - - def test_shares_list_filter_by_share_server_as_admin(self): - self.clients['admin'].manila('list', params='--share-server fake') - - def test_shares_list_filter_by_share_server_as_user(self): - self.assertRaises( - exceptions.CommandFailed, - self.clients['user'].manila, - 'list', - params='--share-server fake', - ) - - @ddt.data('admin', 'user') - def test_shares_list_filter_by_project_id(self, role): - self.clients[role].manila('list', params='--project-id fake') - - def test_shares_list_filter_by_host(self): - self.clients['admin'].manila('list', params='--host fake') - - @ddt.data('admin', 'user') - def test_shares_list_with_limit_and_offset(self, role): - self.clients[role].manila('list', params='--limit 1 --offset 1') - - @ddt.data( - {'role': 'admin', 'direction': 'asc'}, - {'role': 'admin', 'direction': 'desc'}, - {'role': 'user', 'direction': 'asc'}, - {'role': 'user', 'direction': 'desc'}, - ) - @ddt.unpack - def test_shares_list_with_sorting(self, role, direction): - self.clients[role].manila( - 'list', params='--sort-key host --sort-dir ' + direction - ) - - @ddt.data('admin', 'user') - def test_snapshot_list(self, role): - self.clients[role].manila('snapshot-list') - - @ddt.data('admin', 'user') - def test_snapshot_list_all_tenants(self, role): - self.clients[role].manila('snapshot-list', params='--all-tenants') - - @ddt.data('admin', 'user') - def test_snapshot_list_filter_by_name(self, role): - self.clients[role].manila('snapshot-list', params='--name name') - - @ddt.data('admin', 'user') - def test_snapshot_list_filter_by_status(self, role): - self.clients[role].manila('snapshot-list', params='--status status') - - -@ddt.ddt -class SharesListReadWriteTest(base.BaseTestCase): - def setUp(self): - super().setUp() - self.private_name = data_utils.rand_name('autotest_share_name') - self.private_description = data_utils.rand_name( - 'autotest_share_description' - ) - self.public_name = data_utils.rand_name('autotest_public_share_name') - self.public_description = data_utils.rand_name( - 'autotest_public_share_description' - ) - - self.admin_private_name = data_utils.rand_name( - 'autotest_admin_private_share_name' - ) - self.admin_private_description = data_utils.rand_name( - 'autotest_admin_private_share_description' - ) - - self.soft_name = data_utils.rand_name('soft_delete_share_name') - - self.admin_private_share = self.create_share( - name=self.admin_private_name, - description=self.admin_private_description, - public=False, - client=None, - wait_for_creation=False, - ) - - self.private_share = self.create_share( - name=self.private_name, - description=self.private_description, - public=False, - client=self.get_user_client(), - wait_for_creation=False, - ) - - self.public_share = self.create_share( - name=self.public_name, - description=self.public_description, - public=True, - client=self.admin_client, - ) - - self.wait_soft_delete_share = self.create_share( - name=self.soft_name, - public=False, - client=self.get_user_client(), - wait_for_creation=False, - ) - - self.shares_created = ( - self.private_share['id'], - self.public_share['id'], - self.admin_private_share['id'], - self.wait_soft_delete_share['id'], - ) - - for share_id in self.shares_created: - self.admin_client.wait_for_resource_status( - share_id, constants.STATUS_AVAILABLE - ) - - self.soft_delete_share( - [self.wait_soft_delete_share['id']], - client=self.get_user_client(), - microversion='2.69', - ) - - def _list_shares(self, filters=None): - filters = filters or dict() - shares = self.user_client.list_shares(filters=filters) - - self.assertGreater(len(shares), 0) - if filters: - for share in shares: - try: - share_get = self.user_client.get_share(share['ID']) - except exceptions.NotFound: - # NOTE(vponomaryov): Case when some share was deleted - # between our 'list' and 'get' requests. Skip such case. - # It occurs with concurrently running tests. - continue - if 'migrating' in share_get['status']: - # all bets are off, a fair chance share migration - # started between the 'list' and 'get' requests. No need - # to verify these shares. - continue - for filter_key, expected_value in filters.items(): - if filter_key in ('share_network', 'share-network'): - filter_key = 'share_network_id' - if share_get[filter_key] != expected_value: - # Possibly a mismatch because the share was - # migrated to a new network in the time that - # elapsed between the 'list' and 'get' requests. - # If this isn't one of the shares created in - # this class, don't worry about such mismatches - self.assertNotIn( - share_get['id'], self.shares_created - ) - continue - - if ( - expected_value != 'deleting' - and share_get[filter_key] == 'deleting' - ): - continue - self.assertEqual(expected_value, share_get[filter_key]) - - def test_list_shares(self): - self._list_shares() - - @ddt.data(1, 0) - def test_list_shares_for_all_tenants(self, all_tenants): - shares = self.admin_client.list_shares(all_tenants=all_tenants) - self.assertLessEqual(1, len(shares)) - - if all_tenants: - self.assertTrue(all('Project ID' in s for s in shares)) - for s_id in ( - self.private_share['id'], - self.public_share['id'], - self.admin_private_share['id'], - ): - self.assertTrue(any(s_id == s['ID'] for s in shares)) - else: - self.assertTrue(all('Project ID' not in s for s in shares)) - self.assertTrue( - any(self.admin_private_share['id'] == s['ID'] for s in shares) - ) - if ( - self.private_share['project_id'] - != (self.admin_private_share['project_id']) - ): - for s_id in ( - self.private_share['id'], - self.public_share['id'], - ): - self.assertFalse(any(s_id == s['ID'] for s in shares)) - - @ddt.data(True, False) - def test_list_shares_with_public(self, public): - shares = self.user_client.list_shares(is_public=public) - self.assertGreater(len(shares), 1) - if public: - self.assertTrue(all('Project ID' in s for s in shares)) - else: - self.assertTrue(all('Project ID' not in s for s in shares)) - - def test_list_shares_by_name(self): - shares = self.user_client.list_shares( - filters={'name': self.private_name} - ) - - self.assertEqual(1, len(shares)) - self.assertTrue( - any(self.private_share['id'] == s['ID'] for s in shares) - ) - for share in shares: - get = self.user_client.get_share(share['ID']) - self.assertEqual(self.private_name, get['name']) - - def test_list_shares_by_share_type(self): - share_type_id = self.user_client.get_share_type( - self.private_share['share_type'] - )['ID'] - # NOTE(vponomaryov): this is API 2.6+ specific - self._list_shares({'share_type': share_type_id}) - - def test_list_shares_by_status(self): - self._list_shares({'status': 'available'}) - - def test_list_shares_by_project_id(self): - project_id = self.user_client.get_project_id( - self.user_client.tenant_name - ) - self._list_shares({'project_id': project_id}) - - @testtools.skipUnless( - CONF.share_network, "Usage of Share networks is disabled" - ) - def test_list_shares_by_share_network(self): - share_network_id = self.user_client.get_share_network( - CONF.share_network - )['id'] - self._list_shares({'share_network': share_network_id}) - - @ddt.data( - {'limit': 1}, - {'limit': 2}, - {'limit': 1, 'offset': 1}, - {'limit': 2, 'offset': 0}, - ) - def test_list_shares_with_limit(self, filters): - shares = self.user_client.list_shares(filters=filters) - self.assertEqual(filters['limit'], len(shares)) - - def test_list_share_select_column(self): - shares = self.user_client.list_shares(columns="Name,Size") - self.assertTrue(any(s['Name'] is not None for s in shares)) - self.assertTrue(any(s['Size'] is not None for s in shares)) - self.assertTrue(all('Description' not in s for s in shares)) - - @ddt.data('ID', 'Path') - def test_list_shares_by_export_location(self, option): - export_locations = self.admin_client.list_share_export_locations( - self.public_share['id'] - ) - shares = self.admin_client.list_shares( - filters={'export_location': export_locations[0][option]} - ) - - self.assertEqual(1, len(shares)) - self.assertTrue( - any(self.public_share['id'] == s['ID'] for s in shares) - ) - for share in shares: - get = self.admin_client.get_share(share['ID']) - self.assertEqual(self.public_name, get['name']) - - @ddt.data('ID', 'Path') - def test_list_share_instances_by_export_location(self, option): - export_locations = self.admin_client.list_share_export_locations( - self.public_share['id'] - ) - share_instances = self.admin_client.list_share_instances( - filters={'export_location': export_locations[0][option]} - ) - - self.assertEqual(1, len(share_instances)) - - share_instance_id = share_instances[0]['ID'] - except_export_locations = ( - self.admin_client.list_share_instance_export_locations( - share_instance_id - ) - ) - self.assertGreater(len(except_export_locations), 0) - self.assertTrue( - any( - export_locations[0][option] == e[option] - for e in except_export_locations - ) - ) - - def test_list_share_by_export_location_with_invalid_version(self): - self.assertRaises( - exceptions.CommandFailed, - self.admin_client.list_shares, - filters={'export_location': 'fake'}, - microversion='2.34', - ) - - def test_list_share_instance_by_export_location_invalid_version(self): - self.assertRaises( - exceptions.CommandFailed, - self.admin_client.list_share_instances, - filters={'export_location': 'fake'}, - microversion='2.34', - ) - - @ddt.data('name', 'description') - def test_list_shares_by_inexact_option(self, option): - shares = self.user_client.list_shares(filters={option + '~': option}) - - # We know we have to have atleast three shares. - # Due to test concurrency, there can be - # more than three shares (some created by other tests). - self.assertGreaterEqual(len(shares), 3) - self.assertTrue( - any(self.private_share['id'] == s['ID'] for s in shares) - ) - - def test_list_shares_by_inexact_unicode_option(self): - self.create_share( - name='共享名称', description='共享描述', client=self.user_client - ) - filters = {'name~': '名称'} - shares = self.user_client.list_shares(filters=filters) - self.assertGreater(len(shares), 0) - - filters = {'description~': '描述'} - shares = self.user_client.list_shares(filters=filters) - self.assertGreater(len(shares), 0) - - def test_list_shares_by_description(self): - shares = self.user_client.list_shares( - filters={'description': self.private_description} - ) - - self.assertEqual(1, len(shares)) - self.assertTrue( - any(self.private_share['id'] == s['ID'] for s in shares) - ) - - def test_list_shares_in_recycle_bin(self): - shares = self.user_client.list_shares(is_soft_deleted=True) - - self.assertTrue( - any(self.wait_soft_delete_share['id'] == s['ID'] for s in shares) - ) diff --git a/manilaclient/tests/functional/test_shares_metadata.py b/manilaclient/tests/functional/test_shares_metadata.py deleted file mode 100644 index 5d642d0a3..000000000 --- a/manilaclient/tests/functional/test_shares_metadata.py +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright 2015 Mirantis Inc. -# 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. - -import ddt - -from manilaclient.tests.functional import base - - -@ddt.ddt -class SharesMetadataReadWriteTest(base.BaseTestCase): - def setUp(self): - super().setUp() - self.share = self.create_share(client=self.get_user_client()) - - def test_set_metadata_in_share_creation(self): - md = {"key1": "value1", "key2": "value2"} - - # Create share with metadata - share = self.create_share(metadata=md, client=self.get_user_client()) - - # Read share metadata - metadata = self.user_client.get_share_metadata(share["id"]) - - # Verify share metadata - self.assertEqual(2, len(metadata)) - self.assertIn('key1', metadata) - self.assertIn('key2', metadata) - self.assertEqual(md['key1'], metadata['key1']) - self.assertEqual(md['key2'], metadata['key2']) - - def test_set_and_get_metadata(self): - # Create share - share = self.create_share( - cleanup_in_class=False, client=self.get_user_client() - ) - - # Set share metadata - md = {"key3": "value3", "key4": "value4"} - self.user_client.set_share_metadata(share["id"], md) - - # Read share metadata - metadata = self.user_client.get_share_metadata(share["id"]) - - # Verify share metadata - self.assertEqual(2, len(metadata)) - self.assertIn('key3', metadata) - self.assertIn('key4', metadata) - self.assertEqual(md['key3'], metadata['key3']) - self.assertEqual(md['key4'], metadata['key4']) - - def test_set_and_delete_metadata(self): - # Create share - share = self.create_share( - cleanup_in_class=False, client=self.get_user_client() - ) - - # Set share metadata - md = {"key3": "value3", "key4": "value4"} - self.user_client.set_share_metadata(share["id"], md) - - # Unset share metadata - self.user_client.unset_share_metadata(share["id"], list(md.keys())) - - # Verify deletion of share metadata - metadata = self.user_client.get_share_metadata(share["id"]) - self.assertEqual({}, metadata) - - def test_set_and_add_metadata(self): - md = {'key5': 'value5'} - - # Create share with metadata - share = self.create_share( - metadata=md, cleanup_in_class=False, client=self.get_user_client() - ) - - # Set share metadata - self.user_client.set_share_metadata(share["id"], {'key6': 'value6'}) - self.user_client.set_share_metadata(share["id"], {'key7': 'value7'}) - - # Read share metadata - metadata = self.user_client.get_share_metadata(share["id"]) - - # Verify share metadata - self.assertEqual(3, len(metadata)) - for i in (5, 6, 7): - key = f'key{i}' - self.assertIn(key, metadata) - self.assertEqual(f'value{i}', metadata[key]) - - def test_set_and_replace_metadata(self): - md = {'key8': 'value8'} - - # Create share with metadata - share = self.create_share( - metadata=md, cleanup_in_class=False, client=self.get_user_client() - ) - - # Set share metadata - self.user_client.set_share_metadata(share["id"], {'key9': 'value9'}) - - # Replace all existing share metadata - self.user_client.update_all_share_metadata( - share["id"], {'key10': 'value10'} - ) - - # Read share metadata - metadata = self.user_client.get_share_metadata(share["id"]) - - # Verify share metadata - self.assertEqual(1, len(metadata)) - self.assertIn('key10', metadata) - self.assertEqual('value10', metadata['key10']) - - @ddt.data( - {"k": "value"}, {"k" * 255: "value"}, {"key": "v"}, {"key": "v" * 1023} - ) - def test_set_metadata_min_max_sizes_of_keys_and_values(self, metadata): - # Set share metadata - self.user_client.set_share_metadata(self.share["id"], metadata) - - # Read share metadata - get = self.user_client.get_share_metadata(self.share["id"]) - - # Verify share metadata - key = list(metadata.keys())[0] - self.assertIn(key, get) - self.assertEqual(metadata[key], get[key]) - - @ddt.data( - {"k": "value"}, {"k" * 255: "value"}, {"key": "v"}, {"key": "v" * 1023} - ) - def test_update_metadata_min_max_sizes_of_keys_and_values(self, metadata): - # Update share metadata - self.user_client.update_all_share_metadata(self.share["id"], metadata) - - # Read share metadata - get = self.user_client.get_share_metadata(self.share["id"]) - - # Verify share metadata - self.assertEqual(len(metadata), len(get)) - for key in metadata: - self.assertIn(key, get) - self.assertEqual(metadata[key], get[key]) diff --git a/manilaclient/tests/functional/test_snapshot_access.py b/manilaclient/tests/functional/test_snapshot_access.py deleted file mode 100644 index 817d00887..000000000 --- a/manilaclient/tests/functional/test_snapshot_access.py +++ /dev/null @@ -1,221 +0,0 @@ -# Copyright (c) 2017 Hitachi Data Systems -# 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 tempest.lib import exceptions as tempest_lib_exc -import testtools - -from manilaclient import config -from manilaclient.tests.functional import base -from manilaclient.tests.functional import utils - -CONF = config.CONF - - -@utils.skip_if_microversion_not_supported('2.32') -class SnapshotAccessReadBase(base.BaseTestCase): - protocol = None - - def setUp(self): - super().setUp() - if self.protocol not in CONF.enable_protocols: - message = f"{self.protocol} tests are disabled." - raise self.skipException(message) - self.access_types = CONF.access_types_mapping.get( - self.protocol, '' - ).split(' ') - if not self.access_types: - raise self.skipException( - f"No access types were provided for {self.protocol} " - "snapshot access tests." - ) - - self.share = self.create_share( - share_protocol=self.protocol, client=self.get_user_client() - ) - int_range = range(0, 10) - - self.access_to = { - 'ip': [f'99.88.77.{i}' for i in int_range], - 'user': [f'foo_user_{i}' for i in int_range], - 'cert': [f'tenant_{i}.example.com' for i in int_range], - } - - def _test_create_list_access_rule_for_snapshot(self, snapshot_id): - access = [] - access_type = self.access_types[0] - - for i in range(5): - access_ = self.user_client.snapshot_access_allow( - snapshot_id, access_type, self.access_to[access_type][i] - ) - access.append(access_) - - return access - - def test_create_list_access_rule_for_snapshot(self): - snapshot = self.create_snapshot( - share=self.share['id'], - client=self.get_user_client(), - cleanup_in_class=False, - ) - - access = self._test_create_list_access_rule_for_snapshot( - snapshot['id'] - ) - - access_list = self.user_client.list_access( - snapshot['id'], is_snapshot=True - ) - - for i in range(5): - self.assertIn( - access[i]['id'], [access_list[j]['id'] for j in range(5)] - ) - self.assertIn( - access[i]['access_type'], - [access_list[j]['access_type'] for j in range(5)], - ) - self.assertIn( - access[i]['access_to'], - [access_list[j]['access_to'] for j in range(5)], - ) - self.assertIsNotNone(access_list[i]['access_type']) - self.assertIsNotNone(access_list[i]['access_to']) - - def test_create_list_access_rule_for_snapshot_select_column(self): - snapshot = self.create_snapshot( - share=self.share['id'], - client=self.get_user_client(), - cleanup_in_class=False, - ) - - self._test_create_list_access_rule_for_snapshot(snapshot['id']) - - access_list = self.user_client.list_access( - snapshot['id'], columns="access_type,access_to", is_snapshot=True - ) - - self.assertTrue(any(x['Access_Type'] is not None for x in access_list)) - self.assertTrue(any(x['Access_To'] is not None for x in access_list)) - - def _create_delete_access_rule(self, snapshot_id, access_type, access_to): - if access_type not in self.access_types: - raise self.skipException( - f"'{access_type}' access rules is disabled for protocol " - f"'{self.protocol}'." - ) - - access = self.user_client.snapshot_access_allow( - snapshot_id, access_type, access_to - ) - - self.assertEqual(access_type, access.get('access_type')) - self.assertEqual( - access_to.replace('\\\\', '\\'), access.get('access_to') - ) - - self.user_client.wait_for_access_rule_status( - snapshot_id, access['id'], is_snapshot=True - ) - self.user_client.snapshot_access_deny(snapshot_id, access['id']) - self.user_client.wait_for_access_rule_deletion( - snapshot_id, access['id'], is_snapshot=True - ) - - self.assertRaises( - tempest_lib_exc.NotFound, - self.user_client.get_access, - snapshot_id, - access['id'], - is_snapshot=True, - ) - - def test_create_delete_snapshot_ip_access_rule(self): - snapshot = self.create_snapshot( - share=self.share['id'], - client=self.get_user_client(), - cleanup_in_class=False, - ) - self._create_delete_access_rule( - snapshot['id'], 'ip', self.access_to['ip'][0] - ) - - def test_create_delete_snapshot_user_access_rule(self): - snapshot = self.create_snapshot( - share=self.share['id'], - client=self.get_user_client(), - cleanup_in_class=False, - ) - self._create_delete_access_rule( - snapshot['id'], 'user', CONF.username_for_user_rules - ) - - def test_create_delete_snapshot_cert_access_rule(self): - snapshot = self.create_snapshot( - share=self.share['id'], - client=self.get_user_client(), - cleanup_in_class=False, - ) - self._create_delete_access_rule( - snapshot['id'], 'cert', self.access_to['cert'][0] - ) - - -@testtools.skipUnless( - CONF.run_snapshot_tests and CONF.run_mount_snapshot_tests, - "Snapshots or mountable snapshots tests are disabled.", -) -class NFSSnapshotAccessTest(SnapshotAccessReadBase): - protocol = 'nfs' - - -@testtools.skipUnless( - CONF.run_snapshot_tests and CONF.run_mount_snapshot_tests, - "Snapshots or mountable snapshots tests are disabled.", -) -class CIFSSnapshotAccessTest(SnapshotAccessReadBase): - protocol = 'cifs' - - -@testtools.skipUnless( - CONF.run_snapshot_tests and CONF.run_mount_snapshot_tests, - "Snapshots or mountable snapshots tests are disabled.", -) -class GlusterFSSnapshotAccessTest(SnapshotAccessReadBase): - protocol = 'glusterfs' - - -@testtools.skipUnless( - CONF.run_snapshot_tests and CONF.run_mount_snapshot_tests, - "Snapshots or mountable snapshots tests are disabled.", -) -class HDFSSnapshotAccessTest(SnapshotAccessReadBase): - protocol = 'hdfs' - - -@testtools.skipUnless( - CONF.run_snapshot_tests and CONF.run_mount_snapshot_tests, - "Snapshots or mountable snapshots tests are disabled.", -) -class MAPRFSSnapshotAccessTest(SnapshotAccessReadBase): - protocol = 'maprfs' - - -def load_tests(loader, tests, _): - result = [] - for test_case in tests: - if type(test_case._tests[0]) is SnapshotAccessReadBase: - continue - result.append(test_case) - return loader.suiteClass(result) diff --git a/manilaclient/tests/functional/test_snapshot_instances.py b/manilaclient/tests/functional/test_snapshot_instances.py deleted file mode 100644 index d0ee9b606..000000000 --- a/manilaclient/tests/functional/test_snapshot_instances.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright 2016 Huawei inc. -# 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. - -import ddt -from oslo_utils import uuidutils -import testtools - -from manilaclient import config -from manilaclient.tests.functional import base -from manilaclient.tests.functional import utils - -CONF = config.CONF - - -@ddt.ddt -@testtools.skipUnless(CONF.run_snapshot_tests, 'Snapshot tests disabled.') -@utils.skip_if_microversion_not_supported('2.19') -class SnapshotInstancesTest(base.BaseTestCase): - def setUp(self): - super().setUp() - self.share = self.create_share(client=self.get_user_client()) - self.snapshot = self.create_snapshot( - share=self.share['id'], client=self.get_user_client() - ) - - def test_list_all_snapshot_instances(self): - snapshot_instances = self.admin_client.list_snapshot_instances() - - self.assertGreater(len(snapshot_instances), 0) - expected_keys = ('ID', 'Snapshot ID', 'Status') - for si in snapshot_instances: - for key in expected_keys: - self.assertIn(key, si) - self.assertTrue(uuidutils.is_uuid_like(si['ID'])) - self.assertTrue(uuidutils.is_uuid_like(si['Snapshot ID'])) - - def test_list_all_snapshot_instances_details(self): - snapshot_instances = self.admin_client.list_snapshot_instances( - detailed=True - ) - - self.assertGreater(len(snapshot_instances), 0) - expected_keys = ( - 'ID', - 'Snapshot ID', - 'Status', - 'Created_at', - 'Updated_at', - 'Share_id', - 'Share_instance_id', - 'Progress', - 'Provider_location', - ) - for si in snapshot_instances: - for key in expected_keys: - self.assertIn(key, si) - for key in ('ID', 'Snapshot ID', 'Share_id', 'Share_instance_id'): - self.assertTrue(uuidutils.is_uuid_like(si[key])) - - def test_list_snapshot_instance_with_snapshot(self): - snapshot_instances = self.admin_client.list_snapshot_instances( - snapshot_id=self.snapshot['id'] - ) - - self.assertEqual(1, len(snapshot_instances)) - expected_keys = ('ID', 'Snapshot ID', 'Status') - for si in snapshot_instances: - for key in expected_keys: - self.assertIn(key, si) - self.assertTrue(uuidutils.is_uuid_like(si['ID'])) - self.assertTrue(uuidutils.is_uuid_like(si['Snapshot ID'])) - - def test_list_snapshot_instance_with_columns(self): - snapshot_instances = self.admin_client.list_snapshot_instances( - self.snapshot['id'], columns='id,status' - ) - - self.assertGreater(len(snapshot_instances), 0) - expected_keys = ('Id', 'Status') - unexpected_keys = ('Snapshot ID',) - for si in snapshot_instances: - for key in expected_keys: - self.assertIn(key, si) - for key in unexpected_keys: - self.assertNotIn(key, si) - self.assertTrue(uuidutils.is_uuid_like(si['Id'])) - - def test_get_snapshot_instance(self): - snapshot_instances = self.admin_client.list_snapshot_instances( - self.snapshot['id'] - ) - - snapshot_instance = self.admin_client.get_snapshot_instance( - snapshot_instances[0]['ID'] - ) - self.assertGreater(len(snapshot_instance), 0) - expected_keys = ( - 'id', - 'snapshot_id', - 'status', - 'created_at', - 'updated_at', - 'share_id', - 'share_instance_id', - 'progress', - 'provider_location', - ) - - for key in expected_keys: - self.assertIn(key, snapshot_instance) - for key in ('id', 'snapshot_id', 'share_id', 'share_instance_id'): - self.assertTrue(uuidutils.is_uuid_like(snapshot_instance[key])) - - def test_snapshot_instance_reset_state(self): - snapshot_instances = self.admin_client.list_snapshot_instances( - self.snapshot['id'] - ) - self.admin_client.reset_snapshot_instance( - snapshot_instances[0]['ID'], 'error' - ) - snapshot_instance = self.admin_client.get_snapshot_instance( - snapshot_instances[0]['ID'] - ) - - self.assertEqual('error', snapshot_instance['status']) - self.admin_client.reset_snapshot_instance( - snapshot_instance['id'], 'available' - ) diff --git a/manilaclient/tests/functional/test_snapshot_instances_export_locations.py b/manilaclient/tests/functional/test_snapshot_instances_export_locations.py deleted file mode 100644 index 92931f2d3..000000000 --- a/manilaclient/tests/functional/test_snapshot_instances_export_locations.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) 2017 Hitachi Data Systems -# 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. - -import ddt -from oslo_utils import uuidutils -import testtools - -from manilaclient import config -from manilaclient.tests.functional import base -from manilaclient.tests.functional import utils - -CONF = config.CONF - - -@ddt.ddt -@testtools.skipUnless( - CONF.run_snapshot_tests and CONF.run_mount_snapshot_tests, - "Snapshots or mountable snapshots tests are disabled.", -) -@utils.skip_if_microversion_not_supported('2.32') -class SnapshotInstanceExportLocationReadWriteTest(base.BaseTestCase): - def setUp(self): - super().setUp() - self.share = self.create_share(client=self.get_user_client()) - self.snapshot = self.create_snapshot( - share=self.share['id'], client=self.get_user_client() - ) - - def test_get_snapshot_instance_export_location(self): - client = self.admin_client - snapshot_instances = client.list_snapshot_instances( - self.snapshot['id'] - ) - - self.assertGreater(len(snapshot_instances), 0) - self.assertIn('ID', snapshot_instances[0]) - self.assertTrue(uuidutils.is_uuid_like(snapshot_instances[0]['ID'])) - - snapshot_instance_id = snapshot_instances[0]['ID'] - - export_locations = client.list_snapshot_instance_export_locations( - snapshot_instance_id - ) - - el = client.get_snapshot_instance_export_location( - snapshot_instance_id, export_locations[0]['ID'] - ) - expected_keys = [ - 'path', - 'id', - 'is_admin_only', - 'share_snapshot_instance_id', - 'updated_at', - 'created_at', - ] - - for key in expected_keys: - self.assertIn(key, el) - for key, key_el in ( - ('ID', 'id'), - ('Path', 'path'), - ('Is Admin only', 'is_admin_only'), - ): - self.assertEqual(export_locations[0][key], el[key_el]) - self.assertTrue( - uuidutils.is_uuid_like(el['share_snapshot_instance_id']) - ) - self.assertTrue(uuidutils.is_uuid_like(el['id'])) - self.assertIn(el['is_admin_only'], ('True', 'False')) - - def test_list_snapshot_instance_export_locations(self): - client = self.admin_client - snapshot_instances = client.list_snapshot_instances( - self.snapshot['id'] - ) - - self.assertGreater(len(snapshot_instances), 0) - self.assertIn('ID', snapshot_instances[0]) - self.assertTrue(uuidutils.is_uuid_like(snapshot_instances[0]['ID'])) - - snapshot_instance_id = snapshot_instances[0]['ID'] - - export_locations = client.list_snapshot_instance_export_locations( - snapshot_instance_id - ) - - self.assertGreater(len(export_locations), 0) - - expected_keys = ('ID', 'Path', 'Is Admin only') - for el in export_locations: - for key in expected_keys: - self.assertIn(key, el) - self.assertTrue(uuidutils.is_uuid_like(el['ID'])) - - def test_list_snapshot_instance_export_locations_with_columns(self): - client = self.admin_client - snapshot_instances = client.list_snapshot_instances( - self.snapshot['id'] - ) - - self.assertGreater(len(snapshot_instances), 0) - self.assertIn('ID', snapshot_instances[0]) - self.assertTrue(uuidutils.is_uuid_like(snapshot_instances[0]['ID'])) - snapshot_instance_id = snapshot_instances[0]['ID'] - - export_locations = client.list_snapshot_instance_export_locations( - snapshot_instance_id, columns='id,path' - ) - - self.assertGreater(len(export_locations), 0) - expected_keys = ('Id', 'Path') - unexpected_keys = ('Updated At', 'Created At', 'Is Admin only') - - for el in export_locations: - for key in expected_keys: - self.assertIn(key, el) - for key in unexpected_keys: - self.assertNotIn(key, el) - self.assertTrue(uuidutils.is_uuid_like(el['Id'])) diff --git a/manilaclient/tests/functional/test_snapshots_export_locations.py b/manilaclient/tests/functional/test_snapshots_export_locations.py deleted file mode 100644 index 3059467cb..000000000 --- a/manilaclient/tests/functional/test_snapshots_export_locations.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) 2017 Hitachi Data Systems -# 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. - -import ddt -from oslo_utils import uuidutils -import testtools - -from manilaclient import config -from manilaclient.tests.functional import base -from manilaclient.tests.functional import utils - -CONF = config.CONF - - -@ddt.ddt -@testtools.skipUnless( - CONF.run_snapshot_tests and CONF.run_mount_snapshot_tests, - "Snapshots or mountable snapshots tests are disabled.", -) -@utils.skip_if_microversion_not_supported('2.32') -class SnapshotExportLocationReadWriteTest(base.BaseTestCase): - def setUp(self): - super().setUp() - self.share = self.create_share(client=self.get_user_client()) - self.snapshot = self.create_snapshot( - share=self.share['id'], client=self.get_user_client() - ) - - @ddt.data('admin', 'user') - def test_get_snapshot_export_location(self, role): - client = self.admin_client if role == 'admin' else self.user_client - - export_locations = client.list_snapshot_export_locations( - self.snapshot['id'] - ) - - el = client.get_snapshot_export_location( - self.snapshot['id'], export_locations[0]['ID'] - ) - - expected_keys = ['path', 'id', 'updated_at', 'created_at'] - if role == 'admin': - expected_keys.extend( - ['is_admin_only', 'share_snapshot_instance_id'] - ) - self.assertTrue( - uuidutils.is_uuid_like(el['share_snapshot_instance_id']) - ) - self.assertIn(el['is_admin_only'], ('True', 'False')) - self.assertTrue(uuidutils.is_uuid_like(el['id'])) - for key in expected_keys: - self.assertIn(key, el) - - @ddt.data('admin', 'user') - def test_list_snapshot_export_locations(self, role): - client = self.admin_client if role == 'admin' else self.user_client - export_locations = client.list_snapshot_export_locations( - self.snapshot['id'] - ) - - self.assertGreater(len(export_locations), 0) - expected_keys = ('ID', 'Path') - - for el in export_locations: - for key in expected_keys: - self.assertIn(key, el) - self.assertTrue(uuidutils.is_uuid_like(el['ID'])) - - @ddt.data('admin', 'user') - def test_list_snapshot_export_locations_with_columns(self, role): - client = self.admin_client if role == 'admin' else self.user_client - export_locations = client.list_snapshot_export_locations( - self.snapshot['id'], columns='id,path' - ) - - self.assertGreater(len(export_locations), 0) - expected_keys = ('Id', 'Path') - unexpected_keys = ('Updated At', 'Created At') - for el in export_locations: - for key in expected_keys: - self.assertIn(key, el) - for key in unexpected_keys: - self.assertNotIn(key, el) - self.assertTrue(uuidutils.is_uuid_like(el['Id'])) diff --git a/manilaclient/tests/unit/test_shell.py b/manilaclient/tests/unit/test_shell.py deleted file mode 100644 index 478ffd85a..000000000 --- a/manilaclient/tests/unit/test_shell.py +++ /dev/null @@ -1,556 +0,0 @@ -# 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 io -import re -import sys -from unittest import mock - -import ddt -import fixtures -from tempest.lib.cli import output_parser -from testtools import matchers - -import manilaclient -from manilaclient.common import cliutils -from manilaclient.common import constants -from manilaclient import exceptions -from manilaclient import shell -from manilaclient.tests.unit import utils -from manilaclient.tests.unit.v2 import fakes - - -@ddt.ddt -class OpenstackManilaShellTest(utils.TestCase): - FAKE_ENV = { - 'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password', - 'OS_TENANT_NAME': 'tenant_name', - 'OS_AUTH_URL': 'http://no.where', - } - - # Patch os.environ to avoid required auth info. - def set_env_vars(self, env_vars): - for k, v in env_vars.items(): - self.useFixture(fixtures.EnvironmentVariable(k, v)) - - def shell_discover_client( - self, - current_client, - os_api_version, - os_endpoint_type, - os_service_type, - client_args, - ): - return current_client, manilaclient.API_MAX_VERSION - - def shell(self, argstr): - orig = sys.stdout - try: - sys.stdout = io.StringIO() - _shell = shell.OpenStackManilaShell() - _shell._discover_client = self.shell_discover_client - _shell.main(argstr.split()) - except SystemExit: - exc_type, exc_value, exc_traceback = sys.exc_info() - self.assertEqual(exc_value.code, 0) - finally: - out = sys.stdout.getvalue() - sys.stdout.close() - sys.stdout = orig - - return out - - @ddt.data( - {}, - {'OS_AUTH_URL': 'http://foo.bar'}, - {'OS_AUTH_URL': 'http://foo.bar', 'OS_USERNAME': 'foo'}, - { - 'OS_AUTH_URL': 'http://foo.bar', - 'OS_USERNAME': 'foo_user', - 'OS_PASSWORD': 'foo_password', - }, - { - 'OS_TENANT_NAME': 'foo_tenant', - 'OS_USERNAME': 'foo_user', - 'OS_PASSWORD': 'foo_password', - }, - {'OS_TOKEN': 'foo_token'}, - {'OS_MANILA_BYPASS_URL': 'http://foo.foo'}, - ) - def test_main_failure(self, env_vars): - self.set_env_vars(env_vars) - with mock.patch.object(shell, 'client') as mock_client: - self.assertRaises(exceptions.CommandError, self.shell, 'list') - self.assertFalse(mock_client.Client.called) - - @ddt.data(None, 'foo_key') - def test_main_success(self, os_key): - env_vars = { - 'OS_AUTH_URL': 'http://foo.bar', - 'OS_USERNAME': 'foo_username', - 'OS_USER_ID': 'foo_user_id', - 'OS_PASSWORD': 'foo_password', - 'OS_TENANT_NAME': 'foo_tenant', - 'OS_TENANT_ID': 'foo_tenant_id', - 'OS_PROJECT_NAME': 'foo_project', - 'OS_PROJECT_ID': 'foo_project_id', - 'OS_PROJECT_DOMAIN_ID': 'foo_project_domain_id', - 'OS_PROJECT_DOMAIN_NAME': 'foo_project_domain_name', - 'OS_USER_DOMAIN_NAME': 'foo_user_domain_name', - 'OS_USER_DOMAIN_ID': 'foo_user_domain_id', - 'OS_CERT': 'foo_cert', - 'OS_KEY': os_key, - } - self.set_env_vars(env_vars) - cert = env_vars['OS_CERT'] - if os_key: - cert = (cert, env_vars['OS_KEY']) - - with mock.patch.object(shell, 'client') as mock_client: - self.shell('list') - - mock_client.Client.assert_called_with( - manilaclient.API_MAX_VERSION, - username=env_vars['OS_USERNAME'], - password=env_vars['OS_PASSWORD'], - project_name=env_vars['OS_PROJECT_NAME'], - auth_url=env_vars['OS_AUTH_URL'], - insecure=False, - region_name='', - tenant_id=env_vars['OS_PROJECT_ID'], - endpoint_type='publicURL', - extensions=mock.ANY, - service_type=constants.V2_SERVICE_TYPE, - service_name='', - retries=0, - http_log_debug=False, - cacert=None, - use_keyring=False, - force_new_token=False, - user_id=env_vars['OS_USER_ID'], - user_domain_id=env_vars['OS_USER_DOMAIN_ID'], - user_domain_name=env_vars['OS_USER_DOMAIN_NAME'], - project_domain_id=env_vars['OS_PROJECT_DOMAIN_ID'], - project_domain_name=env_vars['OS_PROJECT_DOMAIN_NAME'], - cert=cert, - input_auth_token='', - service_catalog_url='', - ) - - @ddt.data( - { - "env_vars": { - "OS_MANILA_BYPASS_URL": "http://foo.url", - "OS_TOKEN": "foo_token", - }, - "kwargs": { - "--os-token": "bar_token", - "--bypass-url": "http://bar.url", - }, - "expected": { - "input_auth_token": "bar_token", - "service_catalog_url": "http://bar.url", - }, - }, - { - "env_vars": { - "OS_MANILA_BYPASS_URL": "http://foo.url", - "OS_TOKEN": "foo_token", - }, - "kwargs": {}, - "expected": { - "input_auth_token": "foo_token", - "service_catalog_url": "http://foo.url", - }, - }, - { - "env_vars": {}, - "kwargs": { - "--os-token": "bar_token", - "--bypass-url": "http://bar.url", - }, - "expected": { - "input_auth_token": "bar_token", - "service_catalog_url": "http://bar.url", - }, - }, - { - "env_vars": { - "MANILACLIENT_BYPASS_URL": "http://foo.url", - "OS_TOKEN": "foo_token", - }, - "kwargs": {}, - "expected": { - "input_auth_token": "foo_token", - "service_catalog_url": "http://foo.url", - }, - }, - { - "env_vars": {"OS_TOKEN": "foo_token"}, - "kwargs": {"--bypass-url": "http://bar.url"}, - "expected": { - "input_auth_token": "foo_token", - "service_catalog_url": "http://bar.url", - }, - }, - { - "env_vars": { - "MANILACLIENT_BYPASS_URL": "http://foo.url", - "OS_MANILA_BYPASS_URL": "http://bar.url", - "OS_TOKEN": "foo_token", - }, - "kwargs": {"--os-token": "bar_token"}, - "expected": { - "input_auth_token": "bar_token", - "service_catalog_url": "http://bar.url", - }, - }, - ) - @ddt.unpack - def test_main_success_with_token(self, env_vars, kwargs, expected): - self.set_env_vars(env_vars) - with mock.patch.object(shell, "client") as mock_client: - cmd = "" - for k, v in kwargs.items(): - cmd += f"{k}={v} " - cmd += "list" - - self.shell(cmd) - - mock_client.Client.assert_called_with( - manilaclient.API_MAX_VERSION, - username="", - password="", - project_name="", - auth_url="", - insecure=False, - region_name="", - tenant_id="", - endpoint_type="publicURL", - extensions=mock.ANY, - service_type=constants.V2_SERVICE_TYPE, - service_name="", - retries=0, - http_log_debug=False, - cacert=None, - use_keyring=False, - force_new_token=False, - user_id="", - user_domain_id="", - user_domain_name="", - project_domain_id="", - project_domain_name="", - cert=None, - input_auth_token=expected["input_auth_token"], - service_catalog_url=expected["service_catalog_url"], - ) - - @ddt.data( - # default without any env var or kwargs - { - "env_vars": { - "OS_TOKEN": "foo_token", - "OS_MANILA_BYPASS_URL": "http://bar.url", - }, - "kwargs": {}, - "expected": { - "input_auth_token": "foo_token", - "service_catalog_url": "http://bar.url", - "os_endpoint_type": "publicURL", - }, - }, - # only env var - { - "env_vars": { - "OS_TOKEN": "foo_token", - "OS_MANILA_BYPASS_URL": "http://bar.url", - "OS_MANILA_ENDPOINT_TYPE": "custom-endpoint-type", - }, - "kwargs": {}, - "expected": { - "input_auth_token": "foo_token", - "service_catalog_url": "http://bar.url", - "os_endpoint_type": "custom-endpoint-type", - }, - }, - # only kwargs - { - "env_vars": { - "OS_TOKEN": "foo_token", - "OS_MANILA_BYPASS_URL": "http://bar.url", - }, - "kwargs": {"--endpoint-type": "custom-kwargs-endpoint-type"}, - "expected": { - "input_auth_token": "foo_token", - "service_catalog_url": "http://bar.url", - "os_endpoint_type": "custom-kwargs-endpoint-type", - }, - }, - # env var *and* kwargs (kwargs should win) - { - "env_vars": { - "OS_TOKEN": "foo_token", - "OS_MANILA_BYPASS_URL": "http://bar.url", - "os_endpoint_type": "custom-env-endpoint-type", - }, - "kwargs": {"--endpoint-type": "custom-kwargs-endpoint-type"}, - "expected": { - "input_auth_token": "foo_token", - "service_catalog_url": "http://bar.url", - "os_endpoint_type": "custom-kwargs-endpoint-type", - }, - }, - ) - @ddt.unpack - def test_main_success_with_os_endpoint(self, env_vars, kwargs, expected): - self.set_env_vars(env_vars) - with mock.patch.object(shell, "client") as mock_client: - cmd = "" - for k, v in kwargs.items(): - cmd += f"{k}={v} " - cmd += "list" - - self.shell(cmd) - - mock_client.Client.assert_called_with( - manilaclient.API_MAX_VERSION, - username="", - password="", - project_name="", - auth_url="", - insecure=False, - region_name="", - tenant_id="", - endpoint_type=expected["os_endpoint_type"], - extensions=mock.ANY, - service_type=constants.V2_SERVICE_TYPE, - service_name="", - retries=0, - http_log_debug=False, - cacert=None, - use_keyring=False, - force_new_token=False, - user_id="", - user_domain_id="", - user_domain_name="", - project_domain_id="", - project_domain_name="", - cert=None, - input_auth_token=expected["input_auth_token"], - service_catalog_url=expected["service_catalog_url"], - ) - - def test_help_unknown_command(self): - self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo') - - @ddt.data('list --help', '--help list', 'help list') - def test_help_on_subcommand(self, cmd): - required = [ - '.*?^usage: manila list', - '.*?^List NAS shares with filters.', - ] - help_text = self.shell(cmd) - for r in required: - self.assertThat( - help_text, matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE) - ) - - def test_common_args_in_help_message(self): - expected_args = ( - '--version', - '', - '--debug', - '--os-cache', - '--os-reset-cache', - '--os-user-id', - '--os-username', - '--os-password', - '--os-tenant-name', - '--os-project-name', - '--os-tenant-id', - '--os-project-id', - '--os-user-domain-id', - '--os-user-domain-name', - '--os-project-domain-id', - '--os-project-domain-name', - '--os-auth-url', - '--os-region-name', - '--service-type', - '--service-name', - '--share-service-name', - '--endpoint-type', - '--os-share-api-version', - '--os-cacert', - '--retries', - '--os-cert', - '--os-key', - ) - - help_text = self.shell('help') - - for expected_arg in expected_args: - self.assertIn(expected_arg, help_text) - - -class CustomOpenStackManilaShell(shell.OpenStackManilaShell): - @staticmethod - @cliutils.arg( - '--default-is-none', - '--default_is_none', - type=str, - metavar='', - action='single_alias', - help='Default value is None and metavar set.', - default=None, - ) - def do_foo(cs, args): - cliutils.print_dict({'key': args.default_is_none}) - - @staticmethod - @cliutils.arg( - '--default-is-not-none', - '--default_is_not_none', - type=str, - action='single_alias', - help='Default value is not None and metavar not set.', - default='bar', - ) - def do_bar(cs, args): - cliutils.print_dict({'key': args.default_is_not_none}) - - @staticmethod - @cliutils.arg( - '--list-like', - '--list_like', - nargs='*', - action='single_alias', - help='Default value is None, metavar not set and result is list.', - default=None, - ) - def do_quuz(cs, args): - cliutils.print_dict({'key': args.list_like}) - - -@ddt.ddt -class AllowOnlyOneAliasAtATimeActionTest(utils.TestCase): - FAKE_ENV = { - 'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password', - 'OS_TENANT_NAME': 'tenant_name', - 'OS_AUTH_URL': 'http://no.where', - } - - def setUp(self): - super(self.__class__, self).setUp() - for k, v in self.FAKE_ENV.items(): - self.useFixture(fixtures.EnvironmentVariable(k, v)) - self.mock_object( - shell.client, - 'get_client_class', - mock.Mock(return_value=fakes.FakeClient), - ) - - def shell_discover_client( - self, - current_client, - os_api_version, - os_endpoint_type, - os_service_type, - client_args, - ): - return current_client, manilaclient.API_MAX_VERSION - - def shell(self, argstr): - orig = sys.stdout - try: - sys.stdout = io.StringIO() - _shell = CustomOpenStackManilaShell() - _shell._discover_client = self.shell_discover_client - _shell.main(argstr.split()) - except SystemExit: - exc_type, exc_value, exc_traceback = sys.exc_info() - self.assertEqual(exc_value.code, 0) - finally: - out = sys.stdout.getvalue() - sys.stdout.close() - sys.stdout = orig - - return out - - @ddt.data( - ('--default-is-none foo', 'foo'), - ('--default-is-none foo --default-is-none foo', 'foo'), - ('--default-is-none foo --default_is_none foo', 'foo'), - ('--default_is_none None', 'None'), - ) - @ddt.unpack - def test_foo_success(self, options_str, expected_result): - output = self.shell(f'foo {options_str}') - parsed_output = output_parser.details(output) - self.assertEqual({'key': expected_result}, parsed_output) - - @ddt.data( - '--default-is-none foo --default-is-none bar', - '--default-is-none foo --default_is_none bar', - '--default-is-none foo --default_is_none FOO', - ) - def test_foo_error(self, options_str): - self.assertRaises( - matchers.MismatchError, self.shell, f'foo {options_str}' - ) - - @ddt.data( - ('--default-is-not-none bar', 'bar'), - ('--default_is_not_none bar --default-is-not-none bar', 'bar'), - ('--default_is_not_none bar --default_is_not_none bar', 'bar'), - ('--default-is-not-none not_bar', 'not_bar'), - ('--default_is_not_none None', 'None'), - ) - @ddt.unpack - def test_bar_success(self, options_str, expected_result): - output = self.shell(f'bar {options_str}') - parsed_output = output_parser.details(output) - self.assertEqual({'key': expected_result}, parsed_output) - - @ddt.data( - '--default-is-not-none foo --default-is-not-none bar', - '--default-is-not-none foo --default_is_not_none bar', - '--default-is-not-none bar --default_is_not_none BAR', - ) - def test_bar_error(self, options_str): - self.assertRaises( - matchers.MismatchError, self.shell, f'bar {options_str}' - ) - - @ddt.data( - ('--list-like q=w', "['q=w']"), - ('--list-like q=w --list_like q=w', "['q=w']"), - ( - '--list-like q=w e=r t=y --list_like e=r t=y q=w', - "['e=r', 'q=w', 't=y']", - ), - ('--list_like q=w e=r t=y', "['e=r', 'q=w', 't=y']"), - ) - @ddt.unpack - def test_quuz_success(self, options_str, expected_result): - output = self.shell(f'quuz {options_str}') - parsed_output = output_parser.details(output) - self.assertEqual({'key': expected_result}, parsed_output) - - @ddt.data( - '--list-like q=w --list_like e=r t=y', - ) - def test_quuz_error(self, options_str): - self.assertRaises( - matchers.MismatchError, self.shell, f'quuz {options_str}' - ) diff --git a/manilaclient/tests/unit/v2/test_shell.py b/manilaclient/tests/unit/v2/test_shell.py deleted file mode 100644 index 821484990..000000000 --- a/manilaclient/tests/unit/v2/test_shell.py +++ /dev/null @@ -1,4859 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# Copyright 2014 Mirantis, Inc. -# 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. - -import itertools -from unittest import mock - -import ddt -import fixtures -from oslo_utils import strutils - -from manilaclient import api_versions -from manilaclient import client -from manilaclient.common.apiclient import utils as apiclient_utils -from manilaclient.common import cliutils -from manilaclient.common import constants -from manilaclient import exceptions -from manilaclient import shell -from manilaclient.tests.unit import utils as test_utils -from manilaclient.tests.unit.v2 import fakes -from manilaclient import utils -from manilaclient.v2 import messages -from manilaclient.v2 import security_services -from manilaclient.v2 import share_access_rules -from manilaclient.v2 import share_group_types -from manilaclient.v2 import share_groups -from manilaclient.v2 import share_instances -from manilaclient.v2 import share_network_subnets -from manilaclient.v2 import share_networks -from manilaclient.v2 import share_servers -from manilaclient.v2 import share_snapshots -from manilaclient.v2 import share_types -from manilaclient.v2 import shares -from manilaclient.v2 import shell as shell_v2 - - -@ddt.ddt -class ShellTest(test_utils.TestCase): - FAKE_ENV = { - 'MANILA_USERNAME': 'username', - 'MANILA_PASSWORD': 'password', - 'MANILA_PROJECT_ID': 'project_id', - 'MANILA_URL': 'http://no.where', - } - - # Patch os.environ to avoid required auth info. - def setUp(self): - """Run before each test.""" - super().setUp() - for var in self.FAKE_ENV: - self.useFixture( - fixtures.EnvironmentVariable(var, self.FAKE_ENV[var]) - ) - self.mock_completion() - - self.shell = shell.OpenStackManilaShell() - - # HACK(bcwaldon): replace this when we start using stubs - self.old_get_client_class = client.get_client_class - client.get_client_class = lambda *_: fakes.FakeClient - - # Following shows available separators for optional params - # and its values - self.separators = [' ', '='] - self.create_share_body = { - "share": { - "share_type": None, - "name": None, - "snapshot_id": None, - "description": None, - "metadata": {}, - "share_proto": "nfs", - "share_network_id": None, - "size": 1, - "is_public": False, - "availability_zone": None, - "scheduler_hints": {}, - } - } - - def tearDown(self): - # For some method like test_image_meta_bad_action we are - # testing a SystemExit to be thrown and object self.shell has - # no time to get instantatiated which is OK in this case, so - # we make sure the method is there before launching it. - if hasattr(self.shell, 'cs') and hasattr( - self.shell.cs, 'clear_callstack' - ): - self.shell.cs.clear_callstack() - - # HACK(bcwaldon): replace this when we start using stubs - client.get_client_class = self.old_get_client_class - super().tearDown() - - def run_command(self, cmd, version=None): - if version: - args = ['--os-share-api-version', version] + cmd.split() - else: - args = cmd.split() - self.shell.main(args) - - def assert_called(self, method, url, body=None, **kwargs): - return self.shell.cs.assert_called(method, url, body, **kwargs) - - def assert_called_anytime( - self, method, url, body=None, clear_callstack=True - ): - return self.shell.cs.assert_called_anytime( - method, url, body, clear_callstack=clear_callstack - ) - - def test_availability_zone_list(self): - self.run_command('availability-zone-list') - self.assert_called('GET', '/availability-zones') - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_availability_zone_list_select_column(self): - self.run_command('availability-zone-list --columns id,name') - self.assert_called('GET', '/availability-zones') - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['Id', 'Name'] - ) - - def test_service_list(self): - self.run_command('service-list') - self.assert_called('GET', '/services') - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_service_list_select_column(self): - self.run_command('service-list --columns id,host') - self.assert_called('GET', '/services') - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['Id', 'Host'] - ) - - def test_service_enable(self): - self.run_command('service-enable foo_host@bar_backend manila-share') - self.assert_called( - 'PUT', - '/services/enable', - {'host': 'foo_host@bar_backend', 'binary': 'manila-share'}, - ) - - def test_service_disable(self): - self.run_command('service-disable foo_host@bar_backend manila-share') - self.assert_called( - 'PUT', - '/services/disable', - {'host': 'foo_host@bar_backend', 'binary': 'manila-share'}, - ) - - def test_list(self): - self.run_command('list') - # NOTE(jdg): we default to detail currently - self.assert_called('GET', '/shares/detail') - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_list_select_column(self): - self.run_command('list --column id,name') - self.assert_called('GET', '/shares/detail') - cliutils.print_list.assert_called_once_with( - mock.ANY, ['Id', 'Name'], sortby_index=None - ) - - def test_list_sort_by_name(self): - self.run_command('list --sort_key name') - self.assert_called('GET', '/shares/detail?sort_key=name') - - def test_list_filter_status(self): - for separator in self.separators: - self.run_command('list --status' + separator + 'available') - self.assert_called('GET', '/shares/detail?status=available') - - def test_list_filter_name(self): - for separator in self.separators: - self.run_command('list --name' + separator + '1234') - self.assert_called('GET', '/shares/detail?name=1234') - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_list_all_tenants_only_key(self): - self.run_command('list --all-tenants') - self.assert_called('GET', '/shares/detail?all_tenants=1') - cliutils.print_list.assert_called_once_with( - mock.ANY, - [ - 'ID', - 'Name', - 'Size', - 'Share Proto', - 'Status', - 'Is Public', - 'Share Type Name', - 'Host', - 'Availability Zone', - 'Project ID', - ], - sortby_index=None, - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_list_select_column_and_all_tenants(self): - self.run_command('list --columns ID,Name --all-tenants') - self.assert_called('GET', '/shares/detail?all_tenants=1') - cliutils.print_list.assert_called_once_with( - mock.ANY, ['Id', 'Name'], sortby_index=None - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_list_select_column_and_public(self): - self.run_command('list --columns ID,Name --public') - self.assert_called('GET', '/shares/detail?is_public=True') - cliutils.print_list.assert_called_once_with( - mock.ANY, ['Id', 'Name'], sortby_index=None - ) - - def test_list_all_tenants_key_and_value_1(self): - for separator in self.separators: - self.run_command('list --all-tenants' + separator + '1') - self.assert_called('GET', '/shares/detail?all_tenants=1') - - def test_list_all_tenants_key_and_value_0(self): - for separator in self.separators: - self.run_command('list --all-tenants' + separator + '0') - self.assert_called('GET', '/shares/detail') - - def test_list_filter_by_share_server_and_its_aliases(self): - aliases = [ - '--share-server-id', - '--share-server_id', - '--share_server-id', - '--share_server_id', - ] - for alias in aliases: - for separator in self.separators: - self.run_command('list ' + alias + separator + '1234') - self.assert_called( - 'GET', '/shares/detail?share_server_id=1234' - ) - - def test_list_filter_by_metadata(self): - self.run_command('list --metadata key=value') - # /shares/detail?metadata={'key': 'value'} - self.assert_called( - 'GET', '/shares/detail?metadata=%7B%27key%27%3A+%27value%27%7D' - ) - - def test_list_filter_by_metadata_with_multiple_key_values(self): - self.run_command('list --metadata key1=value1 key2=value2 key3=value3') - # /shares/detail?metadata={'key1': 'value1', - # 'key2': 'value2', 'key3': 'value3'} - self.assert_called( - 'GET', - '/shares/detail?metadata=' - '%7B%27key1%27%3A+%27value1%27%2C+%27key2%27%3A+' - '%27value2%27%2C+%27key3%27%3A+%27value3%27%7D', - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_list_filter_by_metadata_with_empty_metadata(self): - self.run_command('list --metadata \'\'') - - self.assert_called('GET', '/shares/detail') - cliutils.print_list.assert_called() - args, _ = cliutils.print_list.call_args - shares = args[0] - # All 4 shares irrespective of metadata values printed - self.assertEqual(len(shares), 4) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_list_filter_by_metadata_set_to_None(self): - self.run_command('list --metadata None') - - self.assert_called('GET', '/shares/detail') - cliutils.print_list.assert_called() - args, _ = cliutils.print_list.call_args - shares = args[0] - # Check that the size of shares is 2(shares with metadata={}) - self.assertEqual(len(shares), 2) - for share in shares: - self.assertEqual(share.metadata, {}) - - def test_list_filter_by_metadata_with_one_empty_of_many_metadata(self): - self.run_command('list --metadata key=value \'\'') - # /shares/detail?metadata={'key': 'value'} - self.assert_called( - 'GET', '/shares/detail?metadata=%7B%27key%27%3A+%27value%27%7D' - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_list_filter_by_metadata_with_no_metadata(self): - self.run_command('list --metadata') - # /shares/detail - self.assert_called('GET', '/shares/detail') - cliutils.print_list.assert_called() - args, _ = cliutils.print_list.call_args - shares = args[0] - # All 4 shares printed - self.assertEqual(len(shares), 4) - - def test_list_filter_by_extra_specs_and_its_aliases(self): - aliases = [ - '--extra-specs', - '--extra_specs', - ] - for alias in aliases: - self.run_command('list ' + alias + ' key=value') - self.assert_called( - 'GET', - '/shares/detail?extra_specs=%7B%27key%27%3A+%27value%27%7D', - ) - - def test_list_filter_by_share_type_and_its_aliases(self): - fake_st = type('Empty', (object,), {'id': 'fake_st'}) - aliases = [ - '--share-type', - '--share_type', - '--share-type-id', - '--share-type_id', - '--share_type-id', - '--share_type_id', - ] - for alias in aliases: - for separator in self.separators: - with mock.patch.object( - apiclient_utils, - 'find_resource', - mock.Mock(return_value=fake_st), - ): - self.run_command('list ' + alias + separator + fake_st.id) - self.assert_called( - 'GET', '/shares/detail?share_type_id=' + fake_st.id - ) - - def test_list_filter_by_inexact_name(self): - for separator in self.separators: - self.run_command('list --name~' + separator + 'fake_name') - self.assert_called('GET', '/shares/detail?name~=fake_name') - - def test_list_filter_by_inexact_description(self): - for separator in self.separators: - self.run_command( - 'list --description~' + separator + 'fake_description' - ) - self.assert_called( - 'GET', '/shares/detail?description~=fake_description' - ) - - def test_list_filter_by_inexact_unicode_name(self): - for separator in self.separators: - self.run_command('list --name~' + separator + 'ффф') - self.assert_called( - 'GET', '/shares/detail?name~=%D1%84%D1%84%D1%84' - ) - - def test_list_filter_by_inexact_unicode_description(self): - for separator in self.separators: - self.run_command('list --description~' + separator + 'ффф') - self.assert_called( - 'GET', '/shares/detail?description~=%D1%84%D1%84%D1%84' - ) - - def test_list_filter_by_share_type_not_found(self): - for separator in self.separators: - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'list --share-type' + separator + 'not_found_expected', - ) - self.assert_called('GET', '/types?all_tenants=1&is_public=all') - - def test_list_with_limit(self): - for separator in self.separators: - self.run_command('list --limit' + separator + '50') - self.assert_called('GET', '/shares/detail?limit=50') - - def test_list_with_offset(self): - for separator in self.separators: - self.run_command('list --offset' + separator + '50') - self.assert_called('GET', '/shares/detail?offset=50') - - def test_list_with_sort_dir_verify_keys(self): - # Verify allowed aliases and keys - aliases = ['--sort_dir', '--sort-dir'] - for alias in aliases: - for key in constants.SORT_DIR_VALUES: - for separator in self.separators: - self.run_command('list ' + alias + separator + key) - self.assert_called('GET', '/shares/detail?sort_dir=' + key) - - def test_list_with_fake_sort_dir(self): - self.assertRaises( - ValueError, - self.run_command, - 'list --sort-dir fake_sort_dir', - ) - - def test_list_with_sort_key_verify_keys(self): - # Verify allowed aliases and keys - aliases = ['--sort_key', '--sort-key'] - for alias in aliases: - for key in constants.SHARE_SORT_KEY_VALUES: - for separator in self.separators: - self.run_command('list ' + alias + separator + key) - key = 'share_network_id' if key == 'share_network' else key - key = 'snapshot_id' if key == 'snapshot' else key - key = 'share_type_id' if key == 'share_type' else key - key = ( - 'availability_zone_id' - if key == 'availability_zone' - else key - ) - self.assert_called('GET', '/shares/detail?sort_key=' + key) - - def test_list_with_fake_sort_key(self): - self.assertRaises( - ValueError, - self.run_command, - 'list --sort-key fake_sort_key', - ) - - def test_list_filter_by_snapshot(self): - fake_s = type('Empty', (object,), {'id': 'fake_snapshot_id'}) - for separator in self.separators: - with mock.patch.object( - apiclient_utils, - 'find_resource', - mock.Mock(return_value=fake_s), - ): - self.run_command('list --snapshot' + separator + fake_s.id) - self.assert_called( - 'GET', '/shares/detail?snapshot_id=' + fake_s.id - ) - - def test_list_filter_by_snapshot_not_found(self): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'list --snapshot not_found_expected', - ) - self.assert_called('GET', '/snapshots/detail?all_tenants=1') - - def test_list_filter_by_host(self): - for separator in self.separators: - self.run_command('list --host' + separator + 'fake_host') - self.assert_called('GET', '/shares/detail?host=fake_host') - - @ddt.data( - ('id', 'b4991315-eb7d-43ec-979e-5715d4399827'), ('path', 'fake_path') - ) - @ddt.unpack - def test_share_list_filter_by_export_location(self, filter_type, value): - for separator in self.separators: - self.run_command('list --export_location' + separator + value) - self.assert_called( - 'GET', - '/shares/detail?export_location_' + filter_type + '=' + value, - ) - - @ddt.data('list', 'share-instance-list') - def test_share_or_instance_list_filter_by_export_location_version_invalid( - self, cmd - ): - self.assertRaises( - exceptions.CommandError, - self.run_command, - cmd + ' --export_location=fake', - '2.34', - ) - - def test_list_filter_by_share_network(self): - aliases = [ - '--share-network', - '--share_network', - ] - fake_sn = type('Empty', (object,), {'id': 'fake_share_network_id'}) - for alias in aliases: - for separator in self.separators: - with mock.patch.object( - apiclient_utils, - 'find_resource', - mock.Mock(return_value=fake_sn), - ): - self.run_command('list ' + alias + separator + fake_sn.id) - self.assert_called( - 'GET', '/shares/detail?share_network_id=' + fake_sn.id - ) - - def test_list_filter_by_share_network_not_found(self): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'list --share-network not_found_expected', - ) - self.assert_called('GET', '/share-networks/detail?all_tenants=1') - - @ddt.data('True', 'False') - def test_list_filter_with_count(self, value): - except_url = '/shares/detail?with_count=' + value - if value == 'False': - except_url = '/shares/detail' - - for separator in self.separators: - self.run_command('list --count' + separator + value) - self.assert_called('GET', except_url) - - @ddt.data('True', 'False') - def test_list_filter_with_count_invalid_version(self, value): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'list --count ' + value, - version='2.41', - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_instance_list(self): - self.run_command('share-instance-list') - - self.assert_called('GET', '/share_instances') - cliutils.print_list.assert_called_once_with( - mock.ANY, - [ - 'ID', - 'Share ID', - 'Host', - 'Status', - 'Availability Zone', - 'Share Network ID', - 'Share Server ID', - 'Share Type ID', - ], - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_instance_list_select_column(self): - self.run_command('share-instance-list --column id,host,status') - - self.assert_called('GET', '/share_instances') - cliutils.print_list.assert_called_once_with( - mock.ANY, ['Id', 'Host', 'Status'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - @ddt.data( - ('id', 'b4991315-eb7d-43ec-979e-5715d4399827'), ('path', 'fake_path') - ) - @ddt.unpack - def test_share_instance_list_filter_by_export_location( - self, filter_type, value - ): - for separator in self.separators: - self.run_command( - 'share-instance-list --export_location' + separator + value - ) - self.assert_called( - 'GET', - ( - '/share_instances?export_location_' - + filter_type - + '=' - + value - ), - ) - - @mock.patch.object( - apiclient_utils, 'find_resource', mock.Mock(return_value='fake') - ) - def test_share_instance_list_with_share(self): - self.run_command('share-instance-list --share-id=fake') - self.assert_called('GET', '/shares/fake/instances') - - def test_share_instance_list_invalid_share(self): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'share-instance-list --share-id=not-found-id', - ) - - def test_share_instance_show(self): - self.run_command('share-instance-show 1234') - self.assert_called_anytime('GET', '/share_instances/1234') - - def test_share_instance_export_location_list(self): - self.run_command('share-instance-export-location-list 1234') - - self.assert_called_anytime( - 'GET', '/share_instances/1234/export_locations' - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_instance_export_location_list_with_columns(self): - self.run_command( - 'share-instance-export-location-list 1234 --columns uuid,path' - ) - - self.assert_called_anytime( - 'GET', '/share_instances/1234/export_locations' - ) - cliutils.print_list.assert_called_once_with(mock.ANY, ['Uuid', 'Path']) - - def test_share_instance_export_location_show(self): - self.run_command( - 'share-instance-export-location-show 1234 fake_el_uuid' - ) - self.assert_called_anytime( - 'GET', '/share_instances/1234/export_locations/fake_el_uuid' - ) - - def test_share_instance_reset_state(self): - self.run_command('share-instance-reset-state 1234') - expected = {'reset_status': {'status': 'available'}} - self.assert_called( - 'POST', '/share_instances/1234/action', body=expected - ) - - def test_share_instance_force_delete(self): - manager_mock = mock.Mock() - share_instance = share_instances.ShareInstance( - manager_mock, {'id': 'fake'}, True - ) - - with mock.patch.object( - shell_v2, - '_find_share_instance', - mock.Mock(return_value=share_instance), - ): - self.run_command('share-instance-force-delete 1234') - manager_mock.force_delete.assert_called_once_with(share_instance) - - @ddt.data( - ('share_instance_xyz',), ('share_instance_abc', 'share_instance_xyz') - ) - def test_share_instance_force_delete_wait(self, instances_to_delete): - fake_manager = mock.Mock() - fake_instances = [ - share_instances.ShareInstance(fake_manager, {'id': '1234'}) - for instance in instances_to_delete - ] - instance_not_found_error = ( - "Delete for instance %s failed: No " - "instance with a name or " - "ID of '%s' exists." - ) - instances_are_not_found_errors = [ - exceptions.CommandError( - instance_not_found_error % (instance, instance) - ) - for instance in instances_to_delete - ] - self.mock_object( - shell_v2, - '_find_share_instance', - mock.Mock( - side_effect=(fake_instances + instances_are_not_found_errors) - ), - ) - self.run_command( - 'share-instance-force-delete {} --wait'.format( - ' '.join(instances_to_delete) - ) - ) - shell_v2._find_share_instance.assert_has_calls( - [ - mock.call(self.shell.cs, instance) - for instance in instances_to_delete - ] - ) - fake_manager.force_delete.assert_has_calls( - [mock.call(instance) for instance in fake_instances] - ) - self.assertEqual( - len(instances_to_delete), fake_manager.force_delete.call_count - ) - - def test_type_show_details(self): - self.run_command('type-show 1234') - self.assert_called_anytime('GET', '/types/1234') - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - @ddt.data( - *itertools.product( - ( - 'type-list --columns id,is_default', - 'type-list --columns id,name', - 'type-list --columns is_default', - 'type-list', - ), - {'2.45', '2.46', api_versions.MAX_VERSION}, - ) - ) - @ddt.unpack - def test_type_list(self, command, version): - self.run_command(command, version=version) - - columns_requested = [ - 'ID', - 'Name', - 'visibility', - 'is_default', - 'required_extra_specs', - 'optional_extra_specs', - 'Description', - ] - if 'columns' in command: - columns_requested = command.split('--columns ')[1].split(',') - - is_default_in_api = api_versions.APIVersion( - version - ) >= api_versions.APIVersion('2.46') - - if not is_default_in_api and 'is_default' in columns_requested: - self.assert_called('GET', '/types/default') - self.assert_called_anytime('GET', '/types') - else: - self.assert_called('GET', '/types') - - cliutils.print_list.assert_called_with( - mock.ANY, columns_requested, mock.ANY - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_type_list_select_column(self): - self.run_command('type-list --columns id,name') - - self.assert_called('GET', '/types') - cliutils.print_list.assert_called_once_with( - mock.ANY, ['id', 'name'], mock.ANY - ) - - def test_type_list_all(self): - self.run_command('type-list --all') - self.assert_called_anytime('GET', '/types?is_public=all') - - @ddt.data(True, False) - def test_type_create_with_access(self, public): - expected = { - 'share_type': { - 'name': 'test-type-3', - 'extra_specs': { - 'driver_handles_share_servers': False, - }, - 'share_type_access:is_public': public, - } - } - self.run_command( - f'type-create test-type-3 false --is-public {str(public)}' - ) - self.assert_called('POST', '/types', body=expected) - - def test_type_access_list(self): - self.run_command('type-access-list 3') - self.assert_called('GET', '/types/3/share_type_access') - - def test_type_access_add_project(self): - expected = {'addProjectAccess': {'project': '101'}} - self.run_command('type-access-add 3 101') - self.assert_called('POST', '/types/3/action', body=expected) - - def test_type_access_remove_project(self): - expected = {'removeProjectAccess': {'project': '101'}} - self.run_command('type-access-remove 3 101') - self.assert_called('POST', '/types/3/action', body=expected) - - def test_list_filter_by_project_id(self): - aliases = ['--project-id', '--project_id'] - for alias in aliases: - for separator in self.separators: - self.run_command('list ' + alias + separator + 'fake_id') - self.assert_called('GET', '/shares/detail?project_id=fake_id') - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_list_with_public_shares(self): - listed_fields = [ - 'ID', - 'Name', - 'Size', - 'Share Proto', - 'Status', - 'Is Public', - 'Share Type Name', - 'Host', - 'Availability Zone', - 'Project ID', - ] - self.run_command('list --public') - self.assert_called('GET', '/shares/detail?is_public=True') - cliutils.print_list.assert_called_with( - mock.ANY, listed_fields, sortby_index=None - ) - - def test_show(self): - self.run_command('show 1234') - self.assert_called_anytime('GET', '/shares/1234') - - def test_share_export_location_list(self): - self.run_command('share-export-location-list 1234') - self.assert_called_anytime('GET', '/shares/1234/export_locations') - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_export_location_list_with_columns(self): - self.run_command('share-export-location-list 1234 --columns uuid,path') - - self.assert_called_anytime('GET', '/shares/1234/export_locations') - cliutils.print_list.assert_called_once_with(mock.ANY, ['Uuid', 'Path']) - - def test_share_export_location_show(self): - self.run_command('share-export-location-show 1234 fake_el_uuid') - self.assert_called_anytime( - 'GET', '/shares/1234/export_locations/fake_el_uuid' - ) - - @ddt.data( - { - 'cmd_args': '--driver_options opt1=opt1 opt2=opt2' - ' --share_type fake_share_type', - 'valid_params': { - 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, - 'share_type': 'fake_share_type', - 'share_server_id': None, - }, - }, - { - 'cmd_args': '--share_type fake_share_type', - 'valid_params': { - 'driver_options': {}, - 'share_type': 'fake_share_type', - 'share_server_id': None, - }, - }, - { - 'cmd_args': '', - 'valid_params': { - 'driver_options': {}, - 'share_type': None, - 'share_server_id': None, - }, - }, - { - 'cmd_args': '--public --wait', - 'valid_params': { - 'driver_options': {}, - 'share_type': None, - 'share_server_id': None, - }, - 'is_public': True, - 'version': '--os-share-api-version 2.8', - }, - { - 'cmd_args': '', - 'valid_params': { - 'driver_options': {}, - 'share_type': None, - 'share_server_id': None, - }, - 'is_public': False, - 'version': '--os-share-api-version 2.8', - }, - { - 'cmd_args': '--driver_options opt1=opt1 opt2=opt2' - ' --share_type fake_share_type' - ' --wait', - 'valid_params': { - 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, - 'share_type': 'fake_share_type', - 'share_server_id': None, - }, - 'version': '--os-share-api-version 2.49', - }, - { - 'cmd_args': '--driver_options opt1=opt1 opt2=opt2' - ' --share_type fake_share_type' - ' --share_server_id fake_server', - 'valid_params': { - 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, - 'share_type': 'fake_share_type', - 'share_server_id': 'fake_server', - }, - 'version': '--os-share-api-version 2.49', - }, - { - 'cmd_args': '--driver_options opt1=opt1 opt2=opt2' - ' --share_type fake_share_type' - ' --share_server_id fake_server' - ' --wait', - 'valid_params': { - 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, - 'share_type': 'fake_share_type', - 'share_server_id': 'fake_server', - }, - }, - ) - @ddt.unpack - def test_manage( - self, cmd_args, valid_params, is_public=False, version=None - ): - share_to_be_managed = shares.Share('fake_share', {'id': 'fake'}) - self.mock_object( - shell_v2, - '_wait_for_resource_status', - mock.Mock(return_value=share_to_be_managed), - ) - - if version is not None: - self.run_command( - version - + ' manage fake_service fake_protocol ' - + ' fake_export_path ' - + cmd_args - ) - else: - self.run_command( - ' manage fake_service fake_protocol ' - + ' fake_export_path ' - + cmd_args - ) - expected = { - 'share': { - 'service_host': 'fake_service', - 'protocol': 'fake_protocol', - 'export_path': 'fake_export_path', - 'name': None, - 'description': None, - 'is_public': is_public, - 'share_server_id': valid_params['share_server_id'], - } - } - expected['share'].update(valid_params) - - self.assert_called('POST', '/shares/manage', body=expected) - - if '--wait' in cmd_args: - shell_v2._wait_for_resource_status.assert_called_once_with( - self.shell.cs, - share_to_be_managed, - resource_type='share', - expected_status='available', - ) - else: - shell_v2._wait_for_resource_status.assert_not_called() - - def test_manage_invalid_param_share_server_id(self): - self.assertRaises( - exceptions.CommandError, - self.run_command, - '--os-share-api-version 2.48' - + ' manage fake_service fake_protocol ' - + ' fake_export_path ' - + ' --driver_options opt1=opt1 opt2=opt2' - + ' --share_type fake_share_type' - + ' --share_server_id fake_server', - ) - - def test_share_server_manage_unsupported_version(self): - self.assertRaises( - exceptions.UnsupportedVersion, - self.run_command, - '--os-share-api-version 2.48 ' - + 'share-server-manage fake_host fake_share_net_id fake_id', - ) - - def test_share_server_manage_invalid_param_subnet_id(self): - self.assertRaises( - exceptions.CommandError, - self.run_command, - '--os-share-api-version 2.49 ' - + 'share-server-manage fake_host fake_share_net_id fake_id ' - + '--share-network-subnet fake_subnet_id', - ) - - @ddt.data( - { - 'driver_args': '--driver_options opt1=opt1 opt2=opt2', - 'valid_params': { - 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, - }, - }, - { - 'driver_args': '--driver_options opt1=opt1 opt2=opt2', - 'subnet_id': 'fake_subnet_1', - 'valid_params': { - 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, - }, - }, - { - 'driver_args': '--driver_options opt1=opt1 opt2=opt2', - 'valid_params': { - 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, - }, - 'version': '2.51', - }, - { - 'driver_args': '--driver_options opt1=opt1 opt2=opt2', - 'subnet_id': 'fake_subnet_1', - 'valid_params': { - 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, - }, - 'version': '2.51', - }, - { - 'driver_args': "", - 'valid_params': {'driver_options': {}}, - 'version': '2.51', - }, - { - 'driver_args': '--driver_options opt1=opt1 opt2=opt2', - 'valid_params': { - 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, - }, - 'version': '2.49', - }, - { - 'driver_args': '', - 'valid_params': { - 'driver_options': {}, - }, - 'network_id': 'fake_network_id', - 'version': '2.49', - }, - { - 'driver_args': "", - 'valid_params': {'driver_options': {}}, - 'version': '2.49', - }, - ) - @ddt.unpack - def test_share_server_manage_wait( - self, - driver_args, - valid_params, - version=None, - network_id=None, - subnet_id=None, - ): - fake_manager = mock.Mock() - subnet_support = version is None or api_versions.APIVersion( - version - ) >= api_versions.APIVersion('2.51') - - network_id = '3456' if network_id is None else network_id - fake_share_network = share_networks.ShareNetwork( - fake_manager, {'id': network_id, 'uuid': network_id} - ) - self.mock_object( - shell_v2, - '_find_share_network', - mock.Mock(return_value=fake_share_network), - ) - fake_share_server = share_servers.ShareServer( - fake_manager, {'id': 'fake'} - ) - self.mock_object( - shell_v2, - '_find_share_server', - mock.Mock(return_value=fake_share_server), - ) - - self.mock_object(shell_v2, '_wait_for_resource_status', mock.Mock()) - command = ( - 'share-server-manage ' - '{host} ' - '{share_network_id} ' - '{identifier} ' - '{driver_args} '.format( - host='fake_host', - share_network_id=fake_share_network.id, - identifier='88-as-23-f3-45', - driver_args=driver_args, - ) - ) - command += f'--share-network-subnet {subnet_id}' if subnet_id else '' - - self.run_command(command, version=version) - - expected = { - 'share_server': { - 'host': 'fake_host', - 'share_network_id': fake_share_network.id, - 'identifier': '88-as-23-f3-45', - 'driver_options': driver_args, - } - } - if subnet_support: - expected['share_server']['share_network_subnet_id'] = subnet_id - expected['share_server'].update(valid_params) - - self.assert_called('POST', '/share-servers/manage', body=expected) - - shell_v2._wait_for_resource_status.assert_has_calls( - [ - mock.call( - self.shell.cs, - fake_share_server, - resource_type='share_server', - expected_status='active', - ) - ] - ) - - @ddt.data( - constants.STATUS_ERROR, - constants.STATUS_ACTIVE, - constants.STATUS_MANAGE_ERROR, - constants.STATUS_UNMANAGE_ERROR, - constants.STATUS_DELETING, - constants.STATUS_CREATING, - ) - def test_share_server_reset_state(self, status): - self.run_command(f'share-server-reset-state 1234 --state {status} ') - expected = {'reset_status': {'status': status}} - self.assert_called('POST', '/share-servers/1234/action', body=expected) - - @ddt.data('--wait', '') - def test_unmanage(self, wait_option): - version = api_versions.APIVersion('2.46') - api = mock.Mock(api_version=version) - manager = shares.ShareManager(api=api) - fake_share = shares.Share( - manager, - { - 'id': 'xyzzyspoon', - 'api_version': version, - 'status': 'available', - }, - ) - share_not_found_error = ( - "ERROR: No share with a name or ID of '%s' exists." - ) - share_not_found_error = exceptions.CommandError( - share_not_found_error % (fake_share.id) - ) - self.mock_object( - shell_v2, - '_find_share', - mock.Mock( - side_effect=( - [fake_share, fake_share, fake_share, share_not_found_error] - ) - ), - ) - self.mock_object( - shares.ShareManager, 'get', mock.Mock(return_value=fake_share) - ) - - self.run_command(f'unmanage {wait_option} xyzzyspoon') - - expected_get_share_calls = 4 if wait_option else 1 - shell_v2._find_share.assert_has_calls( - [mock.call(self.shell.cs, fake_share.id)] - * expected_get_share_calls - ) - uri = f'/shares/{fake_share.id}/action' - api.client.post.assert_called_once_with(uri, body={'unmanage': None}) - - def test_share_server_unmanage(self): - self.run_command('share-server-unmanage 1234') - self.assert_called( - 'POST', - '/share-servers/1234/action', - body={'unmanage': {'force': False}}, - ) - - def test_share_server_unmanage_force(self): - self.run_command('share-server-unmanage 1234 --force') - self.assert_called( - 'POST', - '/share-servers/1234/action', - body={'unmanage': {'force': True}}, - ) - - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_share_server_unmanage_wait(self): - self.run_command('share-server-unmanage 1234 --wait') - - self.assert_called( - 'POST', - '/share-servers/1234/action', - body={'unmanage': {'force': False}}, - pos=-2, - ) - expected_share_server = shell_v2._find_share_server( - self.shell.cs, '1234' - ) - shell_v2._wait_for_resource_status.assert_called_once_with( - self.shell.cs, - expected_share_server, - resource_type='share_server', - expected_status='unmanaged', - ) - - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_share_server_unmanage_wait_with_force(self): - self.run_command('share-server-unmanage 1234 --force --wait') - - self.assert_called( - 'POST', - '/share-servers/1234/action', - body={'unmanage': {'force': True}}, - pos=-2, - ) - expected_share_server = shell_v2._find_share_server( - self.shell.cs, '1234' - ) - shell_v2._wait_for_resource_status.assert_called_once_with( - self.shell.cs, - expected_share_server, - resource_type='share_server', - expected_status='unmanaged', - ) - - @ddt.data( - { - 'cmd_args': '--driver_options opt1=opt1 opt2=opt2', - 'valid_params': { - 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, - }, - }, - { - 'cmd_args': '', - 'valid_params': { - 'driver_options': {}, - }, - }, - ) - @ddt.unpack - @mock.patch.object(shell_v2, '_find_share', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_snapshot_manage(self, cmd_args, valid_params): - share_containing_snapshot = shares.Share('fake_share', {'id': '1234'}) - shell_v2._find_share.return_value = share_containing_snapshot - self.run_command( - 'snapshot-manage fake_share fake_provider_location ' + cmd_args - ) - expected = { - 'snapshot': { - 'share_id': '1234', - 'provider_location': 'fake_provider_location', - 'name': None, - 'description': None, - } - } - expected['snapshot'].update(valid_params) - self.assert_called('POST', '/snapshots/manage', body=expected) - # _wait_for_resource_status should not be triggered - self.assertEqual(0, shell_v2._wait_for_resource_status.call_count) - - @mock.patch.object(shell_v2, '_find_share', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_snapshot_manage_with_wait(self): - share_containing_snapshot = shares.Share('fake_share', {'id': '1234'}) - shell_v2._find_share.return_value = share_containing_snapshot - cmd_args = '--wait --driver_options opt1=opt1 opt2=opt2' - self.run_command( - 'snapshot-manage fake_share fake_provider_location ' + cmd_args - ) - expected = { - 'snapshot': { - 'share_id': '1234', - 'provider_location': 'fake_provider_location', - 'name': None, - 'description': None, - } - } - valid_params = {'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}} - expected['snapshot'].update(valid_params) - self.assert_called('POST', '/snapshots/manage', body=expected) - - shell_v2._find_share.assert_has_calls( - [mock.call(self.shell.cs, 'fake_share')] - ) - self.assertEqual(1, shell_v2._find_share.call_count) - # _wait_for_resource_status should be triggered once - shell_v2._wait_for_resource_status.assert_called_once_with( - self.shell.cs, mock.ANY, 'available', resource_type='snapshot' - ) - - @mock.patch.object(shell_v2, '_find_share', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_snapshot_unmanage(self): - share_containing_snapshot = shares.Share('fake_share', {'id': '1234'}) - shell_v2._find_share.return_value = share_containing_snapshot - self.run_command('snapshot-unmanage 1234') - - self.assert_called( - 'POST', '/snapshots/1234/action', body={'unmanage': None} - ) - self.assertEqual(0, shell_v2._wait_for_resource_status.call_count) - - @mock.patch.object(shell_v2, '_find_share', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_snapshot_unmanage_with_wait(self): - share_containing_snapshot = shares.Share('fake_share', {'id': '1234'}) - shell_v2._find_share.return_value = share_containing_snapshot - self.run_command('snapshot-unmanage 1234 --wait') - - self.assert_called( - 'POST', '/snapshots/1234/action', body={'unmanage': None} - ) - expected_snapshot = shell_v2._find_share_snapshot( - self.shell.cs, '1234' - ) - # _wait_for_resource_status should be trigerred once - shell_v2._wait_for_resource_status.assert_called_once_with( - self.shell.cs, - expected_snapshot, - 'deleted', - resource_type='snapshot', - ) - - @mock.patch.object(shell_v2, '_wait_for_share_status', mock.Mock()) - def test_revert_to_snapshot(self): - fake_share_snapshot = type( - 'FakeShareSnapshot', (object,), {'id': '5678', 'share_id': '1234'} - ) - self.mock_object( - shell_v2, - '_find_share_snapshot', - mock.Mock(return_value=fake_share_snapshot), - ) - - self.run_command('revert-to-snapshot 5678') - - self.assert_called( - 'POST', - '/shares/1234/action', - body={'revert': {'snapshot_id': '5678'}}, - ) - # _wait_for_share_status should not be trigerred - self.assertEqual(0, shell_v2._wait_for_share_status.call_count) - - @mock.patch.object(shell_v2, '_wait_for_share_status', mock.Mock()) - def test_revert_to_snapshot_with_wait(self): - fake_share_snapshot = type( - 'FakeShareSnapshot', (object,), {'id': '5678', 'share_id': '1234'} - ) - self.mock_object( - shell_v2, - '_find_share_snapshot', - mock.Mock(return_value=fake_share_snapshot), - ) - - self.run_command('revert-to-snapshot 5678 --wait') - - self.assert_called( - 'POST', - '/shares/1234/action', - body={'revert': {'snapshot_id': '5678'}}, - ) - # _wait_for_share_status should be trigerred once - shell_v2._wait_for_share_status.assert_called_once_with( - self.shell.cs, mock.ANY - ) - - def test_delete(self): - self.run_command('delete 1234') - self.assert_called('DELETE', '/shares/1234') - - @ddt.data('--group sg1313', '--share-group sg1313', '--share_group sg1313') - @mock.patch.object(shell_v2, '_find_share_group', mock.Mock()) - def test_delete_with_share_group(self, sg_cmd): - fake_sg = type('FakeShareGroup', (object,), {'id': sg_cmd.split()[-1]}) - shell_v2._find_share_group.return_value = fake_sg - - self.run_command(f'delete 1234 {sg_cmd}') - - self.assert_called('DELETE', '/shares/1234?share_group_id=sg1313') - self.assertTrue(shell_v2._find_share_group.called) - - def test_delete_not_found(self): - self.assertRaises( - exceptions.CommandError, self.run_command, 'delete fake-not-found' - ) - - @ddt.data(('share_xyz',), ('share_abc', 'share_xyz')) - def test_delete_wait(self, shares_to_delete): - fake_shares = [ - shares.Share('fake', {'id': share}) for share in shares_to_delete - ] - share_not_found_error = ( - "Delete for share %s failed: No share with " - "a name or ID of '%s' exists." - ) - shares_are_not_found_errors = [ - exceptions.CommandError(share_not_found_error % (share, share)) - for share in shares_to_delete - ] - self.mock_object( - shell_v2, - '_find_share', - mock.Mock(side_effect=(fake_shares + shares_are_not_found_errors)), - ) - - self.run_command('delete {} --wait'.format(' '.join(shares_to_delete))) - - shell_v2._find_share.assert_has_calls( - [mock.call(self.shell.cs, share) for share in shares_to_delete] - ) - for share in fake_shares: - uri = f'/shares/{share.id}' - self.assert_called_anytime('DELETE', uri, clear_callstack=False) - - @ddt.data(('share_xyz',), ('share_abc', 'share_xyz')) - def test_force_delete_wait(self, shares_to_delete): - fake_manager = mock.Mock() - fake_shares = [ - shares.Share(fake_manager, {'id': '1234'}) - for share in shares_to_delete - ] - share_not_found_error = ( - "Delete for share %s failed: No share with " - "a name or ID of '%s' exists." - ) - shares_are_not_found_errors = [ - exceptions.CommandError(share_not_found_error % (share, share)) - for share in shares_to_delete - ] - self.mock_object( - shell_v2, - '_find_share', - mock.Mock(side_effect=(fake_shares + shares_are_not_found_errors)), - ) - self.run_command( - 'force-delete {} --wait'.format(' '.join(shares_to_delete)) - ) - shell_v2._find_share.assert_has_calls( - [mock.call(self.shell.cs, share) for share in shares_to_delete] - ) - fake_manager.force_delete.assert_has_calls( - [mock.call(share) for share in fake_shares] - ) - self.assertEqual( - len(shares_to_delete), fake_manager.force_delete.call_count - ) - - def test_soft_delete(self): - self.run_command('soft-delete 1234') - expected = {'soft_delete': None} - self.assert_called('POST', '/shares/1234/action', body=expected) - - def test_restore(self): - self.run_command('restore 1234') - expected = {'restore': None} - self.assert_called('POST', '/shares/1234/action', body=expected) - - def test_list_snapshots(self): - self.run_command('snapshot-list') - self.assert_called('GET', '/snapshots/detail') - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_snapshot_list_select_column(self): - self.run_command('snapshot-list --columns id,name') - self.assert_called('GET', '/snapshots/detail') - cliutils.print_list.assert_called_once_with( - mock.ANY, ['Id', 'Name'], sortby_index=None - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_list_snapshots_all_tenants_only_key(self): - self.run_command('snapshot-list --all-tenants') - self.assert_called('GET', '/snapshots/detail?all_tenants=1') - cliutils.print_list.assert_called_once_with( - mock.ANY, - ['ID', 'Share ID', 'Status', 'Name', 'Share Size', 'Project ID'], - sortby_index=None, - ) - - def test_list_snapshots_all_tenants_key_and_value_1(self): - for separator in self.separators: - self.run_command('snapshot-list --all-tenants' + separator + '1') - self.assert_called('GET', '/snapshots/detail?all_tenants=1') - - def test_list_snapshots_all_tenants_key_and_value_0(self): - for separator in self.separators: - self.run_command('snapshot-list --all-tenants' + separator + '0') - self.assert_called('GET', '/snapshots/detail') - - def test_list_snapshots_filter_by_name(self): - for separator in self.separators: - self.run_command('snapshot-list --name' + separator + '1234') - self.assert_called('GET', '/snapshots/detail?name=1234') - - def test_list_snapshots_filter_by_status(self): - for separator in self.separators: - self.run_command('snapshot-list --status' + separator + '1234') - self.assert_called('GET', '/snapshots/detail?status=1234') - - def test_list_snapshots_filter_by_share_id(self): - aliases = ['--share_id', '--share-id'] - for alias in aliases: - for separator in self.separators: - self.run_command('snapshot-list ' + alias + separator + '1234') - self.assert_called('GET', '/snapshots/detail?share_id=1234') - - def test_list_snapshots_only_used(self): - for separator in self.separators: - self.run_command('snapshot-list --usage' + separator + 'used') - self.assert_called('GET', '/snapshots/detail?usage=used') - - def test_list_snapshots_only_unused(self): - for separator in self.separators: - self.run_command('snapshot-list --usage' + separator + 'unused') - self.assert_called('GET', '/snapshots/detail?usage=unused') - - def test_list_snapshots_any(self): - for separator in self.separators: - self.run_command('snapshot-list --usage' + separator + 'any') - self.assert_called('GET', '/snapshots/detail?usage=any') - - def test_list_snapshots_with_limit(self): - for separator in self.separators: - self.run_command('snapshot-list --limit' + separator + '50') - self.assert_called('GET', '/snapshots/detail?limit=50') - - def test_list_snapshots_with_offset(self): - for separator in self.separators: - self.run_command('snapshot-list --offset' + separator + '50') - self.assert_called('GET', '/snapshots/detail?offset=50') - - def test_list_snapshots_filter_by_inexact_name(self): - for separator in self.separators: - self.run_command('snapshot-list --name~' + separator + 'fake_name') - self.assert_called('GET', '/snapshots/detail?name~=fake_name') - - def test_list_snapshots_filter_by_inexact_description(self): - for separator in self.separators: - self.run_command( - 'snapshot-list --description~' + separator + 'fake_description' - ) - self.assert_called( - 'GET', '/snapshots/detail?description~=fake_description' - ) - - def test_list_snapshots_filter_by_inexact_unicode_name(self): - for separator in self.separators: - self.run_command('snapshot-list --name~' + separator + 'ффф') - self.assert_called( - 'GET', '/snapshots/detail?name~=%D1%84%D1%84%D1%84' - ) - - def test_list_snapshots_filter_by_inexact_unicode_description(self): - for separator in self.separators: - self.run_command( - 'snapshot-list --description~' + separator + 'ффф' - ) - self.assert_called( - 'GET', '/snapshots/detail?description~=%D1%84%D1%84%D1%84' - ) - - def test_list_snapshots_with_sort_dir_verify_keys(self): - aliases = ['--sort_dir', '--sort-dir'] - for alias in aliases: - for key in constants.SORT_DIR_VALUES: - for separator in self.separators: - self.run_command( - 'snapshot-list ' + alias + separator + key - ) - self.assert_called( - 'GET', '/snapshots/detail?sort_dir=' + key - ) - - def test_list_snapshots_with_fake_sort_dir(self): - self.assertRaises( - ValueError, - self.run_command, - 'snapshot-list --sort-dir fake_sort_dir', - ) - - def test_list_snapshots_with_sort_key_verify_keys(self): - aliases = ['--sort_key', '--sort-key'] - for alias in aliases: - for key in constants.SNAPSHOT_SORT_KEY_VALUES: - for separator in self.separators: - self.run_command( - 'snapshot-list ' + alias + separator + key - ) - self.assert_called( - 'GET', '/snapshots/detail?sort_key=' + key - ) - - def test_list_snapshots_with_fake_sort_key(self): - self.assertRaises( - ValueError, - self.run_command, - 'snapshot-list --sort-key fake_sort_key', - ) - - @ddt.data('True', 'False') - def test_list_snapshots_filter_with_count(self, value): - except_url = '/snapshots/detail?with_count=' + value - if value == 'False': - except_url = '/snapshots/detail' - - for separator in self.separators: - self.run_command('snapshot-list --count' + separator + value) - self.assert_called('GET', except_url) - - @ddt.data('True', 'False') - def test_list_snapshots_filter_with_count_invalid_version(self, value): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'snapshot-list --count ' + value, - version='2.78', - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_extra_specs_list(self): - self.run_command('extra-specs-list') - - self.assert_called('GET', '/types?is_public=all') - cliutils.print_list.assert_called_once_with( - mock.ANY, ['ID', 'Name', 'all_extra_specs'], mock.ANY - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_extra_specs_list_select_column(self): - self.run_command('extra-specs-list --columns id,name') - - self.assert_called('GET', '/types?is_public=all') - cliutils.print_list.assert_called_once_with( - mock.ANY, ['id', 'name'], mock.ANY - ) - - @ddt.data('fake', 'FFFalse', 'trueee') - def test_type_create_invalid_dhss_value(self, value): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'type-create test ' + value, - ) - - @ddt.data('True', 'False') - def test_type_create_duplicate_dhss(self, value): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'type-create test ' - + value - + ' --extra-specs driver_handles_share_servers=' - + value, - ) - - @ddt.data( - *itertools.product( - ['snapshot_support', 'create_share_from_snapshot_support'], - ['True', 'False'], - ) - ) - @ddt.unpack - def test_type_create_duplicate_switch_and_extra_spec(self, key, value): - cmd = ( - f'type-create test True --{key} {value} --extra-specs ' - f'{key}={value}' - ) - - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_type_create_duplicate_extra_spec_key(self): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'type-create test True --extra-specs a=foo1 a=foo2', - ) - - @ddt.unpack - @ddt.data( - {'expected_bool': True, 'text': 'true'}, - {'expected_bool': True, 'text': '1'}, - {'expected_bool': False, 'text': 'false'}, - {'expected_bool': False, 'text': '0'}, - ) - def test_type_create(self, expected_bool, text): - expected = { - "share_type": { - "name": "test", - "share_type_access:is_public": True, - "extra_specs": { - "driver_handles_share_servers": expected_bool, - }, - } - } - - self.run_command('type-create test ' + text) - - self.assert_called('POST', '/types', body=expected) - - def test_type_create_with_description(self): - expected = { - "share_type": { - "name": "test", - "description": "test_description", - "share_type_access:is_public": True, - "extra_specs": { - "driver_handles_share_servers": False, - }, - } - } - self.run_command( - 'type-create test false --description test_description', - version='2.41', - ) - - self.assert_called('POST', '/types', body=expected) - - @ddt.data('2.26', '2.40') - def test_type_create_invalid_description_version(self, version): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'type-create test false --description test_description', - version=version, - ) - - @ddt.unpack - @ddt.data( - *( - [ - {'expected_bool': True, 'text': v} - for v in ('true', 'True', '1', 'TRUE', 'tRuE') - ] - + [ - {'expected_bool': False, 'text': v} - for v in ('false', 'False', '0', 'FALSE', 'fAlSe') - ] - ) - ) - def test_type_create_with_snapshot_support(self, expected_bool, text): - expected = { - "share_type": { - "name": "test", - "share_type_access:is_public": True, - "extra_specs": { - "snapshot_support": expected_bool, - "driver_handles_share_servers": False, - }, - } - } - self.run_command('type-create test false --snapshot-support ' + text) - - self.assert_called('POST', '/types', body=expected) - - @ddt.unpack - @ddt.data( - { - 'expected_bool': True, - 'snapshot_text': 'true', - 'replication_type': 'readable', - }, - { - 'expected_bool': False, - 'snapshot_text': 'false', - 'replication_type': 'writable', - }, - ) - def test_create_with_extra_specs( - self, expected_bool, snapshot_text, replication_type - ): - expected = { - "share_type": { - "name": "test", - "share_type_access:is_public": True, - "extra_specs": { - "driver_handles_share_servers": False, - "snapshot_support": expected_bool, - "replication_type": replication_type, - }, - } - } - - self.run_command( - 'type-create test false --extra-specs' - ' snapshot_support=' - + snapshot_text - + ' replication_type=' - + replication_type - ) - - self.assert_called('POST', '/types', body=expected) - - @ddt.unpack - @ddt.data( - *( - [ - {'expected_bool': True, 'text': v} - for v in ('true', 'True', '1', 'TRUE', 'tRuE') - ] - + [ - {'expected_bool': False, 'text': v} - for v in ('false', 'False', '0', 'FALSE', 'fAlSe') - ] - ) - ) - def test_type_create_with_create_share_from_snapshot_support( - self, expected_bool, text - ): - expected = { - "share_type": { - "name": "test", - "share_type_access:is_public": True, - "extra_specs": { - "driver_handles_share_servers": False, - "snapshot_support": True, - "create_share_from_snapshot_support": expected_bool, - }, - } - } - - self.run_command( - 'type-create test false --snapshot-support true ' - '--create-share-from-snapshot-support ' + text - ) - - self.assert_called('POST', '/types', body=expected) - - @ddt.data('snapshot_support', 'create_share_from_snapshot_support') - def test_type_create_invalid_switch_value(self, value): - self.assertRaises( - exceptions.CommandError, - self.run_command, - f'type-create test false --{value} fake', - ) - - @ddt.data('snapshot_support', 'create_share_from_snapshot_support') - def test_type_create_invalid_extra_spec_value(self, value): - self.assertRaises( - exceptions.CommandError, - self.run_command, - f'type-create test false --extra-specs {value}=fake', - ) - - @ddt.unpack - @ddt.data( - *( - [ - {'expected_bool': True, 'text': v} - for v in ('true', 'True', '1', 'TRUE', 'tRuE') - ] - + [ - {'expected_bool': False, 'text': v} - for v in ('false', 'False', '0', 'FALSE', 'fAlSe') - ] - ) - ) - def test_type_create_with_revert_to_snapshot_support( - self, expected_bool, text - ): - expected = { - "share_type": { - "name": "test", - "share_type_access:is_public": True, - "extra_specs": { - "driver_handles_share_servers": False, - "snapshot_support": True, - "revert_to_snapshot_support": expected_bool, - }, - } - } - - self.run_command( - 'type-create test false --snapshot-support true ' - '--revert-to-snapshot-support ' + text - ) - - self.assert_called('POST', '/types', body=expected) - - @ddt.data('fake', 'FFFalse', 'trueee') - def test_type_create_invalid_revert_to_snapshot_support_value(self, value): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'type-create test false --revert-to-snapshot-support ' + value, - ) - - @ddt.unpack - @ddt.data( - *( - [ - {'expected_bool': True, 'text': v} - for v in ('true', 'True', '1', 'TRUE', 'tRuE') - ] - + [ - {'expected_bool': False, 'text': v} - for v in ('false', 'False', '0', 'FALSE', 'fAlSe') - ] - ) - ) - def test_type_create_with_mount_snapshot_support( - self, expected_bool, text - ): - expected = { - "share_type": { - "name": "test", - "share_type_access:is_public": True, - "extra_specs": { - "driver_handles_share_servers": False, - "snapshot_support": True, - "revert_to_snapshot_support": False, - "mount_snapshot_support": expected_bool, - }, - } - } - - self.run_command( - 'type-create test false --snapshot-support true ' - '--revert-to-snapshot-support false ' - '--mount-snapshot-support ' + text - ) - - self.assert_called('POST', '/types', body=expected) - - @ddt.data('fake', 'FFFalse', 'trueee') - def test_type_create_invalid_mount_snapshot_support_value(self, value): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'type-create test false --mount-snapshot-support ' + value, - ) - - @ddt.data('--is-public', '--is_public') - def test_update(self, alias): - # basic rename with positional arguments - self.run_command('update 1234 --name new-name') - expected = {'share': {'display_name': 'new-name'}} - self.assert_called('PUT', '/shares/1234', body=expected) - # change description only - self.run_command('update 1234 --description=new-description') - expected = {'share': {'display_description': 'new-description'}} - self.assert_called('PUT', '/shares/1234', body=expected) - # update is_public attr - valid_is_public_values = strutils.TRUE_STRINGS + strutils.FALSE_STRINGS - for is_public in valid_is_public_values: - self.run_command(f'update 1234 {alias} {is_public}') - expected = { - 'share': { - 'is_public': strutils.bool_from_string( - is_public, strict=True - ), - }, - } - self.assert_called('PUT', '/shares/1234', body=expected) - for invalid_val in ['truebar', 'bartrue']: - self.assertRaises( - ValueError, - self.run_command, - f'update 1234 {alias} {invalid_val}', - ) - # update all attributes - self.run_command( - 'update 1234 --name new-name ' - '--description=new-description ' - f'{alias} True' - ) - expected = { - 'share': { - 'display_name': 'new-name', - 'display_description': 'new-description', - 'is_public': True, - } - } - self.assert_called('PUT', '/shares/1234', body=expected) - self.assertRaises( - exceptions.CommandError, self.run_command, 'update 1234' - ) - - def test_rename_snapshot(self): - # basic rename with positional arguments - self.run_command('snapshot-rename 1234 new-name') - expected = {'snapshot': {'display_name': 'new-name'}} - self.assert_called('PUT', '/snapshots/1234', body=expected) - # change description only - self.run_command('snapshot-rename 1234 --description=new-description') - expected = {'snapshot': {'display_description': 'new-description'}} - - self.assert_called('PUT', '/snapshots/1234', body=expected) - # snapshot-rename and change description - self.run_command( - 'snapshot-rename 1234 new-name --description=new-description' - ) - expected = { - 'snapshot': { - 'display_name': 'new-name', - 'display_description': 'new-description', - } - } - self.assert_called('PUT', '/snapshots/1234', body=expected) - # noop, the only all will be the lookup - self.assertRaises( - exceptions.CommandError, self.run_command, 'snapshot-rename 1234' - ) - - def test_set_metadata_set(self): - self.run_command('metadata 1234 set key1=val1 key2=val2') - self.assert_called( - 'POST', - '/shares/1234/metadata', - {'metadata': {'key1': 'val1', 'key2': 'val2'}}, - ) - - def test_set_metadata_delete_dict(self): - self.run_command('metadata 1234 unset key1=val1 key2=val2') - self.assert_called('DELETE', '/shares/1234/metadata/key1') - self.assert_called('DELETE', '/shares/1234/metadata/key2', pos=-2) - - def test_set_metadata_delete_keys(self): - self.run_command('metadata 1234 unset key1 key2') - self.assert_called('DELETE', '/shares/1234/metadata/key1') - self.assert_called('DELETE', '/shares/1234/metadata/key2', pos=-2) - - def test_share_metadata_update_all(self): - self.run_command('metadata-update-all 1234 key1=val1 key2=val2') - self.assert_called( - 'PUT', - '/shares/1234/metadata', - {'metadata': {'key1': 'val1', 'key2': 'val2'}}, - ) - - def test_extract_metadata(self): - # mimic the result of argparse's parse_args() method - class Arguments: - def __init__(self, metadata=None): - if metadata is None: - metadata = [] - self.metadata = metadata - - inputs = [ - ([], {}), - (["key=value"], {"key": "value"}), - (["key"], {"key": None}), - (["k1=v1", "k2=v2"], {"k1": "v1", "k2": "v2"}), - (["k1=v1", "k2"], {"k1": "v1", "k2": None}), - (["k1", "k2=v2"], {"k1": None, "k2": "v2"}), - ] - - for input in inputs: - args = Arguments(metadata=input[0]) - self.assertEqual(shell_v2._extract_metadata(args), input[1]) - - @ddt.data('--wait', '') - def test_extend_with_wait_option(self, wait_option): - available_share = shares.Share( - 'fake', {'id': '1234', 'status': 'available'} - ) - share_to_extend = shares.Share( - 'fake', {'id': '1234', 'status': 'extending'} - ) - fake_shares = [ - available_share, - share_to_extend, - share_to_extend, - available_share, - ] - self.mock_object( - shell_v2, '_find_share', mock.Mock(side_effect=fake_shares) - ) - expected_extend_body = {'extend': {'new_size': 77}} - self.run_command(f'extend 1234 77 {wait_option}') - self.assert_called_anytime( - 'POST', - '/shares/1234/action', - body=expected_extend_body, - clear_callstack=False, - ) - if wait_option: - shell_v2._find_share.assert_has_calls( - [mock.call(self.shell.cs, '1234')] * 4 - ) - self.assertEqual(4, shell_v2._find_share.call_count) - else: - shell_v2._find_share.assert_called_with(self.shell.cs, '1234') - self.assertEqual(2, shell_v2._find_share.call_count) - - def test_reset_state(self): - self.run_command('reset-state 1234') - expected = {'reset_status': {'status': 'available'}} - self.assert_called('POST', '/shares/1234/action', body=expected) - - @ddt.data('--wait', '') - def test_shrink_with_wait_option(self, wait_option): - available_share = shares.Share( - 'fake', {'id': '1234', 'status': 'available'} - ) - share_to_shrink = shares.Share( - 'fake', {'id': '1234', 'status': 'shrinking'} - ) - fake_shares = [ - available_share, - share_to_shrink, - share_to_shrink, - available_share, - ] - self.mock_object( - shell_v2, '_find_share', mock.Mock(side_effect=fake_shares) - ) - expected_shrink_body = {'shrink': {'new_size': 77}} - self.run_command(f'shrink 1234 77 {wait_option}') - self.assert_called_anytime( - 'POST', - '/shares/1234/action', - body=expected_shrink_body, - clear_callstack=False, - ) - if wait_option: - shell_v2._find_share.assert_has_calls( - [mock.call(self.shell.cs, '1234')] * 4 - ) - self.assertEqual(4, shell_v2._find_share.call_count) - else: - shell_v2._find_share.assert_called_with(self.shell.cs, '1234') - self.assertEqual(2, shell_v2._find_share.call_count) - - def test_reset_state_with_flag(self): - self.run_command('reset-state --state error 1234') - expected = {'reset_status': {'status': 'error'}} - self.assert_called('POST', '/shares/1234/action', body=expected) - - def test_snapshot_reset_state(self): - self.run_command('snapshot-reset-state 1234') - expected = {'reset_status': {'status': 'available'}} - self.assert_called('POST', '/snapshots/1234/action', body=expected) - - def test_snapshot_reset_state_with_flag(self): - self.run_command('snapshot-reset-state --state error 1234') - expected = {'reset_status': {'status': 'error'}} - self.assert_called('POST', '/snapshots/1234/action', body=expected) - - @ddt.data( - {}, - {'--name': 'fake_name'}, - {'--description': 'fake_description'}, - {'--neutron_net_id': 'fake_neutron_net_id'}, - {'--neutron_subnet_id': 'fake_neutron_subnet_id'}, - { - '--description': 'fake_description', - '--name': 'fake_name', - '--neutron_net_id': 'fake_neutron_net_id', - '--neutron_subnet_id': 'fake_neutron_subnet_id', - }, - ) - def test_share_network_create(self, data): - cmd = 'share-network-create' - for k, v in data.items(): - cmd += ' ' + k + ' ' + v - self.run_command(cmd) - - self.assert_called('POST', '/share-networks') - - @ddt.unpack - @ddt.data( - {'data': {'--name': 'fake_name'}}, - {'data': {'--description': 'fake_description'}}, - { - 'data': {'--neutron_net_id': 'fake_neutron_net_id'}, - 'version': '2.49', - }, - { - 'data': {'--neutron_subnet_id': 'fake_neutron_subnet_id'}, - 'version': '2.49', - }, - { - 'data': { - '--description': 'fake_description', - '--name': 'fake_name', - '--neutron_net_id': 'fake_neutron_net_id', - '--neutron_subnet_id': 'fake_neutron_subnet_id', - }, - 'version': '2.49', - }, - {'data': {'--name': '""'}}, - {'data': {'--description': '""'}}, - { - 'data': {'--neutron_net_id': '""'}, - 'version': '2.49', - }, - { - 'data': {'--neutron_subnet_id': '""'}, - 'version': '2.49', - }, - { - 'data': { - '--description': '""', - '--name': '""', - '--neutron_net_id': '""', - '--neutron_subnet_id': '""', - }, - 'version': '2.49', - }, - ) - def test_share_network_update(self, data, version=None): - cmd = 'share-network-update 1111' - expected = dict() - for k, v in data.items(): - cmd += ' ' + k + ' ' + v - expected[k[2:]] = v - expected = dict(share_network=expected) - - self.run_command(cmd, version=version) - - self.assert_called('PUT', '/share-networks/1111', body=expected) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_network_list(self): - self.run_command('share-network-list') - self.assert_called( - 'GET', - '/share-networks/detail', - ) - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['id', 'name'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_network_list_select_column(self): - self.run_command('share-network-list --columns id') - self.assert_called( - 'GET', - '/share-networks/detail', - ) - cliutils.print_list.assert_called_once_with(mock.ANY, fields=['Id']) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_network_list_all_tenants(self): - self.run_command('share-network-list --all-tenants') - self.assert_called( - 'GET', - '/share-networks/detail?all_tenants=1', - ) - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['id', 'name'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - @mock.patch.object(shell_v2, '_find_security_service', mock.Mock()) - def test_share_network_list_filter_by_security_service(self): - ss = type('FakeSecurityService', (object,), {'id': 'fake-ss-id'}) - shell_v2._find_security_service.return_value = ss - for command in ['--security_service', '--security-service']: - self.run_command(f'share-network-list {command} {ss.id}') - self.assert_called( - 'GET', - f'/share-networks/detail?security_service_id={ss.id}', - ) - shell_v2._find_security_service.assert_called_with(mock.ANY, ss.id) - cliutils.print_list.assert_called_with( - mock.ANY, fields=['id', 'name'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_network_list_project_id_aliases(self): - for command in ['--project-id', '--project_id']: - self.run_command(f'share-network-list {command} 1234') - self.assert_called( - 'GET', - '/share-networks/detail?project_id=1234', - ) - cliutils.print_list.assert_called_with( - mock.ANY, fields=['id', 'name'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_network_list_created_before_aliases(self): - for command in ['--created-before', '--created_before']: - self.run_command(f'share-network-list {command} 2001-01-01') - self.assert_called( - 'GET', - '/share-networks/detail?created_before=2001-01-01', - ) - cliutils.print_list.assert_called_with( - mock.ANY, fields=['id', 'name'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_network_list_created_since_aliases(self): - for command in ['--created-since', '--created_since']: - self.run_command(f'share-network-list {command} 2001-01-01') - self.assert_called( - 'GET', - '/share-networks/detail?created_since=2001-01-01', - ) - cliutils.print_list.assert_called_with( - mock.ANY, fields=['id', 'name'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_network_list_neutron_net_id_aliases(self): - for command in [ - '--neutron-net-id', - '--neutron-net_id', - '--neutron_net-id', - '--neutron_net_id', - ]: - self.run_command(f'share-network-list {command} fake-id') - self.assert_called( - 'GET', - '/share-networks/detail?neutron_net_id=fake-id', - ) - cliutils.print_list.assert_called_with( - mock.ANY, fields=['id', 'name'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_network_list_neutron_subnet_id_aliases(self): - for command in [ - '--neutron-subnet-id', - '--neutron-subnet_id', - '--neutron_subnet-id', - '--neutron_subnet_id', - ]: - self.run_command(f'share-network-list {command} fake-id') - self.assert_called( - 'GET', - '/share-networks/detail?neutron_subnet_id=fake-id', - ) - cliutils.print_list.assert_called_with( - mock.ANY, fields=['id', 'name'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_network_list_network_type_aliases(self): - for command in ['--network_type', '--network-type']: - self.run_command(f'share-network-list {command} local') - self.assert_called( - 'GET', - '/share-networks/detail?network_type=local', - ) - cliutils.print_list.assert_called_with( - mock.ANY, fields=['id', 'name'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_network_list_segmentation_id_aliases(self): - for command in ['--segmentation-id', '--segmentation_id']: - self.run_command(f'share-network-list {command} 1234') - self.assert_called( - 'GET', - '/share-networks/detail?segmentation_id=1234', - ) - cliutils.print_list.assert_called_with( - mock.ANY, fields=['id', 'name'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_network_list_ip_version_aliases(self): - for command in ['--ip-version', '--ip_version']: - self.run_command(f'share-network-list {command} 4') - self.assert_called( - 'GET', - '/share-networks/detail?ip_version=4', - ) - cliutils.print_list.assert_called_with( - mock.ANY, fields=['id', 'name'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_network_list_all_filters(self): - filters = { - 'name': 'fake-name', - 'project-id': '1234', - 'created-since': '2001-01-01', - 'created-before': '2002-02-02', - 'neutron-net-id': 'fake-net', - 'neutron-subnet-id': 'fake-subnet', - 'network-type': 'local', - 'segmentation-id': '5678', - 'cidr': 'fake-cidr', - 'ip-version': '4', - 'offset': 10, - 'limit': 20, - } - command_str = 'share-network-list' - for key, value in filters.items(): - command_str += f' --{key}={value}' - self.run_command(command_str) - query = utils.safe_urlencode( - sorted([(k.replace('-', '_'), v) for (k, v) in filters.items()]) - ) - self.assert_called( - 'GET', - f'/share-networks/detail?{query}', - ) - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['id', 'name'] - ) - - def test_share_network_list_filter_by_inexact_name(self): - for separator in self.separators: - self.run_command( - 'share-network-list --name~' + separator + 'fake_name' - ) - self.assert_called('GET', '/share-networks/detail?name~=fake_name') - - def test_share_network_list_filter_by_inexact_description(self): - for separator in self.separators: - self.run_command( - 'share-network-list --description~' - + separator - + 'fake_description' - ) - self.assert_called( - 'GET', '/share-networks/detail?description~=fake_description' - ) - - def test_share_network_list_filter_by_inexact_unicode_name(self): - for separator in self.separators: - self.run_command('share-network-list --name~' + separator + 'ффф') - self.assert_called( - 'GET', '/share-networks/detail?name~=%D1%84%D1%84%D1%84' - ) - - def test_share_network_list_filter_by_inexact_unicode_description(self): - for separator in self.separators: - self.run_command( - 'share-network-list --description~' + separator + 'ффф' - ) - self.assert_called( - 'GET', '/share-networks/detail?description~=%D1%84%D1%84%D1%84' - ) - - def test_share_network_security_service_add(self): - self.run_command( - 'share-network-security-service-add fake_share_nw ' - 'fake_security_service' - ) - self.assert_called( - 'POST', - '/share-networks/1234/action', - ) - - def test_share_network_security_service_remove(self): - self.run_command( - 'share-network-security-service-remove fake_share_nw ' - 'fake_security_service' - ) - self.assert_called( - 'POST', - '/share-networks/1234/action', - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_network_security_service_list_select_column(self): - self.run_command( - 'share-network-security-service-list ' - 'fake_share_nw --column id,name' - ) - self.assert_called( - 'GET', - '/security-services/detail?share_network_id=1234', - ) - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['Id', 'Name'] - ) - - def test_share_network_security_service_list_by_name(self): - self.run_command('share-network-security-service-list fake_share_nw') - self.assert_called( - 'GET', - '/security-services/detail?share_network_id=1234', - ) - - def test_share_network_security_service_list_by_name_not_found(self): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'share-network-security-service-list inexistent_share_nw', - ) - - def test_share_network_security_service_list_by_name_multiple(self): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'share-network-security-service-list duplicated_name', - ) - - def test_share_network_security_service_list_by_id(self): - self.run_command('share-network-security-service-list 1111') - self.assert_called( - 'GET', - '/security-services/detail?share_network_id=1111', - ) - - @ddt.data( - {}, - { - '--neutron_net_id': 'fake_neutron_net_id', - '--neutron_subnet_id': 'fake_neutron_subnet_id', - }, - {'--availability-zone': 'fake_availability_zone_id'}, - { - '--neutron_net_id': 'fake_neutron_net_id', - '--neutron_subnet_id': 'fake_neutron_subnet_id', - '--availability-zone': 'fake_availability_zone_id', - }, - ) - def test_share_network_subnet_add(self, data): - fake_share_network = type( - 'FakeShareNetwork', (object,), {'id': '1234'} - ) - self.mock_object( - shell_v2, - '_find_share_network', - mock.Mock(return_value=fake_share_network), - ) - - cmd = 'share-network-subnet-create' - for k, v in data.items(): - cmd += ' ' + k + ' ' + v - cmd += ' ' + fake_share_network.id - self.run_command(cmd) - - shell_v2._find_share_network.assert_called_once_with( - mock.ANY, fake_share_network.id - ) - self.assert_called('POST', '/share-networks/1234/subnets') - - @ddt.data( - {'--neutron_net_id': 'fake_neutron_net_id'}, - {'--neutron_subnet_id': 'fake_neutron_subnet_id'}, - { - '--neutron_net_id': 'fake_neutron_net_id', - '--availability-zone': 'fake_availability_zone_id', - }, - { - '--neutron_subnet_id': 'fake_neutron_subnet_id', - '--availability-zone': 'fake_availability_zone_id', - }, - ) - def test_share_network_subnet_add_invalid_param(self, data): - cmd = 'share-network-subnet-create' - for k, v in data.items(): - cmd += ' ' + k + ' ' + v - cmd += ' fake_network_id' - - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_share_network_subnet_add_invalid_share_network(self): - cmd = 'share-network-subnet-create not-found-id' - - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - @ddt.data(('fake_subnet1',), ('fake_subnet1', 'fake_subnet2')) - def test_share_network_subnet_delete(self, subnet_ids): - fake_share_network = type( - 'FakeShareNetwork', (object,), {'id': '1234'} - ) - self.mock_object( - shell_v2, - '_find_share_network', - mock.Mock(return_value=fake_share_network), - ) - fake_share_network_subnets = [ - share_network_subnets.ShareNetworkSubnet( - 'fake', {'id': subnet_id}, True - ) - for subnet_id in subnet_ids - ] - - self.run_command( - 'share-network-subnet-delete {network_id} {subnet_ids}'.format( - network_id=fake_share_network.id, - subnet_ids=' '.join(subnet_ids), - ) - ) - - shell_v2._find_share_network.assert_called_once_with( - mock.ANY, fake_share_network.id - ) - for subnet in fake_share_network_subnets: - self.assert_called_anytime( - 'DELETE', - f'/share-networks/1234/subnets/{subnet.id}', - clear_callstack=False, - ) - - def test_share_network_subnet_delete_invalid_share_network(self): - command = 'share-network-subnet-delete {net_id} {subnet_id}'.format( - net_id='not-found-id', - subnet_id='1234', - ) - - self.assertRaises(exceptions.CommandError, self.run_command, command) - - def test_share_network_subnet_delete_invalid_share_network_subnet(self): - fake_share_network = type( - 'FakeShareNetwork', (object,), {'id': '1234'} - ) - self.mock_object( - shell_v2, - '_find_share_network', - mock.Mock(return_value=fake_share_network), - ) - command = 'share-network-subnet-delete {net_id} {subnet_id}'.format( - net_id=fake_share_network.id, - subnet_id='not-found-id', - ) - - self.assertRaises(exceptions.CommandError, self.run_command, command) - - @mock.patch.object(cliutils, 'print_dict', mock.Mock()) - def test_share_network_subnet_show(self): - fake_share_network = type( - 'FakeShareNetwork', (object,), {'id': '1234'} - ) - self.mock_object( - shell_v2, - '_find_share_network', - mock.Mock(return_value=fake_share_network), - ) - args = { - 'share_net_id': fake_share_network.id, - 'subnet_id': 'fake_subnet_id', - } - - self.run_command( - 'share-network-subnet-show {share_net_id} {subnet_id}'.format( - **args - ) - ) - - self.assert_called( - 'GET', - '/share-networks/{share_net_id}/subnets/{subnet_id}'.format( - **args - ), - ) - cliutils.print_dict.assert_called_once_with(mock.ANY) - - def test_share_network_subnet_show_invalid_share_network(self): - command = 'share-network-subnet-show {net_id} {subnet_id}'.format( - net_id='not-found-id', - subnet_id=1234, - ) - - self.assertRaises(exceptions.CommandError, self.run_command, command) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_server_list_select_column(self): - self.run_command('share-server-list --columns id,host,status') - self.assert_called('GET', '/share-servers') - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['Id', 'Host', 'Status'] - ) - - def test_create_share(self): - # Use only required fields - expected = self.create_share_body.copy() - expected['share']['share_type'] = 'test_type' - self.run_command("create nfs 1 --share-type test_type") - self.assert_called("POST", "/shares", body=expected) - - def test_create_public_share(self): - expected = self.create_share_body.copy() - expected['share']['is_public'] = True - expected['share']['share_type'] = 'test_type' - self.run_command("create --public nfs 1 --share-type test_type") - self.assert_called("POST", "/shares", body=expected) - - def test_create_with_share_network(self): - # Except required fields added share network - sn = "fake-share-network" - with mock.patch.object( - shell_v2, "_find_share_network", mock.Mock(return_value=sn) - ): - self.run_command( - f"create nfs 1 --share-type test_type --share-network {sn}" - ) - expected = self.create_share_body.copy() - expected['share']['share_network_id'] = sn - expected['share']['share_type'] = 'test_type' - self.assert_called("POST", "/shares", body=expected) - shell_v2._find_share_network.assert_called_once_with(mock.ANY, sn) - - def test_create_with_metadata(self): - # Except required fields added metadata - self.run_command( - "create nfs 1 --metadata key1=value1 key2=value2 " - "--share-type test_type" - ) - expected = self.create_share_body.copy() - expected['share']['metadata'] = {"key1": "value1", "key2": "value2"} - expected['share']['share_type'] = 'test_type' - self.assert_called("POST", "/shares", body=expected) - - def test_create_with_wait(self): - self.run_command("create nfs 1 --wait --share-type test_type") - expected = self.create_share_body.copy() - expected['share']['share_type'] = 'test_type' - self.assert_called_anytime( - "POST", "/shares", body=expected, clear_callstack=False - ) - self.assert_called("GET", "/shares/1234") - - def test_create_share_with_no_existing_share_type(self): - self.assertRaises( - exceptions.CommandError, self.run_command, "create nfs 1" - ) - - @ddt.data('None', 'NONE', 'none') - def test_create_share_with_the_name_none(self, name): - self.assertRaises( - exceptions.CommandError, - self.run_command, - f"create nfs 1 --name {name} --share-type test_type", - ) - - def test_allow_access_cert(self): - self.run_command("access-allow 1234 cert client.example.com") - - expected = { - "allow_access": { - "access_type": "cert", - "access_to": "client.example.com", - } - } - self.assert_called("POST", "/shares/1234/action", body=expected) - - def test_allow_access_cert_error_gt64(self): - common_name = 'x' * 65 - self.assertRaises( - exceptions.CommandError, - self.run_command, - (f"access-allow 1234 cert {common_name}"), - ) - - def test_allow_access_cert_error_zero(self): - cmd = mock.Mock() - cmd.split = mock.Mock( - side_effect=lambda: ['access-allow', '1234', 'cert', ''] - ) - - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - cmd.split.assert_called_once_with() - - def test_allow_access_cert_error_whitespace(self): - cmd = mock.Mock() - cmd.split = mock.Mock( - side_effect=lambda: ['access-allow', '1234', 'cert', ' '] - ) - - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - cmd.split.assert_called_once_with() - - def test_allow_access_with_access_level(self): - aliases = ['--access_level', '--access-level'] - expected = { - "allow_access": { - "access_type": "ip", - "access_to": "10.0.0.6", - "access_level": "ro", - } - } - - for alias in aliases: - for s in self.separators: - self.run_command( - "access-allow " + alias + s + "ro 1111 ip 10.0.0.6" - ) - self.assert_called( - "POST", "/shares/1111/action", body=expected - ) - - def test_allow_access_with_valid_access_levels(self): - expected = { - "allow_access": { - "access_type": "ip", - "access_to": "10.0.0.6", - } - } - - for level in ['rw', 'ro']: - expected["allow_access"]['access_level'] = level - self.run_command( - "access-allow --access-level " + level + " 1111 ip 10.0.0.6" - ) - self.assert_called("POST", "/shares/1111/action", body=expected) - - def test_allow_access_with_invalid_access_level(self): - self.assertRaises( - SystemExit, - self.run_command, - "access-allow --access-level fake 1111 ip 10.0.0.6", - ) - - def test_allow_access_with_metadata(self): - expected = { - "allow_access": { - "access_type": "ip", - "access_to": "10.0.0.6", - "metadata": {"key1": "v1", "key2": "v2"}, - } - } - - self.run_command( - "access-allow 2222 ip 10.0.0.6 --metadata key1=v1 key2=v2", - version="2.45", - ) - self.assert_called("POST", "/shares/2222/action", body=expected) - - def test_set_access_metadata(self): - expected = { - "metadata": { - "key1": "v1", - "key2": "v2", - } - } - self.run_command( - "access-metadata 9999 set key1=v1 key2=v2", version="2.45" - ) - self.assert_called( - "PUT", "/share-access-rules/9999/metadata", body=expected - ) - - def test_unset_access_metadata(self): - self.run_command("access-metadata 9999 unset key1", version="2.45") - self.assert_called("DELETE", "/share-access-rules/9999/metadata/key1") - - @ddt.data("1.0", "2.0", "2.44") - def test_allow_access_with_metadata_not_support_version(self, version): - self.assertRaises( - exceptions.CommandError, - self.run_command, - "access-allow 2222 ip 10.0.0.6 --metadata key1=v1", - version=version, - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - @ddt.data(*set(["2.44", "2.45", api_versions.MAX_VERSION])) - def test_access_list(self, version): - self.run_command("access-list 1111", version=version) - version = api_versions.APIVersion(version) - cliutils.print_list.assert_called_with( - mock.ANY, - [ - 'id', - 'access_type', - 'access_to', - 'access_level', - 'state', - 'access_key', - 'created_at', - 'updated_at', - ], - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - @ddt.data(*set(["2.44", "2.45", api_versions.MAX_VERSION])) - def test_access_list_select_column(self, version): - self.run_command( - "access-list 1111 --columns id,access_type", version=version - ) - cliutils.print_list.assert_called_with(mock.ANY, ['Id', 'Access_Type']) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_snapshot_access_list(self): - self.run_command("snapshot-access-list 1234") - - self.assert_called('GET', '/snapshots/1234/access-list') - cliutils.print_list.assert_called_with( - mock.ANY, ['id', 'access_type', 'access_to', 'state'] - ) - - @mock.patch.object(cliutils, 'print_dict', mock.Mock()) - def test_snapshot_access_allow(self): - self.run_command("snapshot-access-allow 1234 ip 1.1.1.1") - - self.assert_called('POST', '/snapshots/1234/action') - cliutils.print_dict.assert_called_with( - {'access_type': 'ip', 'access_to': '1.1.1.1'} - ) - - @ddt.data(*set(["2.45", api_versions.MAX_VERSION])) - def test_allow_access_wait(self, version): - fake_access_rule = {'id': 'fake_id'} - fake_access = mock.Mock() - fake_access._info = fake_access_rule - fake_share = mock.Mock() - fake_share.name = 'fake_share' - fake_share.allow = mock.Mock(return_value=fake_access_rule) - self.mock_object( - shell_v2, - '_wait_for_resource_status', - mock.Mock(return_value=fake_access), - ) - self.mock_object( - share_access_rules.ShareAccessRuleManager, - 'get', - mock.Mock(return_value=fake_access_rule), - ) - with mock.patch.object( - apiclient_utils, - 'find_resource', - mock.Mock(return_value=fake_share), - ): - is_default_in_api = api_versions.APIVersion( - version - ) >= api_versions.APIVersion('2.45') - if is_default_in_api: - self.run_command( - "access-allow fake_share ip 10.0.0.1 --wait", - version=version, - ) - shell_v2._wait_for_resource_status.assert_has_calls( - [ - mock.call( - self.shell.cs, - fake_access_rule, - resource_type='share_access_rule', - expected_status='active', - status_attr='state', - ) - ] - ) - - def test_snapshot_access_deny(self): - self.run_command("snapshot-access-deny 1234 fake_id") - - self.assert_called('POST', '/snapshots/1234/action') - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_snapshot_export_location_list(self): - self.run_command('snapshot-export-location-list 1234') - - self.assert_called('GET', '/snapshots/1234/export-locations') - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_snapshot_instance_export_location_list(self): - self.run_command('snapshot-instance-export-location-list 1234') - - self.assert_called('GET', '/snapshot-instances/1234/export-locations') - - @mock.patch.object(cliutils, 'print_dict', mock.Mock()) - def test_snapshot_instance_export_location_show(self): - self.run_command( - 'snapshot-instance-export-location-show 1234 fake_el_id' - ) - - self.assert_called( - 'GET', '/snapshot-instances/1234/export-locations/fake_el_id' - ) - cliutils.print_dict.assert_called_once_with( - {'path': '/fake_path', 'id': 'fake_id'} - ) - - @mock.patch.object(cliutils, 'print_dict', mock.Mock()) - def test_snapshot_export_location_show(self): - self.run_command('snapshot-export-location-show 1234 fake_el_id') - - self.assert_called( - 'GET', '/snapshots/1234/export-locations/fake_el_id' - ) - cliutils.print_dict.assert_called_once_with( - {'path': '/fake_path', 'id': 'fake_id'} - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_security_service_list(self): - self.run_command('security-service-list') - self.assert_called( - 'GET', - '/security-services', - ) - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['id', 'name', 'status', 'type'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_security_service_list_select_column(self): - self.run_command('security-service-list --columns name,type') - self.assert_called( - 'GET', - '/security-services', - ) - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['Name', 'Type'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - @mock.patch.object(shell_v2, '_find_share_network', mock.Mock()) - def test_security_service_list_filter_share_network(self): - class FakeShareNetwork: - id = 'fake-sn-id' - - sn = FakeShareNetwork() - shell_v2._find_share_network.return_value = sn - for command in ['--share-network', '--share_network']: - self.run_command(f'security-service-list {command} {sn.id}') - self.assert_called( - 'GET', - f'/security-services?share_network_id={sn.id}', - ) - shell_v2._find_share_network.assert_called_with(mock.ANY, sn.id) - cliutils.print_list.assert_called_with( - mock.ANY, fields=['id', 'name', 'status', 'type'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_security_service_list_detailed(self): - self.run_command('security-service-list --detailed') - self.assert_called( - 'GET', - '/security-services/detail', - ) - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['id', 'name', 'status', 'type', 'share_networks'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_security_service_list_all_tenants(self): - self.run_command('security-service-list --all-tenants') - self.assert_called( - 'GET', - '/security-services?all_tenants=1', - ) - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['id', 'name', 'status', 'type'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_security_service_list_all_filters(self): - filters = { - 'status': 'new', - 'name': 'fake-name', - 'type': 'ldap', - 'user': 'fake-user', - 'dns-ip': '1.1.1.1', - 'ou': 'fake-ou', - 'server': 'fake-server', - 'domain': 'fake-domain', - 'offset': 10, - 'limit': 20, - } - command_str = 'security-service-list' - for key, value in filters.items(): - command_str += f' --{key}={value}' - self.run_command(command_str) - self.assert_called( - 'GET', - '/security-services?dns_ip=1.1.1.1&domain=fake-domain&limit=20' - '&name=fake-name&offset=10&ou=fake-ou&server=fake-server' - '&status=new&type=ldap&user=fake-user', - ) - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['id', 'name', 'status', 'type'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_security_service_list_filter_by_dns_ip_alias(self): - self.run_command('security-service-list --dns_ip 1.1.1.1') - self.assert_called( - 'GET', - '/security-services?dns_ip=1.1.1.1', - ) - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['id', 'name', 'status', 'type'] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_security_service_list_filter_by_ou_alias(self): - self.run_command('security-service-list --ou fake-ou') - self.assert_called( - 'GET', - '/security-services?ou=fake-ou', - ) - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['id', 'name', 'status', 'type'] - ) - - @ddt.data( - {'--name': 'fake_name'}, - {'--description': 'fake_description'}, - {'--dns-ip': 'fake_dns_ip'}, - {'--ou': 'fake_ou'}, - {'--domain': 'fake_domain'}, - {'--server': 'fake_server'}, - {'--user': 'fake_user'}, - {'--password': 'fake_password'}, - { - '--name': 'fake_name', - '--description': 'fake_description', - '--dns-ip': 'fake_dns_ip', - '--ou': 'fake_ou', - '--domain': 'fake_domain', - '--server': 'fake_server', - '--user': 'fake_user', - '--password': 'fake_password', - }, - {'--name': '""'}, - {'--description': '""'}, - {'--dns-ip': '""'}, - {'--ou': '""'}, - {'--domain': '""'}, - {'--server': '""'}, - {'--user': '""'}, - {'--password': '""'}, - { - '--name': '""', - '--description': '""', - '--dns-ip': '""', - '--ou': '""', - '--domain': '""', - '--server': '""', - '--user': '""', - '--password': '""', - }, - ) - def test_security_service_update(self, data): - cmd = 'security-service-update 1111' - expected = dict() - for k, v in data.items(): - cmd += ' ' + k + ' ' + v - expected[k[2:].replace('-', '_')] = v - expected = dict(security_service=expected) - - self.run_command(cmd) - - self.assert_called('PUT', '/security-services/1111', body=expected) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_pool_list(self): - self.run_command('pool-list') - self.assert_called( - 'GET', - '/scheduler-stats/pools?backend=.%2A&host=.%2A&pool=.%2A', - ) - cliutils.print_list.assert_called_with( - mock.ANY, fields=["Name", "Host", "Backend", "Pool"] - ) - - @mock.patch.object(cliutils, 'print_dict', mock.Mock()) - def test_quota_show(self): - self.run_command('quota-show --tenant 1234') - self.assert_called( - 'GET', - '/quota-sets/1234', - ) - cliutils.print_dict.assert_called_once_with(mock.ANY) - - @mock.patch.object(cliutils, 'print_dict', mock.Mock()) - def test_quota_show_with_detail(self): - self.run_command('quota-show --tenant 1234 --detail') - self.assert_called( - 'GET', - '/quota-sets/1234/detail', - ) - cliutils.print_dict.assert_called_once_with(mock.ANY) - - @mock.patch.object(cliutils, 'print_dict', mock.Mock()) - def test_quota_show_with_user_id(self): - self.run_command('quota-show --tenant 1234 --user 1111') - self.assert_called( - 'GET', - '/quota-sets/1234?user_id=1111', - ) - cliutils.print_dict.assert_called_once_with(mock.ANY) - - @ddt.data('1111', '0') - @mock.patch('manilaclient.common.cliutils.print_dict') - def test_quota_show_with_share_type(self, share_type_id, mock_print_dict): - self.run_command( - f'quota-show --tenant 1234 --share_type {share_type_id}' - ) - - self.assert_called( - 'GET', - f'/quota-sets/1234?share_type={share_type_id}', - ) - mock_print_dict.assert_called_once_with(mock.ANY) - - @ddt.data( - ('--shares 13', {'shares': 13}), - ('--gigabytes 14', {'gigabytes': 14}), - ('--snapshots 15', {'snapshots': 15}), - ('--snapshot-gigabytes 13', {'snapshot_gigabytes': 13}), - ('--share-networks 13', {'share_networks': 13}), - ('--share-groups 13', {'share_groups': 13}), - ('--share-groups 0', {'share_groups': 0}), - ('--share-group-snapshots 13', {'share_group_snapshots': 13}), - ('--share-group-snapshots 0', {'share_group_snapshots': 0}), - ('--share-replicas 15', {'share_replicas': 15}), - ('--replica_gigabytes 100', {'replica_gigabytes': 100}), - ('--per_share_gigabytes 101', {'per_share_gigabytes': 101}), - ) - @ddt.unpack - def test_quota_update(self, cmd, expected_body): - self.run_command(f'quota-update 1234 {cmd}') - - expected = {'quota_set': dict(expected_body, tenant_id='1234')} - self.assert_called('PUT', '/quota-sets/1234', body=expected) - - @ddt.data( - "quota-update 1234 --share-groups 13 --share-type foo", - "quota-update 1234 --share-group-snapshots 14 --share-type bar", - ( - "quota-update 1234 --share-groups 13 --share-type foo " - "--share-group-snapshots 14" - ), - "--os-share-api-version 2.39 quota-update 1234 --share-groups 13", - ( - "--os-share-api-version 2.39 quota-update 1234 " - "--share-group-snapshots 13" - ), - ( - "--os-share-api-version 2.38 quota-update 1234 --shares 5 " - "--share-type foo" - ), - ) - def test_quota_update_with_wrong_combinations(self, cmd): - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - @mock.patch.object(cliutils, 'print_dict', mock.Mock()) - def test_pool_list_with_detail(self): - self.run_command('pool-list --detail') - self.assert_called( - 'GET', - '/scheduler-stats/pools/detail?backend=.%2A&host=.%2A&pool=.%2A', - ) - cliutils.print_dict.assert_called_with( - {'name': 'host1@backend1#pool2', 'qos': False} - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_pool_list_select_column(self): - self.run_command('pool-list --columns name,host') - self.assert_called( - 'GET', - '/scheduler-stats/pools/detail?backend=.%2A&host=.%2A&pool=.%2A', - ) - cliutils.print_list.assert_called_with( - mock.ANY, fields=["Name", "Host"] - ) - - @ddt.data( - ( - {"key1": "value1", "key2": "value2"}, - {"key1": "value1", "key2": "value2"}, - ), - ( - { - "key1": {"key11": "value11", "key12": "value12"}, - "key2": {"key21": "value21"}, - }, - { - "key1": "key11 = value11\nkey12 = value12", - "key2": "key21 = value21", - }, - ), - ({}, {}), - ) - @ddt.unpack - @mock.patch.object(cliutils, 'print_dict', mock.Mock()) - def test_quota_set_pretty_show(self, value, expected): - fake_quota_set = fakes.FakeQuotaSet(value) - - shell_v2._quota_set_pretty_show(fake_quota_set) - cliutils.print_dict.assert_called_with(expected) - - @ddt.data( - '--share-type test_type', - '--share_type test_type', - '--share-type-id 0123456789', - '--share_type_id 0123456789', - ) - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_pool_list_with_filters(self, param): - cmd = ( - 'pool-list --host host1 --backend backend1 --pool pool1' - + ' ' - + param - ) - self.run_command(cmd) - self.assert_called( - 'GET', - '/scheduler-stats/pools?backend=backend1&host=host1&' - f'pool=pool1&share_type={param.split()[-1]}', - ) - cliutils.print_list.assert_called_with( - mock.ANY, fields=["Name", "Host", "Backend", "Pool"] - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_api_version(self): - self.run_command('api-version') - self.assert_called('GET', '') - cliutils.print_list.assert_called_with( - mock.ANY, - ['ID', 'Status', 'Version', 'Min_version'], - field_labels=['ID', 'Status', 'Version', 'Minimum Version'], - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_group_list(self): - self.run_command('share-group-list') - - self.assert_called('GET', '/share-groups/detail') - cliutils.print_list.assert_called_once_with( - mock.ANY, - fields=('ID', 'Name', 'Status', 'Description'), - sortby_index=None, - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_group_list_select_column(self): - self.run_command('share-group-list --columns id,name,description') - - self.assert_called('GET', '/share-groups/detail') - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['Id', 'Name', 'Description'], sortby_index=None - ) - - def test_share_group_list_filter_by_inexact_name(self): - for separator in self.separators: - self.run_command( - 'share-group-list --name~' + separator + 'fake_name' - ) - self.assert_called('GET', '/share-groups/detail?name~=fake_name') - - def test_share_group_list_filter_by_inexact_description(self): - for separator in self.separators: - self.run_command( - 'share-group-list --description~' - + separator - + 'fake_description' - ) - self.assert_called( - 'GET', '/share-groups/detail?description~=fake_description' - ) - - def test_share_group_list_filter_by_inexact_unicode_name(self): - for separator in self.separators: - self.run_command('share-group-list --name~' + separator + 'ффф') - self.assert_called( - 'GET', '/share-groups/detail?name~=%D1%84%D1%84%D1%84' - ) - - def test_share_group_list_filter_by_inexact_unicode_description(self): - for separator in self.separators: - self.run_command( - 'share-group-list --description~' + separator + 'ффф' - ) - self.assert_called( - 'GET', '/share-groups/detail?description~=%D1%84%D1%84%D1%84' - ) - - def test_share_group_show(self): - fake_manager = mock.Mock() - fake_share_group = share_groups.ShareGroup( - fake_manager, {'id': '1234'} - ) - self.mock_object( - shell_v2, - '_find_share_group', - mock.Mock(side_effect=[fake_share_group]), - ) - - self.run_command('share-group-show 1234') - - shell_v2._find_share_group.assert_has_calls( - [mock.call(self.shell.cs, "1234")] - ) - - def test_share_group_create_wait(self): - fake_manager = mock.Mock() - fake_share_type1 = share_types.ShareType( - fake_manager, {'name': '1234', 'uuid': '1234'} - ) - fake_share_type2 = share_types.ShareType( - fake_manager, {'name': '5678', 'uuid': '5678'} - ) - fake_share_group_type = share_group_types.ShareGroupType( - fake_manager, {'name': 'fake_sg', 'uuid': '2345'} - ) - fake_share_network = share_networks.ShareNetwork( - fake_manager, {'id': '3456', 'uuid': '3456'} - ) - fake_share_group = share_groups.ShareGroup( - fake_manager, {'id': 'fake-sg-id', 'name': 'fake_sg'} - ) - - self.mock_object( - shell_v2, - '_find_share_type', - mock.Mock(side_effect=[fake_share_type1, fake_share_type2]), - ) - self.mock_object( - shell_v2, - '_find_share_group_type', - mock.Mock(side_effect=[fake_share_group_type]), - ) - self.mock_object( - shell_v2, - '_find_share_network', - mock.Mock(side_effect=[fake_share_network]), - ) - self.mock_object( - shell_v2, - '_wait_for_resource_status', - mock.Mock(side_effect=[fake_share_group]), - ) - - self.run_command( - 'share-group-create --name fake_sg --description my_group ' - '--share-types 1234,5678 ' - '--share-group-type fake_sg ' - '--share-network 3456 ' - '--availability-zone fake_az --wait' - ) - - shell_v2._find_share_type.assert_has_calls( - [ - mock.call(self.shell.cs, '1234'), - mock.call(self.shell.cs, '5678'), - ] - ) - - shell_v2._find_share_group_type.assert_has_calls( - [mock.call(self.shell.cs, 'fake_sg')] - ) - - shell_v2._find_share_network.assert_has_calls( - [mock.call(self.shell.cs, '3456')] - ) - - expected = { - 'share_group': { - 'name': 'fake_sg', - 'description': 'my_group', - 'availability_zone': 'fake_az', - 'share_group_type_id': '2345', - 'share_network_id': '3456', - 'share_types': ['1234', '5678'], - }, - } - self.assert_called('POST', '/share-groups', body=expected) - - shell_v2._wait_for_resource_status.assert_has_calls( - [ - mock.call( - self.shell.cs, - fake_share_group, - resource_type='share_group', - expected_status='available', - ) - ] - ) - - @ddt.data( - '--name fake_name --availability-zone fake_az', - '--description my_fake_description --name fake_name', - '--availability-zone fake_az', - ) - def test_share_group_create_no_share_types(self, data): - cmd = 'share-group-create' + ' ' + data - - self.run_command(cmd) - - self.assert_called('POST', '/share-groups') - - def test_share_group_create_invalid_args(self): - fake_share_type_1 = type('FakeShareType1', (object,), {'id': '1234'}) - fake_share_type_2 = type('FakeShareType2', (object,), {'id': '5678'}) - self.mock_object( - shell_v2, - '_find_share_type', - mock.Mock(side_effect=[fake_share_type_1, fake_share_type_2]), - ) - fake_share_group_type = type( - 'FakeShareGroupType', (object,), {'id': '2345'} - ) - self.mock_object( - shell_v2, - '_find_share_group_type', - mock.Mock(return_value=fake_share_group_type), - ) - fake_share_group_snapshot = type( - 'FakeShareGroupSnapshot', (object,), {'id': '3456'} - ) - self.mock_object( - shell_v2, - '_find_share_group_snapshot', - mock.Mock(return_value=fake_share_group_snapshot), - ) - - self.assertRaises( - ValueError, - self.run_command, - 'share-group-create --name fake_sg ' - '--description my_group --share-types 1234,5678 ' - '--share-group-type fake_sg_type ' - '--source-share-group-snapshot fake_share_group_snapshot ' - '--availability-zone fake_az', - ) - - @ddt.data( - ('--name new-name', {'name': 'new-name'}), - ('--description new-description', {'description': 'new-description'}), - ( - '--name new-name --description new-description', - {'name': 'new-name', 'description': 'new-description'}, - ), - ) - @ddt.unpack - def test_share_group_update(self, cmd, expected_body): - fake_manager = mock.Mock() - fake_share_group = share_groups.ShareGroup( - fake_manager, {'uuid': '1234', 'id': '1234'} - ) - self.mock_object( - shell_v2, - '_find_share_group', - mock.Mock(side_effect=[fake_share_group]), - ) - - self.run_command(f'share-group-update 1234 {cmd}') - - shell_v2._find_share_group.assert_has_calls( - [mock.call(self.shell.cs, '1234')] - ) - expected = {'share_group': expected_body} - self.assert_called('PUT', '/share-groups/1234', body=expected) - - def test_try_update_share_group_without_data(self): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'share-group-update 1234', - ) - - @ddt.data(('share_group_xyz',), ('share_group_abc', 'share_group_xyz')) - def test_share_group_delete_wait(self, share_group_to_delete): - fake_manager = mock.Mock() - fake_share_group = [ - share_groups.ShareGroup(fake_manager, {'id': share_group}) - for share_group in share_group_to_delete - ] - share_group_not_found_error = ( - "Delete for share group %s " - "failed: No group with a " - "name or ID of '%s' exists." - ) - share_group_are_not_found_errors = [ - exceptions.CommandError( - share_group_not_found_error % (share_group, share_group) - ) - for share_group in share_group_to_delete - ] - self.mock_object( - shell_v2, - '_find_share_group', - mock.Mock( - side_effect=( - fake_share_group + share_group_are_not_found_errors - ) - ), - ) - self.mock_object(shell_v2, '_wait_for_resource_status', mock.Mock()) - self.run_command( - 'share-group-delete {} --wait'.format( - ' '.join(share_group_to_delete) - ) - ) - shell_v2._find_share_group.assert_has_calls( - [ - mock.call(self.shell.cs, share_group) - for share_group in share_group_to_delete - ] - ) - fake_manager.delete.assert_has_calls( - [ - mock.call(share_group, force=False) - for share_group in fake_share_group - ] - ) - shell_v2._wait_for_resource_status.assert_has_calls( - [ - mock.call( - self.shell.cs, - share_group, - resource_type='share_group', - expected_status='deleted', - ) - for share_group in fake_share_group - ] - ) - - def test_share_group_delete_force(self): - fake_manager = mock.Mock() - fake_share_group = share_groups.ShareGroup( - fake_manager, {'id': 'fake-group'} - ) - self.mock_object( - shell_v2, - '_find_share_group', - mock.Mock(side_effect=[fake_share_group]), - ) - self.run_command('share-group-delete --force fake-group') - shell_v2._find_share_group.assert_has_calls( - [mock.call(self.shell.cs, "fake-group")] - ) - fake_manager.delete.assert_has_calls( - [mock.call(fake_share_group, force=True)] - ) - self.assertEqual(1, fake_manager.delete.call_count) - - @mock.patch.object(shell_v2, '_find_share_group', mock.Mock()) - def test_share_group_delete_all_fail(self): - shell_v2._find_share_group.side_effect = Exception - - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'share-group-delete fake-group', - ) - - @mock.patch.object(shell_v2, '_find_share_group', mock.Mock()) - def test_share_group_reset_state_with_flag(self): - fake_group = type('FakeShareGroup', (object,), {'id': '1234'}) - shell_v2._find_share_group.return_value = fake_group - - self.run_command('share-group-reset-state --state error 1234') - - self.assert_called( - 'POST', - '/share-groups/1234/action', - {'reset_status': {'status': 'error'}}, - ) - - @ddt.data( - 'fake-sg-id', - '--name fake_name fake-sg-id', - '--description my_fake_description --name fake_name fake-sg-id', - ) - @mock.patch.object(shell_v2, '_find_share_group', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_share_group_snapshot_create(self, data): - fake_sg = type('FakeShareGroup', (object,), {'id': '1234'}) - shell_v2._find_share_group.return_value = fake_sg - - self.run_command('share-group-snapshot-create ' + data) - - shell_v2._find_share_group.assert_called_with(mock.ANY, 'fake-sg-id') - self.assert_called('POST', '/share-group-snapshots') - self.assertEqual(0, shell_v2._wait_for_resource_status.call_count) - - @mock.patch.object(shell_v2, '_find_share_group', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_share_group_snapshot_create_with_wait(self): - fake_sg = type('FakeShareGroup', (object,), {'id': '1234'}) - shell_v2._find_share_group.return_value = fake_sg - self.run_command('share-group-snapshot-create fake-sg-id --wait') - - shell_v2._find_share_group.assert_called_with(mock.ANY, 'fake-sg-id') - self.assert_called('POST', '/share-group-snapshots') - # _wait_for_resource_status should be triggered once - shell_v2._wait_for_resource_status.assert_called_once_with( - self.shell.cs, - mock.ANY, - resource_type='share_group_snapshot', - expected_status='available', - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_group_snapshot_list(self): - self.run_command('share-group-snapshot-list') - - self.assert_called('GET', '/share-group-snapshots/detail') - cliutils.print_list.assert_called_once_with( - mock.ANY, - fields=('id', 'name', 'status', 'description'), - sortby_index=None, - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_group_snapshot_list_select_column(self): - self.run_command('share-group-snapshot-list --columns id,name') - - self.assert_called('GET', '/share-group-snapshots/detail') - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['Id', 'Name'], sortby_index=None - ) - - def test_share_group_snapshot_list_all_tenants_only_key(self): - self.run_command('share-group-snapshot-list --all-tenants') - - self.assert_called( - 'GET', '/share-group-snapshots/detail?all_tenants=1' - ) - - def test_share_group_snapshot_list_all_tenants_key_and_value_1(self): - for separator in self.separators: - self.run_command( - 'share-group-snapshot-list --all-tenants' + separator + '1' - ) - - self.assert_called( - 'GET', '/share-group-snapshots/detail?all_tenants=1' - ) - - def test_share_group_snapshot_list_with_filters(self): - self.run_command('share-group-snapshot-list --limit 10 --offset 0') - - self.assert_called( - 'GET', '/share-group-snapshots/detail?limit=10&offset=0' - ) - - def test_share_group_snapshot_show(self): - self.run_command('share-group-snapshot-show 1234') - - self.assert_called('GET', '/share-group-snapshots/1234') - - def test_share_group_snapshot_list_members(self): - self.run_command('share-group-snapshot-list-members 1234') - - self.assert_called('GET', '/share-group-snapshots/1234') - - def test_share_group_snapshot_list_members_select_column(self): - self.mock_object(cliutils, 'print_list') - - self.run_command( - 'share-group-snapshot-list-members 1234 --columns id,size' - ) - - self.assert_called('GET', '/share-group-snapshots/1234') - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['Id', 'Size'] - ) - - @mock.patch.object(shell_v2, '_find_share_group_snapshot', mock.Mock()) - def test_share_group_snapshot_reset_state(self): - fake_sg_snapshot = type( - 'FakeShareGroupSnapshot', (object,), {'id': '1234'} - ) - shell_v2._find_share_group_snapshot.return_value = fake_sg_snapshot - - self.run_command('share-group-snapshot-reset-state 1234') - - self.assert_called( - 'POST', - '/share-group-snapshots/1234/action', - {'reset_status': {'status': 'available'}}, - ) - - @mock.patch.object(shell_v2, '_find_share_group_snapshot', mock.Mock()) - def test_share_group_snapshot_reset_state_with_flag(self): - fake_sg_snapshot = type('FakeSGSnapshot', (object,), {'id': '1234'}) - shell_v2._find_share_group_snapshot.return_value = fake_sg_snapshot - - self.run_command( - 'share-group-snapshot-reset-state --state creating 1234' - ) - - self.assert_called( - 'POST', - '/share-group-snapshots/1234/action', - {'reset_status': {'status': 'creating'}}, - ) - - @ddt.data( - ('--name new-name', {'name': 'new-name'}), - ('--description new-description', {'description': 'new-description'}), - ( - '--name new-name --description new-description', - {'name': 'new-name', 'description': 'new-description'}, - ), - ) - @ddt.unpack - def test_share_group_snapshot_update(self, cmd, expected_body): - self.run_command(f'share-group-snapshot-update 1234 {cmd}') - - expected = {'share_group_snapshot': expected_body} - self.assert_called('PUT', '/share-group-snapshots/1234', body=expected) - - def test_try_update_share_group_snapshot_without_data(self): - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'share-group-snapshot-update 1234', - ) - - @mock.patch.object(shell_v2, '_find_share_group_snapshot', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_share_group_snapshot_delete(self): - fake_sg_snapshot = type('FakeSGSnapshot', (object,), {'id': '1234'}) - shell_v2._find_share_group_snapshot.return_value = fake_sg_snapshot - - self.run_command('share-group-snapshot-delete fake-group-snapshot') - - self.assert_called('DELETE', '/share-group-snapshots/1234') - self.assertEqual(0, shell_v2._wait_for_resource_status.call_count) - - @mock.patch.object(shell_v2, '_find_share_group_snapshot', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_share_group_snapshot_delete_with_wait(self): - fake_sg_snapshot = type('FakeSGSnapshot', (object,), {'id': '1234'}) - shell_v2._find_share_group_snapshot.return_value = fake_sg_snapshot - - self.run_command( - 'share-group-snapshot-delete fake-group-snapshot --wait' - ) - - self.assert_called('DELETE', '/share-group-snapshots/1234') - # _wait_for_resource_status should be triggered once - shell_v2._wait_for_resource_status.assert_called_once_with( - self.shell.cs, - mock.ANY, - resource_type='share_group_snapshot', - expected_status='deleted', - ) - - @mock.patch.object(shell_v2, '_find_share_group_snapshot', mock.Mock()) - def test_share_group_snapshot_delete_force(self): - fake_sg_snapshot = type('FakeSGSnapshot', (object,), {'id': '1234'}) - shell_v2._find_share_group_snapshot.return_value = fake_sg_snapshot - - self.run_command( - 'share-group-snapshot-delete --force fake-sg-snapshot' - ) - - self.assert_called( - 'POST', - '/share-group-snapshots/1234/action', - {'force_delete': None}, - ) - - def test_share_group_snapshot_delete_all_fail(self): - self.mock_object( - shell_v2, - '_find_share_group_snapshot', - mock.Mock(side_effect=Exception), - ) - - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'share-group-snapshot-delete fake-sg-snapshot', - ) - - @ddt.data( - *itertools.product( - ( - '--columns id,is_default', - '--columns id,name', - '--columns is_default', - '', - ), - {'2.45', '2.46', api_versions.MAX_VERSION}, - ) - ) - @ddt.unpack - def test_share_group_type_list(self, command_args, version): - self.mock_object(shell_v2, '_print_share_group_type_list') - command = 'share-group-type-list ' + command_args - columns_requested = command_args.split('--columns ')[-1] or None - is_default_in_api = api_versions.APIVersion( - version - ) >= api_versions.APIVersion('2.46') - - self.run_command(command, version=version) - - if not is_default_in_api and ( - not columns_requested or 'is_default' in columns_requested - ): - self.assert_called('GET', '/share-group-types/default') - self.assert_called_anytime('GET', '/share-group-types') - else: - self.assert_called('GET', '/share-group-types') - - shell_v2._print_share_group_type_list.assert_called_once_with( - mock.ANY, - default_share_group_type=mock.ANY, - columns=columns_requested, - ) - - def test_share_group_type_list_select_column(self): - self.mock_object(shell_v2, '_print_share_group_type_list') - - self.run_command('share-group-type-list --columns id,name') - - self.assert_called('GET', '/share-group-types') - shell_v2._print_share_group_type_list.assert_called_once_with( - mock.ANY, default_share_group_type=mock.ANY, columns='id,name' - ) - - def test_share_group_type_list_all(self): - self.run_command('share-group-type-list --all') - - self.assert_called_anytime('GET', '/share-group-types?is_public=all') - - @ddt.data(('', mock.ANY), (' --columns id,name', 'id,name')) - @ddt.unpack - def test_share_group_specs_list(self, args_cmd, expected_columns): - self.mock_object(shell_v2, '_print_type_and_extra_specs_list') - - self.run_command('share-group-type-specs-list') - - self.assert_called('GET', '/share-group-types?is_public=all') - shell_v2._print_type_and_extra_specs_list.assert_called_once_with( - mock.ANY, columns=mock.ANY - ) - - @ddt.data(True, False) - def test_share_group_type_create_with_access_and_group_specs(self, public): - fake_share_type_1 = type('FakeShareType', (object,), {'id': '1234'}) - fake_share_type_2 = type('FakeShareType', (object,), {'id': '5678'}) - self.mock_object( - shell_v2, - '_find_share_type', - mock.Mock(side_effect=[fake_share_type_1, fake_share_type_2]), - ) - expected = { - 'share_group_type': { - 'name': 'test-group-type-1', - 'share_types': ['1234', '5678'], - 'group_specs': {'spec1': 'value1'}, - 'is_public': public, - } - } - - self.run_command( - 'share-group-type-create test-group-type-1 ' - f'type1,type2 --is-public {str(public)} --group-specs ' - 'spec1=value1' - ) - - self.assert_called_anytime('POST', '/share-group-types', body=expected) - - def test_share_group_type_delete(self): - fake_share_group_type = type( - 'FakeShareGroupType', (object,), {'id': '1234'} - ) - self.mock_object( - shell_v2, - '_find_share_group_type', - mock.Mock(return_value=fake_share_group_type), - ) - - self.run_command('share-group-type-delete test-group-type-1') - - self.assert_called('DELETE', '/share-group-types/1234') - - def test_share_group_type_key_set(self): - fake_share_group_type = type( - 'FakeShareGroupType', - (object,), - { - 'id': '1234', - 'is_public': False, - 'set_keys': mock.Mock(), - 'unset_keys': mock.Mock(), - }, - ) - self.mock_object( - shell_v2, - '_find_share_group_type', - mock.Mock(return_value=fake_share_group_type), - ) - - self.run_command('share-group-type-key fake_sg_type set key1=value1') - - fake_share_group_type.set_keys.assert_called_with({'key1': 'value1'}) - - def test_share_group_type_key_unset(self): - fake_share_group_type = type( - 'FakeShareGroupType', - (object,), - { - 'id': '1234', - 'is_public': False, - 'set_keys': mock.Mock(), - 'unset_keys': mock.Mock(), - }, - ) - self.mock_object( - shell_v2, - '_find_share_group_type', - mock.Mock(return_value=fake_share_group_type), - ) - - self.run_command('share-group-type-key fake_group_type unset key1') - - fake_share_group_type.unset_keys.assert_called_with(['key1']) - - def test_share_group_type_access_list(self): - fake_share_group_type = type( - 'FakeShareGroupType', (object,), {'id': '1234', 'is_public': False} - ) - self.mock_object( - shell_v2, - '_find_share_group_type', - mock.Mock(return_value=fake_share_group_type), - ) - - self.run_command('share-group-type-access-list 1234') - - self.assert_called('GET', '/share-group-types/1234/access') - - def test_share_group_type_access_list_public(self): - fake_share_group_type = type( - 'FakeShareGroupType', (object,), {'id': '1234', 'is_public': True} - ) - self.mock_object( - shell_v2, - '_find_share_group_type', - mock.Mock(return_value=fake_share_group_type), - ) - - self.assertRaises( - exceptions.CommandError, - self.run_command, - 'share-group-type-access-list 1234', - ) - - def test_share_group_type_access_add_project(self): - fake_share_group_type = type( - 'FakeShareGroupType', (object,), {'id': '1234', 'is_public': False} - ) - self.mock_object( - shell_v2, - '_find_share_group_type', - mock.Mock(return_value=fake_share_group_type), - ) - expected = {'addProjectAccess': {'project': '101'}} - - self.run_command('share-group-type-access-add 1234 101') - - self.assert_called( - 'POST', '/share-group-types/1234/action', body=expected - ) - - def test_share_group_type_access_remove_project(self): - fake_share_group_type = type( - 'FakeShareGroupType', (object,), {'id': '1234', 'is_public': False} - ) - self.mock_object( - shell_v2, - '_find_share_group_type', - mock.Mock(return_value=fake_share_group_type), - ) - expected = {'removeProjectAccess': {'project': '101'}} - - self.run_command('share-group-type-access-remove 1234 101') - - self.assert_called( - 'POST', '/share-group-types/1234/action', body=expected - ) - - @ddt.data( - {'--shares': 5}, - {'--snapshots': 5}, - {'--gigabytes': 5}, - {'--snapshot-gigabytes': 5}, - {'--snapshot_gigabytes': 5}, - {'--share-networks': 5}, - {'--share_networks': 5}, - { - '--shares': 5, - '--snapshots': 5, - '--gigabytes': 5, - '--snapshot-gigabytes': 5, - '--share-networks': 5, - }, - { - '--shares': 5, - '--snapshots': 5, - '--gigabytes': 5, - '--snapshot-gigabytes': 5, - '--share-networks': 5, - '--share-groups': 5, - '--share-group-snapshots': 5, - }, - ) - def test_quota_class_update(self, data): - cmd = 'quota-class-update test' - expected = dict() - for k, v in data.items(): - cmd += f' {k} {v}' - expected[k[2:].replace('-', '_')] = v - expected['class_name'] = 'test' - expected = dict(quota_class_set=expected) - - self.run_command(cmd) - self.assert_called('PUT', '/quota-class-sets/test', body=expected) - - @ddt.data(True, False) - @mock.patch.object(shell_v2, '_find_share_replica', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_share_replica_delete_force(self, force): - fake_replica = type('FakeShareReplica', (object,), {'id': '1234'}) - shell_v2._find_share_replica.return_value = fake_replica - - force = '--force' if force else '' - self.run_command('share-replica-delete fake-replica ' + force) - - if force: - self.assert_called( - 'POST', - '/share-replicas/1234/action', - body={'force_delete': None}, - ) - else: - self.assert_called('DELETE', '/share-replicas/1234') - self.assertEqual(0, shell_v2._wait_for_resource_status.call_count) - - @mock.patch.object(shell_v2, '_find_share_replica', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_share_replica_delete_with_wait(self): - fake_replica = type('FakeShareReplica', (object,), {'id': '1234'}) - shell_v2._find_share_replica.return_value = fake_replica - - self.run_command('share-replica-delete fake-replica --wait') - - self.assert_called('DELETE', '/share-replicas/1234') - - # _wait_for_resource_status should be triggered once - shell_v2._wait_for_resource_status.assert_called_once_with( - self.shell.cs, - mock.ANY, - resource_type='share_replica', - expected_status='deleted', - ) - - @ddt.data([1, 0], [1, 1], [2, 0], [2, 1], [2, 2]) - @ddt.unpack - @mock.patch.object(shell_v2, '_find_share_replica', mock.Mock()) - def test_share_replica_delete_errors(self, replica_count, replica_errors): - class StubbedReplicaFindError(Exception): - """Error in find share replica stub""" - - pass - - class StubbedFindWithErrors: - def __init__(self, existing_replicas): - self.existing_replicas = existing_replicas - - def __call__(self, cs, replica): - if replica not in self.existing_replicas: - raise StubbedReplicaFindError - return type('FakeShareReplica', (object,), {'id': replica}) - - all_replicas = [] - existing_replicas = [] - for counter in range(replica_count): - replica = f'fake-replica-{counter}' - if counter >= replica_errors: - existing_replicas.append(replica) - all_replicas.append(replica) - - shell_v2._find_share_replica.side_effect = StubbedFindWithErrors( - existing_replicas - ) - cmd = 'share-replica-delete {}'.format(' '.join(all_replicas)) - - if replica_count == replica_errors: - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - else: - self.run_command(cmd) - for replica in existing_replicas: - self.assert_called_anytime( - 'DELETE', - '/share-replicas/' + replica, - clear_callstack=False, - ) - - def test_share_replica_list_all(self): - self.run_command('share-replica-list') - - self.assert_called('GET', '/share-replicas/detail') - - @mock.patch.object(shell_v2, '_find_share', mock.Mock()) - def test_share_replica_list_for_share(self): - fshare = type('FakeShare', (object,), {'id': 'fake-share-id'}) - shell_v2._find_share.return_value = fshare - cmd = 'share-replica-list --share-id %s' - self.run_command(cmd % fshare.id) - - self.assert_called( - 'GET', '/share-replicas/detail?share_id=fake-share-id' - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_replica_list_select_column(self): - self.run_command('share-replica-list --columns id,status') - - self.assert_called('GET', '/share-replicas/detail') - - cliutils.print_list.assert_called_once_with(mock.ANY, ['Id', 'Status']) - - @ddt.data( - 'fake-share-id --az fake-az', - 'fake-share-id --availability-zone fake-az', - ) - @mock.patch.object(shell_v2, '_find_share', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_share_replica_create(self, data): - fshare = type('FakeShare', (object,), {'id': 'fake-share-id'}) - shell_v2._find_share.return_value = fshare - - cmd = 'share-replica-create' + ' ' + data - - self.run_command(cmd) - - shell_v2._find_share.assert_called_with(mock.ANY, fshare.id) - self.assert_called('POST', '/share-replicas') - self.assertEqual(0, shell_v2._wait_for_resource_status.call_count) - - @mock.patch.object(shell_v2, '_find_share', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_share_replica_create_with_wait(self): - fshare = type('FakeShare', (object,), {'id': 'fake-share-id'}) - shell_v2._find_share.return_value = fshare - - cmd = ( - 'share-replica-create fake-share-id ' - '--availability-zone fake-az --wait' - ) - - self.run_command(cmd) - - shell_v2._find_share.assert_called_with(mock.ANY, fshare.id) - self.assert_called('POST', '/share-replicas') - # _wait_for_resource_status should be triggered once - shell_v2._wait_for_resource_status.assert_called_once_with( - self.shell.cs, - mock.ANY, - resource_type='share_replica', - expected_status='available', - ) - - def test_share_replica_show(self): - self.run_command('share-replica-show 5678') - - self.assert_called_anytime('GET', '/share-replicas/5678') - - @mock.patch.object(shell_v2, '_find_share_replica', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_share_replica_promote_quiesce_wait_time(self): - fake_replica = type('FakeShareReplica', (object,), {'id': '1234'}) - shell_v2._find_share_replica.return_value = fake_replica - cmd = ( - 'share-replica-promote ' - + fake_replica.id - + ' --quiesce-wait-time 5' - ) - self.run_command(cmd) - self.assert_called( - 'POST', - '/share-replicas/1234/action', - body={'promote': {'quiesce_wait_time': '5'}}, - ) - self.assertEqual(0, shell_v2._wait_for_resource_status.call_count) - - @mock.patch.object(shell_v2, '_find_share_replica', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_resource_status', mock.Mock()) - def test_share_replica_promote_with_wait_(self): - fake_replica = type('FakeShareReplica', (object,), {'id': '1234'}) - shell_v2._find_share_replica.return_value = fake_replica - cmd = 'share-replica-promote ' + fake_replica.id + ' --wait' - self.run_command(cmd) - self.assert_called('POST', '/share-replicas/1234/action') - # _wait_for_resource_status should be triggered once - shell_v2._wait_for_resource_status.assert_called_once_with( - self.shell.cs, - mock.ANY, - resource_type='share_replica', - expected_status='active', - status_attr='replica_state', - ) - - @ddt.data('promote', 'resync') - @mock.patch.object(shell_v2, '_find_share_replica', mock.Mock()) - def test_share_replica_actions(self, action): - fake_replica = type('FakeShareReplica', (object,), {'id': '1234'}) - shell_v2._find_share_replica.return_value = fake_replica - cmd = 'share-replica-' + action + ' ' + fake_replica.id - - self.run_command(cmd) - - self.assert_called( - 'POST', - '/share-replicas/1234/action', - body={action.replace('-', '_'): None}, - ) - - @mock.patch.object(shell_v2, '_find_share_replica', mock.Mock()) - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - @ddt.data(None, "replica_state,path") - def test_share_replica_export_location_list(self, columns): - fake_replica = type('FakeShareReplica', (object,), {'id': '1234'}) - shell_v2._find_share_replica.return_value = fake_replica - cmd = 'share-replica-export-location-list ' + fake_replica.id - if columns is not None: - cmd = cmd + f' --columns={columns}' - expected_columns = list( - map(lambda x: x.strip().title(), columns.split(",")) - ) - else: - expected_columns = [ - 'ID', - 'Availability Zone', - 'Replica State', - 'Preferred', - 'Path', - ] - - self.run_command(cmd) - - self.assert_called('GET', '/share-replicas/1234/export-locations') - cliutils.print_list.assert_called_with(mock.ANY, expected_columns) - - @mock.patch.object(shell_v2, '_find_share_replica', mock.Mock()) - def test_share_replica_export_location_show(self): - fake_replica = type('FakeShareReplica', (object,), {'id': '1234'}) - shell_v2._find_share_replica.return_value = fake_replica - self.run_command( - 'share-replica-export-location-show 1234 fake-el-uuid' - ) - self.assert_called( - 'GET', '/share-replicas/1234/export-locations/fake-el-uuid' - ) - - @ddt.data('reset-state', 'reset-replica-state') - @mock.patch.object(shell_v2, '_find_share_replica', mock.Mock()) - def test_share_replica_reset_state_cmds(self, action): - if action == 'reset-state': - attr = 'status' - action_name = 'reset_status' - else: - attr = 'replica_state' - action_name = action.replace('-', '_') - fake_replica = type('FakeShareReplica', (object,), {'id': '1234'}) - shell_v2._find_share_replica.return_value = fake_replica - cmd = 'share-replica-%(action)s %(resource)s --state %(state)s' - - self.run_command( - cmd % {'action': action, 'resource': 1234, 'state': 'xyzzyspoon!'} - ) - - self.assert_called( - 'POST', - '/share-replicas/1234/action', - body={action_name: {attr: 'xyzzyspoon!'}}, - ) - - def test_snapshot_instance_list_all(self): - self.run_command('snapshot-instance-list') - self.assert_called('GET', '/snapshot-instances') - - def test_snapshot_instance_list_all_detail(self): - self.run_command('snapshot-instance-list --detail True') - self.assert_called('GET', '/snapshot-instances/detail') - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_snapshot_instance_list_select_column(self): - self.run_command('snapshot-instance-list --columns id,status') - self.assert_called('GET', '/snapshot-instances') - cliutils.print_list.assert_called_once_with(mock.ANY, ['Id', 'Status']) - - @mock.patch.object(shell_v2, '_find_share_snapshot', mock.Mock()) - def test_snapshot_instance_list_for_snapshot(self): - fsnapshot = type('FakeSnapshot', (object,), {'id': 'fake-snapshot-id'}) - shell_v2._find_share_snapshot.return_value = fsnapshot - cmd = 'snapshot-instance-list --snapshot %s' - self.run_command(cmd % fsnapshot.id) - - self.assert_called( - 'GET', '/snapshot-instances?snapshot_id=fake-snapshot-id' - ) - - def test_snapshot_instance_show(self): - self.run_command('snapshot-instance-show 1234') - self.assert_called_anytime( - 'GET', '/snapshot-instances/1234', clear_callstack=False - ) - self.assert_called_anytime( - 'GET', '/snapshot-instances/1234/export-locations' - ) - - def test_snapshot_instance_reset_state(self): - self.run_command('snapshot-instance-reset-state 1234') - expected = {'reset_status': {'status': 'available'}} - self.assert_called( - 'POST', '/snapshot-instances/1234/action', body=expected - ) - - def test_migration_start(self): - command = ( - "migration-start --force-host-assisted-migration True " - "--new-share-network 1111 --new-share-type 1 1234 " - "host@backend#pool --writable False --nondisruptive True " - "--preserve-metadata False --preserve-snapshots True" - ) - self.run_command(command) - expected = { - 'migration_start': { - 'host': 'host@backend#pool', - 'force_host_assisted_migration': 'True', - 'preserve_metadata': 'False', - 'writable': 'False', - 'nondisruptive': 'True', - 'preserve_snapshots': 'True', - 'new_share_network_id': 1111, - 'new_share_type_id': 1, - } - } - self.assert_called('POST', '/shares/1234/action', body=expected) - - @ddt.data( - 'migration-complete', 'migration-get-progress', 'migration-cancel' - ) - def test_migration_others(self, method): - command = ' '.join((method, '1234')) - self.run_command(command) - expected = {method.replace('-', '_'): None} - self.assert_called('POST', '/shares/1234/action', body=expected) - - @ddt.data('migration_error', 'migration_success', None) - def test_reset_task_state(self, param): - command = ' '.join(('reset-task-state --state', str(param), '1234')) - self.run_command(command) - expected = {'reset_task_state': {'task_state': param}} - self.assert_called('POST', '/shares/1234/action', body=expected) - - @ddt.data( - ('fake_security_service1',), - ('fake_security_service1', 'fake_security_service2'), - ) - def test_security_service_delete(self, ss_ids): - fake_security_services = [ - security_services.SecurityService('fake', {'id': ss_id}, True) - for ss_id in ss_ids - ] - self.mock_object( - shell_v2, - '_find_security_service', - mock.Mock(side_effect=fake_security_services), - ) - - self.run_command('security-service-delete {}'.format(' '.join(ss_ids))) - - shell_v2._find_security_service.assert_has_calls( - [mock.call(self.shell.cs, ss_id) for ss_id in ss_ids] - ) - for ss in fake_security_services: - self.assert_called_anytime( - 'DELETE', - f'/security-services/{ss.id}', - clear_callstack=False, - ) - - @ddt.data( - ('fake_share_network1',), - ('fake_share_network1', 'fake_share_network1'), - ) - def test_share_network_delete(self, sn_ids): - fake_share_networks = [ - share_networks.ShareNetwork('fake', {'id': sn_id}, True) - for sn_id in sn_ids - ] - self.mock_object( - shell_v2, - '_find_share_network', - mock.Mock(side_effect=fake_share_networks), - ) - - self.run_command('share-network-delete {}'.format(' '.join(sn_ids))) - - shell_v2._find_share_network.assert_has_calls( - [mock.call(self.shell.cs, sn_id) for sn_id in sn_ids] - ) - for sn in fake_share_networks: - self.assert_called_anytime( - 'DELETE', f'/share-networks/{sn.id}', clear_callstack=False - ) - - @mock.patch.object(shell_v2, '_find_share', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_snapshot_status', mock.Mock()) - def test_snapshot_create(self): - share_to_create_snapshot = shares.Share('fake_share', {'id': '1234'}) - shell_v2._find_share.return_value = share_to_create_snapshot - - self.run_command( - 'snapshot-create fake_share --name testshare1snapshot' - ) - - shell_v2._find_share.assert_has_calls( - [mock.call(self.shell.cs, 'fake_share')] - ) - self.assert_called_anytime('POST', '/snapshots', clear_callstack=False) - # _wait_for_snapshot_status should not be trigerred - self.assertEqual(0, shell_v2._wait_for_snapshot_status.call_count) - - @mock.patch.object(shell_v2, '_find_share', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_snapshot_status', mock.Mock()) - def test_snapshot_create_with_wait(self): - share_to_create_snapshot = shares.Share('fake_share', {'id': '1234'}) - shell_v2._find_share.return_value = share_to_create_snapshot - - self.run_command( - 'snapshot-create fake_share --name testshare1snapshot --wait' - ) - - shell_v2._find_share.assert_has_calls( - [mock.call(self.shell.cs, 'fake_share')] - ) - self.assert_called_anytime('POST', '/snapshots', clear_callstack=False) - # _wait_for_snapshot_status should be trigerred once - shell_v2._wait_for_snapshot_status.assert_called_once_with( - self.shell.cs, mock.ANY, expected_status='available' - ) - - @ddt.data(('fake_snapshot1',), ('fake_snapshot1', 'fake_snapshot2')) - def test_snapshot_delete(self, snapshot_ids): - fake_snapshots = [ - share_snapshots.ShareSnapshot('fake', {'id': snapshot_id}, True) - for snapshot_id in snapshot_ids - ] - self.mock_object( - shell_v2, - '_find_share_snapshot', - mock.Mock(side_effect=fake_snapshots), - ) - - self.run_command('snapshot-delete {}'.format(' '.join(snapshot_ids))) - - shell_v2._find_share_snapshot.assert_has_calls( - [mock.call(self.shell.cs, s_id) for s_id in snapshot_ids] - ) - for snapshot in fake_snapshots: - self.assert_called_anytime( - 'DELETE', f'/snapshots/{snapshot.id}', clear_callstack=False - ) - - @mock.patch.object(shell_v2, '_find_share_snapshot', mock.Mock()) - @mock.patch.object(shell_v2, '_wait_for_snapshot_status', mock.Mock()) - def test_snapshot_delete_with_wait(self): - fake_snapshot = share_snapshots.ShareSnapshot( - 'fake', {'id': 'fake_snapshot1'}, True - ) - shell_v2._find_share_snapshot.return_value = fake_snapshot - - self.run_command('snapshot-delete fake_snapshot1 --wait') - - shell_v2._find_share_snapshot.assert_has_calls( - [mock.call(self.shell.cs, 'fake_snapshot1')] - ) - self.assert_called_anytime( - 'DELETE', '/snapshots/fake_snapshot1', clear_callstack=False - ) - # _wait_for_resource_status should be trigerred once - shell_v2._wait_for_snapshot_status.assert_called_once_with( - self.shell.cs, 'fake_snapshot1', expected_status='deleted' - ) - - @ddt.data(('snapshot_xyz',), ('snapshot_abc', 'snapshot_xyz')) - def test_snapshot_force_delete_wait(self, snapshots_to_delete): - fake_manager = mock.Mock() - fake_snapshots = [ - share_snapshots.ShareSnapshot(fake_manager, {'id': '1234'}) - for snapshot in snapshots_to_delete - ] - snapshot_not_found_error = ( - "Delete for snapshot %s failed: No " - "snapshot with a name or " - "ID of '%s' exists." - ) - snapshots_are_not_found_errors = [ - exceptions.CommandError( - snapshot_not_found_error % (snapshot, snapshot) - ) - for snapshot in snapshots_to_delete - ] - self.mock_object( - shell_v2, - '_find_share_snapshot', - mock.Mock( - side_effect=(fake_snapshots + snapshots_are_not_found_errors) - ), - ) - self.run_command( - 'snapshot-force-delete {} --wait'.format( - ' '.join(snapshots_to_delete) - ) - ) - shell_v2._find_share_snapshot.assert_has_calls( - [ - mock.call(self.shell.cs, snapshot) - for snapshot in snapshots_to_delete - ] - ) - fake_manager.force_delete.assert_has_calls( - [mock.call(snapshot) for snapshot in fake_snapshots] - ) - self.assertEqual( - len(snapshots_to_delete), fake_manager.force_delete.call_count - ) - - @ddt.data(('fake_type1',), ('fake_type1', 'fake_type2')) - def test_share_type_delete(self, type_ids): - fake_share_types = [ - share_types.ShareType('fake', {'id': type_id}, True) - for type_id in type_ids - ] - self.mock_object( - shell_v2, - '_find_share_type', - mock.Mock(side_effect=fake_share_types), - ) - - self.run_command('type-delete {}'.format(' '.join(type_ids))) - - shell_v2._find_share_type.assert_has_calls( - [mock.call(self.shell.cs, t_id) for t_id in type_ids] - ) - for fake_share_type in fake_share_types: - self.assert_called_anytime( - 'DELETE', - f'/types/{fake_share_type.id}', - clear_callstack=False, - ) - - @ddt.data(('share_server_xyz',), ('share_server_abc', 'share_server_xyz')) - def test_share_server_delete_wait(self, share_servers_to_delete): - fake_manager = mock.Mock() - fake_share_servers = [ - share_servers.ShareServer(fake_manager, {'id': '1234'}) - for share_server in share_servers_to_delete - ] - share_server_not_found_error = ( - "Delete for share server %s " - "failed: No server with a " - "name or ID of '%s' exists." - ) - share_servers_are_not_found_errors = [ - exceptions.CommandError( - share_server_not_found_error % (share_server, share_server) - ) - for share_server in share_servers_to_delete - ] - self.mock_object( - shell_v2, - '_find_share_server', - mock.Mock( - side_effect=( - fake_share_servers + share_servers_are_not_found_errors - ) - ), - ) - - self.mock_object(shell_v2, '_wait_for_resource_status', mock.Mock()) - - self.run_command( - 'share-server-delete {} --wait'.format( - ' '.join(share_servers_to_delete) - ) - ) - - shell_v2._find_share_server.assert_has_calls( - [ - mock.call(self.shell.cs, share_server) - for share_server in share_servers_to_delete - ] - ) - fake_manager.delete.assert_has_calls( - [mock.call(share_server) for share_server in fake_share_servers] - ) - shell_v2._wait_for_resource_status.assert_has_calls( - [ - mock.call( - self.shell.cs, - share_server, - resource_type='share_server', - expected_status='deleted', - ) - for share_server in fake_share_servers - ] - ) - self.assertEqual( - len(share_servers_to_delete), fake_manager.delete.call_count - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_message_list(self): - self.run_command('message-list') - - self.assert_called('GET', '/messages') - cliutils.print_list.assert_called_once_with( - mock.ANY, - fields=[ - 'ID', - 'Resource Type', - 'Resource ID', - 'Action ID', - 'User Message', - 'Detail ID', - 'Created At', - ], - sortby_index=None, - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_message_list_created_before_aliases(self): - self.run_command('message-list --before 2001-01-01') - self.assert_called( - 'GET', - '/messages?created_before=2001-01-01', - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_share_message_list_created_since_aliases(self): - self.run_command('message-list --since 2001-01-01') - self.assert_called( - 'GET', - '/messages?created_since=2001-01-01', - ) - - @mock.patch.object(cliutils, 'print_list', mock.Mock()) - def test_message_list_select_column(self): - self.run_command('message-list --columns id,resource_type') - - self.assert_called('GET', '/messages') - cliutils.print_list.assert_called_once_with( - mock.ANY, fields=['Id', 'Resource_Type'], sortby_index=None - ) - - def test_message_list_with_filters(self): - self.run_command('message-list --limit 10 --offset 0') - - self.assert_called('GET', '/messages?limit=10&offset=0') - - def test_message_show(self): - self.run_command('message-show 1234') - - self.assert_called('GET', '/messages/1234') - - @ddt.data( - ('1234',), - ('1234_error',), - ('1234_error', '5678'), - ('1234', '5678_error'), - ('1234', '5678'), - ) - def test_message_delete(self, ids): - fake_messages = dict() - for mid in ids: - if mid.endswith('_error'): - continue - fake_messages[mid] = messages.Message('fake', {'id': mid}, True) - - def _find_message_with_errors(cs, mid): - if mid.endswith('_error'): - raise Exception - return fake_messages[mid] - - self.mock_object( - shell_v2, - '_find_message', - mock.Mock(side_effect=_find_message_with_errors), - ) - - cmd = 'message-delete {}'.format(' '.join(ids)) - - if len(fake_messages) == 0: - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - else: - self.run_command(cmd) - - shell_v2._find_message.assert_has_calls( - [mock.call(self.shell.cs, mid) for mid in ids] - ) - for fake_message in fake_messages.values(): - self.assert_called_anytime( - 'DELETE', - f'/messages/{fake_message.id}', - clear_callstack=False, - ) - - @ddt.data( - ('share-network-list', ' --description~', '/share-networks/', '2.35'), - ('share-network-list', ' --name~', '/share-networks/', '2.35'), - ('share-group-list', ' --description~', '/share-groups/', '2.35'), - ('share-group-list', ' --name~', '/share-groups/', '2.35'), - ('list', ' --description~', '/shares/', '2.35'), - ('list', ' --name~', '/shares/', '2.35'), - ('snapshot-list', ' --description~', '/snapshots/', '2.35'), - ('snapshot-list', ' --name~', '/snapshots/', '2.35'), - ) - @ddt.unpack - def test_list_filter_by_inexact_version_not_support( - self, cmd, option, url, version - ): - for separator in self.separators: - self.assertRaises( - exceptions.CommandError, - self.run_command, - cmd + option + separator + 'fake', - version=version, - ) - - def test_share_server_unmanage_all_fail(self): - # All of 2345, 5678, 9999 throw exception - cmd = '--os-share-api-version 2.49' - cmd += ' share-server-unmanage' - cmd += ' 2345 5678 9999' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_share_server_unmanage_some_fail(self): - # 5678 and 9999 throw exception - self.run_command('share-server-unmanage 1234 5678 9999') - expected = {'unmanage': {'force': False}} - self.assert_called('POST', '/share-servers/1234/action', body=expected) - - @ddt.data('migration-start', 'migration-check') - def test_share_server_migration_start_and_check(self, method): - command = ( - f"share-server-{method} " - "1234 host@backend --new-share-network 1111 " - "--writable False --nondisruptive True " - "--preserve-snapshots True" - ) - self.run_command(command) - method = method.replace('-', '_') - expected = { - method: { - 'host': 'host@backend', - 'writable': 'False', - 'nondisruptive': 'True', - 'preserve_snapshots': 'True', - 'new_share_network_id': 1111, - } - } - self.assert_called('POST', '/share-servers/1234/action', body=expected) - - @ddt.data( - 'migration-complete', 'migration-get-progress', 'migration-cancel' - ) - def test_share_server_migration_others(self, method): - command = 'share-server-' + ' '.join((method, '1234')) - self.run_command(command) - expected = {method.replace('-', '_'): None} - self.assert_called('POST', '/share-servers/1234/action', body=expected) - - @ddt.data('migration_error', 'migration_success', None) - def test_share_server_reset_task_state(self, param): - command = ' '.join( - ('share-server-reset-task-state --state', str(param), '1234') - ) - self.run_command(command) - expected = {'reset_task_state': {'task_state': param}} - self.assert_called('POST', '/share-servers/1234/action', body=expected) diff --git a/manilaclient/v2/shell.py b/manilaclient/v2/shell.py deleted file mode 100644 index b2275b64c..000000000 --- a/manilaclient/v2/shell.py +++ /dev/null @@ -1,7907 +0,0 @@ -# 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 - -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 -import manilaclient.v2.shares - - -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, - 'share_instance': _find_share_instance, - 'share_server': _find_share_server, - 'share_access_rule': _find_share_access_rule, - } - - 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, - 'share_instance': _print_share_instance, - 'share_access_rule': _print_share_access_rule, - } - - 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." - ) - unmanaged_message = ( - "%(resource_type)s %(resource)s has been successfully unmanaged." - ) - message_payload = { - 'resource_type': resource_type.capitalize(), - 'resource': resource.id, - } - not_found_regex = "no .* exists" - 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): - if 'deleted' in expected_status: - print(deleted_message % message_payload) - break - if 'unmanaged' in expected_status: - print(unmanaged_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) - - -def _find_share_transfer(cs, transfer): - """Get a share transfer by ID.""" - return apiclient_utils.find_resource(cs.transfers, transfer) - - -@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.convert_dict_list_to_string( - info['export_locations'], - ignored_keys=[ - 'replica_state', - 'availability_zone', - 'share_replica_id', - ], - ) - - # 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.convert_dict_list_to_string( - info['export_locations'], - ignored_keys=[ - 'replica_state', - 'availability_zone', - 'share_replica_id', - ], - ) - - cliutils.print_dict(info) - - -def _find_share_access_rule(cs, access_rule): - """Get share access rule state""" - return apiclient_utils.find_resource(cs.share_access_rules, access_rule) - - -def _print_share_access_rule(cs, access_rule): - info = access_rule._info.copy() - - 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.convert_dict_list_to_string( - info['export_locations'], - ignored_keys=[ - 'replica_state', - 'availability_zone', - 'share_replica_id', - ], - ) - 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 _wait_for_snapshot_status(cs, snapshot, expected_status='available'): - return _wait_for_resource_status( - cs, snapshot, expected_status, resource_type='snapshot' - ) - - -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.convert_dict_list_to_string( - 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( - [f'{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, allow_empty_key=True): - return _extract_key_value_options(args, 'metadata', allow_empty_key) - - -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, allow_empty_key=True): - 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) - elif allow_empty_key: - key = option - value = None - else: - # disallow empty key - continue - 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 = f"Following options were duplicated: {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', - 'per_share_gigabytes', - 'share_groups', - 'share_group_snapshots', -] - - -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='', - default=None, - help='ID of project to list the quotas for.', -) -@cliutils.arg( - '--user-id', - metavar='', - default=None, - help="ID of user to list the quotas for. Optional. " - "Mutually exclusive with '--share-type'.", -) -@cliutils.arg( - '--share-type', - '--share_type', - metavar='', - 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='', - 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='', - help='UUID of project to set the quotas for.', -) -@cliutils.arg( - '--user-id', - metavar='', - default=None, - help="ID of a user to set the quotas for. Optional. " - "Mutually exclusive with '--share-type'.", -) -@cliutils.arg( - '--shares', - metavar='', - type=int, - default=None, - help='New value for the "shares" quota.', -) -@cliutils.arg( - '--snapshots', - metavar='', - type=int, - default=None, - help='New value for the "snapshots" quota.', -) -@cliutils.arg( - '--gigabytes', - metavar='', - type=int, - default=None, - help='New value for the "gigabytes" quota.', -) -@cliutils.arg( - '--snapshot-gigabytes', - '--snapshot_gigabytes', # alias - metavar='', - type=int, - default=None, - action='single_alias', - help='New value for the "snapshot_gigabytes" quota.', -) -@cliutils.arg( - '--share-networks', - '--share_networks', - metavar='', - type=int, - default=None, - action='single_alias', - help='New value for the "share_networks" quota.', -) -@cliutils.arg( - '--share-groups', - '--share_groups', - '--groups', - metavar='', - 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='', - type=int, - default=None, - action='single_alias', - help='New value for the "share_group_snapshots" quota.', -) -@cliutils.arg( - '--share-type', - '--share_type', - metavar='', - 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='', - 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='', - type=int, - default=None, - help='New value for the "replica_gigabytes" quota. Available only for ' - 'microversion >= 2.53', -) -@cliutils.arg( - '--per-share-gigabytes', - '--per_share_gigabytes', - metavar='', - type=int, - default=None, - help='New value for the "per_share_gigabytes" quota. Available only for ' - 'microversion >= 2.62', -) -@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 - if args.per_share_gigabytes is not None: - if cs.api_version < api_versions.APIVersion("2.62"): - raise exceptions.CommandError( - "'per share gigabytes' quotas are available only starting " - "with '2.62' API microversion." - ) - kwargs["per_share_gigabytes"] = args.per_share_gigabytes - - cs.quotas.update(**kwargs) - - -@cliutils.arg( - '--tenant-id', - '--tenant', - '--project', - '--project-id', - action='single_alias', - dest='project_id', - metavar='', - help='ID of the project to delete quota for.', -) -@cliutils.arg( - '--user-id', - metavar='', - help="ID of user to delete quota for. Optional." - "Mutually exclusive with '--share-type'.", -) -@cliutils.arg( - '--share-type', - '--share_type', - metavar='', - 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='', - 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='', - help='Name of quota class to set the quotas for.', -) -@cliutils.arg( - '--shares', - metavar='', - type=int, - default=None, - help='New value for the "shares" quota.', -) -@cliutils.arg( - '--snapshots', - metavar='', - type=int, - default=None, - help='New value for the "snapshots" quota.', -) -@cliutils.arg( - '--gigabytes', - metavar='', - type=int, - default=None, - help='New value for the "gigabytes" quota.', -) -@cliutils.arg( - '--snapshot-gigabytes', - '--snapshot_gigabytes', # alias - metavar='', - type=int, - default=None, - action='single_alias', - help='New value for the "snapshot_gigabytes" quota.', -) -@cliutils.arg( - '--share-networks', - '--share_networks', # alias - metavar='', - type=int, - default=None, - action='single_alias', - help='New value for the "share_networks" quota.', -) -@cliutils.arg( - '--share-groups', - '--share_groups', # alias - metavar='', - type=int, - default=None, - action='single_alias', - help='New value for the "share_groups" quota. Available only for ' - 'microversion >= 2.40', -) -@cliutils.arg( - '--share-group-snapshots', - '--share_group_snapshots', - metavar='', - type=int, - default=None, - action='single_alias', - help='New value for the "share_group_snapshots" quota. Available only for ' - 'microversion >= 2.40', -) -@cliutils.arg( - '--share-replicas', - '--share_replicas', # alias - '--replicas', # alias - metavar='', - 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='', - type=int, - default=None, - action='single_alias', - help='New value for the "replica_gigabytes" quota. Available only for ' - 'microversion >= 2.53', -) -@cliutils.arg( - '--per-share-gigabytes', - '--per_share_gigabytes', # alias - metavar='', - type=int, - default=None, - action='single_alias', - help='New value for the "per_share_gigabytes" quota. Available only for ' - 'microversion >= 2.62', -) -def do_quota_class_update(cs, args): - """Update the quotas for a quota class (Admin only).""" - 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 groups' quotas are available only starting with " - "'2.40' API microversion." - ) - 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." - ) - if args.per_share_gigabytes is not None: - if cs.api_version < api_versions.APIVersion("2.62"): - raise exceptions.CommandError( - "'per_share_gigabytes' quota is available only starting " - "with '2.62' 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='', - 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='', - type=str, - help='Share protocol (NFS, CIFS, CephFS, GlusterFS, HDFS or MAPRFS).', -) -@cliutils.arg('size', metavar='', type=int, help='Share size in GiB.') -@cliutils.arg( - '--snapshot-id', - '--snapshot_id', - '--snapshot', - metavar='', - action='single_alias', - help='Optional snapshot ID or name to create the share from.' - ' (Default=None)', - default=None, -) -@cliutils.arg( - '--name', - metavar='', - help='Optional share name. (Default=None)', - default=None, -) -@cliutils.arg( - '--metadata', - type=str, - nargs='*', - metavar='', - help='Metadata key=value pairs (Optional, Default=None).', - default=None, -) -@cliutils.arg( - '--share-network', - '--share_network', - metavar='', - action='single_alias', - help='Optional network info ID or name.', - default=None, -) -@cliutils.arg( - '--description', - metavar='', - help='Optional share description. (Default=None)', - default=None, -) -@cliutils.arg( - '--share-type', - '--share_type', - '--volume-type', - '--volume_type', - metavar='', - 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='', - default=None, - action='single_alias', - help='Availability zone in which share should be created.', -) -@cliutils.arg( - '--share-group', - '--share_group', - '--group', - metavar='', - 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.arg( - '--scheduler-hints', - '--scheduler_hints', - '--sh', - metavar='', - nargs='*', - help='Scheduler hints for the share as key=value pairs, ' - 'possible keys are same_host, different_host, ' - 'value must be share_name or share_id.', - default=None, -) -@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) - - snapshot = None - if args.snapshot_id: - snapshot = _find_share_snapshot(cs, args.snapshot_id).id - - if args.name: - if args.name.capitalize() == 'None': - raise exceptions.CommandError( - "Share name cannot be with the value 'None'" - ) - - if not args.share_type: - try: - _find_share_type(cs, "default") - except exceptions.CommandError: - msg = ( - "There is no default share type available. You must pick " - "a valid share type to create a share." - ) - raise exceptions.CommandError(msg) - - scheduler_hints = {} - if args.scheduler_hints: - scheduler_hints = _extract_key_value_options(args, 'scheduler_hints') - same_host_hint_shares = scheduler_hints.get('same_host') - different_host_hint_shares = scheduler_hints.get('different_host') - if same_host_hint_shares: - same_host_hint_shares = [ - _find_share(cs, sh).id - for sh in same_host_hint_shares.split(',') - ] - scheduler_hints['same_host'] = ','.join(same_host_hint_shares) - if different_host_hint_shares: - different_host_hint_shares = [ - _find_share(cs, sh).id - for sh in different_host_hint_shares.split(',') - ] - scheduler_hints['different_host'] = ','.join( - different_host_hint_shares - ) - - share = cs.shares.create( - args.share_protocol, - args.size, - snapshot, - 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, - scheduler_hints=scheduler_hints, - ) - - if args.wait: - share = _wait_for_share_status(cs, share) - - _print_share(cs, share) - - -@api_versions.wraps("2.29") -@cliutils.arg( - 'share', metavar='', help='Name or ID of share to migrate.' -) -@cliutils.arg( - 'host', - metavar='', - 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='', - 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='', - 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='', - 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='', - 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='', - 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='', - 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='', - 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='', - 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='', 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='', help='Name or ID of the share to modify.' -) -@cliutils.arg( - '--task-state', - '--task_state', - '--state', - metavar='', - 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='', - 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='', - help='ID of the share server to check if the migration is possible.', -) -@cliutils.arg( - 'host', - metavar='', - help="Destination to migrate the share server to. Use the format " - "'@'.", -) -@cliutils.arg( - '--preserve-snapshots', - '--preserve_snapshots', - action='single_alias', - metavar='', - choices=['True', 'False'], - required=True, - help="Set to True if snapshots must be preserved at the migration " - "destination.", -) -@cliutils.arg( - '--writable', - metavar='', - 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='', - 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='', - 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='', - help='ID of the share server to migrate.', -) -@cliutils.arg( - 'host', - metavar='', - help="Destination to migrate the share server to. Use the format " - "'@'.", -) -@cliutils.arg( - '--preserve-snapshots', - '--preserve_snapshots', - action='single_alias', - metavar='', - choices=['True', 'False'], - required=True, - help="Set to True if snapshots must be preserved at the migration " - "destination.", -) -@cliutils.arg( - '--writable', - metavar='', - choices=['True', 'False'], - required=True, - help="Enforces migration to keep all its shares writable while contents " - "are being moved.", -) -@cliutils.arg( - '--nondisruptive', - metavar='', - choices=['True', 'False'], - required=True, - help="Enforces migration to be nondisruptive.", -) -@cliutils.arg( - '--new_share_network', - '--new-share-network', - metavar='', - 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='', - 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='', - 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='', - help='ID of share server to complete migration.', -) -@cliutils.arg( - '--task-state', - '--task_state', - '--state', - metavar='', - 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='', - 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='', - help='Name or ID of the share to update metadata on.', -) -@cliutils.arg( - 'action', - metavar='', - choices=['set', 'unset'], - help="Actions: 'set' or 'unset'.", -) -@cliutils.arg( - 'metadata', - metavar='', - nargs='+', - default=[], - help='Metadata to set or unset (only key is necessary to 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': - share.set_metadata(metadata) - elif args.action == 'unset': - share.delete_metadata(sorted(list(metadata), reverse=True)) - - -@cliutils.arg('share', metavar='', 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 = share.get_metadata()._info - cliutils.print_dict(metadata, 'Property') - - -@cliutils.arg( - 'share', - metavar='', - help='Name or ID of the share to update metadata on.', -) -@cliutils.arg( - 'metadata', - metavar='', - 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='', help='Name or ID of the share.') -@cliutils.arg( - '--columns', - metavar='', - 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='', help='Name or ID of the share.') -@cliutils.arg( - 'export_location', - metavar='', - 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='', - type=str, - help='manage-share service host: some.host@driver#pool.', -) -@cliutils.arg( - 'protocol', - metavar='', - type=str, - help='Protocol of the share to manage, such as NFS or CIFS.', -) -@cliutils.arg( - 'export_path', - metavar='', - 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='', - help='Optional share name. (Default=None)', - default=None, -) -@cliutils.arg( - '--description', - metavar='', - help='Optional share description. (Default=None)', - default=None, -) -@cliutils.arg( - '--share_type', - '--share-type', - metavar='', - default=None, - action='single_alias', - help='Optional share type assigned to share. (Default=None)', -) -@cliutils.arg( - '--driver_options', - '--driver-options', - type=str, - nargs='*', - metavar='', - 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='', - 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)", -) -@cliutils.arg('--wait', action='store_true', help='Wait for share management') -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, - ) - - if args.wait: - share = _wait_for_resource_status( - cs, share, resource_type='share', expected_status='available' - ) - _print_share(cs, share) - - -@api_versions.wraps("2.49") -@cliutils.arg( - 'host', - metavar='', - type=str, - help='Backend name as "@".', -) -@cliutils.arg( - 'share_network', - metavar='', - help="Share network where share server has network allocations in.", -) -@cliutils.arg( - 'identifier', - metavar='', - 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='', - 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='', - 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, -) -@cliutils.arg( - '--wait', - action='store_true', - default='False', - help='Wait for share server to manage', -) -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 - ) - - if args.wait: - try: - _wait_for_resource_status( - cs, - share_server, - resource_type='share_server', - expected_status='active', - ) - except exceptions.CommandError as e: - print(e, file=sys.stderr) - - cliutils.print_dict(share_server._info) - - -@cliutils.arg( - 'share_server_id', - metavar='', - help='ID of the share server to modify.', -) -@cliutils.arg( - '--state', - metavar='', - 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='', type=str, help='Name or ID of the share.' -) -@cliutils.arg( - 'provider_location', - metavar='', - type=str, - help='Provider location of the snapshot on the backend.', -) -@cliutils.arg( - '--name', - metavar='', - help='Optional snapshot name (Default=None).', - default=None, -) -@cliutils.arg( - '--description', - metavar='', - help='Optional snapshot description (Default=None).', - default=None, -) -@cliutils.arg( - '--driver_options', - '--driver-options', - type=str, - nargs='*', - metavar='', - action='single_alias', - help='Optional driver options as key=value pairs (Default=None).', - default=None, -) -@cliutils.arg( - '--wait', - action='store_true', - default=False, - help='Wait for share snapshot to be managed', -) -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, - ) - - if args.wait: - try: - _wait_for_snapshot_status( - cs, share_snapshot, expected_status='available' - ) - except exceptions.CommandError as e: - print(e, file=sys.stderr) - _print_share_snapshot(cs, share_snapshot) - - -@cliutils.arg('share', metavar='', help='Name or ID of the share(s).') -@cliutils.arg( - '--wait', action='store_true', help='Wait for share unmanagement' -) -def do_unmanage(cs, args): - """Unmanage share (Admin only).""" - share_ref = _find_share(cs, args.share) - share_ref.unmanage() - if args.wait: - _wait_for_share_status(cs, share_ref, expected_status='unmanaged') - - -@api_versions.wraps("2.49") -@cliutils.arg( - 'share_server', - metavar='', - 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.", -) -@cliutils.arg( - '--wait', - action='store_true', - default=False, - help='Wait for share server(s) to be unmanaged', -) -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) - if args.wait: - share_server_ref = _find_share_server(cs, server) - _wait_for_resource_status( - cs, - share_server_ref, - resource_type='share_server', - expected_status='unmanaged', - ) - except Exception as e: - failure_count += 1 - print( - f"Unmanage for share server {server} failed: {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='', - nargs='+', - help='Name or ID of the snapshot(s).', -) -@cliutils.arg( - '--wait', - action='store_true', - default=False, - help='Wait for share snapshot to be unmanaged', -) -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() - if args.wait: - _wait_for_snapshot_status( - cs, snapshot_ref, expected_status='deleted' - ) - except Exception as e: - failure_count += 1 - print( - f"Unmanage for share snapshot {snapshot} failed: {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='', - help='Name or ID of the snapshot to restore. The snapshot must be the ' - 'most recent one known to manila.', -) -@cliutils.arg( - '--wait', - action='store_true', - default=False, - help='Wait for share to be reverted from snapshot.', -) -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) - if args.wait: - _wait_for_share_status(cs, share) - - -@cliutils.arg( - 'share', metavar='', nargs='+', help='Name or ID of the share(s).' -) -@cliutils.arg( - '--share-group', - '--share_group', - '--group', - metavar='', - 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(f"Delete for share {share} failed: {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='', - nargs='+', - help='Name or ID of the share(s) to force delete.', -) -@cliutils.arg('--wait', action='store_true', help='Wait for share to delete') -@cliutils.service_type('sharev2') -def do_force_delete(cs, args): - """Attempt force-delete of share, regardless of state (Admin only).""" - failure_count = 0 - shares_to_delete = [] - for share in args.share: - try: - share_ref = _find_share(cs, share) - shares_to_delete.append(share_ref) - share_ref.force_delete() - except Exception as e: - failure_count += 1 - print(f"Delete for share {share} failed: {e}", file=sys.stderr) - if failure_count == len(args.share): - raise exceptions.CommandError( - "Unable to force delete any of 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='', nargs='+', help='Name or ID of the share(s).' -) -@cliutils.service_type('sharev2') -@api_versions.wraps("2.69") -def do_soft_delete(cs, args): - """Soft delete one or more shares.""" - failure_count = 0 - - for share in args.share: - try: - share_ref = _find_share(cs, share) - cs.shares.soft_delete(share_ref) - except Exception as e: - failure_count += 1 - print( - f"Soft deletion of share {share} failed: {e}", - file=sys.stderr, - ) - - if failure_count == len(args.share): - raise exceptions.CommandError( - "Unable to soft delete any of the specified shares." - ) - - -@cliutils.arg( - 'share', metavar='', nargs='+', help='Name or ID of the share(s).' -) -@cliutils.service_type('sharev2') -@api_versions.wraps("2.69") -def do_restore(cs, args): - """Restore one or more shares from recycle bin.""" - failure_count = 0 - - for share in args.share: - try: - share_ref = _find_share(cs, share) - cs.shares.restore(share_ref) - except Exception as e: - failure_count += 1 - print( - f"Restoration of share {share} failed: {e}", - file=sys.stderr, - ) - - if failure_count == len(args.share): - raise exceptions.CommandError( - "Unable to restore any of the specified shares." - ) - - -@api_versions.wraps("1.0", "2.8") -@cliutils.arg('share', metavar='', 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='', 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='', help='Name or ID of the NAS share to modify.' -) -@cliutils.arg( - 'access_type', - metavar='', - help='Access rule type (only "ip", "user"(user or group), "cert" or ' - '"cephx" are supported).', -) -@cliutils.arg( - 'access_to', metavar='', help='Value that defines access.' -) -@cliutils.arg( - '--access-level', - '--access_level', # alias - metavar='', - 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='', - help='Space Separated list of key=value pairs of metadata items. ' - 'OPTIONAL: Default=None. Available only for microversion >= 2.45.', - default=None, -) -@cliutils.arg( - '--wait', - action='store_true', - help='Wait for share access to become active', -) -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 - ) - if args.wait: - try: - if not cs.api_version.matches( - api_versions.APIVersion("2.45"), api_versions.APIVersion() - ): - raise exceptions.CommandError( - "Waiting on the allowing access operation is only " - "available for API versions equal to or greater than 2.45." - ) - access_id = access.get('id') - share_access_rule = cs.share_access_rules.get(access_id) - access = _wait_for_resource_status( - cs, - share_access_rule, - resource_type='share_access_rule', - expected_status='active', - status_attr='state', - )._info - except exceptions.CommandError as e: - print(e, file=sys.stderr) - cliutils.print_dict(access) - - -@api_versions.wraps("2.45") -@cliutils.arg( - 'access_id', metavar='', 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='', help='ID of the NAS share access rule.' -) -@cliutils.arg( - 'action', - metavar='', - choices=['set', 'unset'], - help="Actions: 'set' or 'unset'.", -) -@cliutils.arg( - 'metadata', - metavar='', - 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='', - help='Name or ID of the share snapshot to allow access to.', -) -@cliutils.arg( - 'access_type', - metavar='', - help='Access rule type (only "ip", "user"(user or group), "cert" or ' - '"cephx" are supported).', -) -@cliutils.arg( - 'access_to', metavar='', 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='', help='Name or ID of the NAS share to modify.' -) -@cliutils.arg( - 'id', metavar='', 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='', - help='Name or ID of the share snapshot to deny access to.', -) -@cliutils.arg( - 'id', - metavar='', - 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( - f"Failed to remove rule {access_id}: {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='', help='Name or ID of the share.') -@cliutils.arg( - '--columns', - metavar='', - 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='', help='Name or ID of the share.') -@cliutils.arg( - '--columns', - metavar='', - 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='', help='Name or ID of the share.') -@cliutils.arg( - '--columns', - metavar='', - 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='', - 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='', - help='Name or ID of the share snapshot to list access of.', -) -@cliutils.arg( - '--columns', - metavar='', - 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='', - type=str, - default=None, - help='Filter results by name.', -) -@cliutils.arg( - '--description', - metavar='', - type=str, - default=None, - help='Filter results by description. ' - 'Available only for microversion >= 2.36.', -) -@cliutils.arg( - '--name~', - metavar='', - type=str, - default=None, - help='Filter results matching a share name pattern. ' - 'Available only for microversion >= 2.36.', -) -@cliutils.arg( - '--description~', - metavar='', - type=str, - default=None, - help='Filter results matching a share description pattern. ' - 'Available only for microversion >= 2.36.', -) -@cliutils.arg( - '--status', - metavar='', - 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='', - type=str, - default=None, - action='single_alias', - help='Filter results by share server ID (Admin only).', -) -@cliutils.arg( - '--metadata', - type=str, - nargs='*', - metavar='', - 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='', - 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='', - 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='', - type=int, - default=None, - help='Maximum number of shares to return. OPTIONAL: Default=None.', -) -@cliutils.arg( - '--offset', - metavar='', - 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='', - type=str, - default=None, - action='single_alias', - help=( - f'Key to be sorted, available keys are ' - f'{constants.SHARE_SORT_KEY_VALUES}. ' - 'OPTIONAL: Default=None.' - ), -) -@cliutils.arg( - '--sort-dir', - '--sort_dir', # alias - metavar='', - type=str, - default=None, - action='single_alias', - help=f'Sort direction, available values are {constants.SORT_DIR_VALUES}. ' - 'OPTIONAL: Default=None.', -) -@cliutils.arg( - '--snapshot', - metavar='', - type=str, - default=None, - help='Filer results by snapshot name or id, that was used for share.', -) -@cliutils.arg( - '--host', metavar='', default=None, help='Filter results by host.' -) -@cliutils.arg( - '--share-network', - '--share_network', # alias - metavar='', - type=str, - default=None, - action='single_alias', - help='Filter results by share-network name or id.', -) -@cliutils.arg( - '--project-id', - '--project_id', # alias - metavar='', - 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='', - type=str, - default=None, - action='single_alias', - help='Filter results by share group name or ID (Default=None).', -) -@cliutils.arg( - '--columns', - metavar='', - 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='', - 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='', - choices=['True', 'False'], - default=False, - help='Display total number of shares to return. ' - 'Available only for microversion >= 2.42.', -) -@cliutils.arg( - '--soft-deleted', - '--soft_deleted', - action='store_true', - help='Get shares in recycle bin. If this parameter is set to ' - 'True(Default=False), will only show shares in recycle bin. ' - 'Available only for microversion >= 2.69.', -) -@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, allow_empty_key=False), - '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 cs.api_version.matches( - api_versions.APIVersion("2.69"), api_versions.APIVersion() - ): - if args.soft_deleted: - search_opts['is_soft_deleted'] = args.soft_deleted - elif args.soft_deleted: - raise exceptions.CommandError( - "Filtering by is_soft_deleted is only " - "available with manila API version >= 2.69" - ) - - 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, - ) - # When shell input is "--metadata None", filter metadata={} - if shares and args.metadata: - if "None" in args.metadata: - shares = [share for share in shares if share.metadata == {}] - # 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(f"Shares in total: {total_count}") - - with cs.shares.completion_cache( - 'uuid', manilaclient.v2.shares.Share, mode="w" - ): - for share in shares: - cs.shares.write_to_completion_cache('uuid', share.id) - - with cs.shares.completion_cache( - 'name', manilaclient.v2.shares.Share, mode="w" - ): - for share in shares: - if share.name is not None: - cs.shares.write_to_completion_cache('name', share.name) - - -@cliutils.arg( - '--share-id', - '--share_id', # alias - metavar='', - default=None, - action='single_alias', - help='Filter results by share ID.', -) -@cliutils.arg( - '--columns', - metavar='', - 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='', - 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='', 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='', 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='', - nargs='+', - help='Name or ID of the instance(s) to force delete.', -) -@api_versions.wraps("2.3") -@cliutils.arg( - '--wait', action='store_true', help='Wait for share instance deletion' -) -@cliutils.service_type('sharev2') -def do_share_instance_force_delete(cs, args): - """Force-delete the share instance, regardless of state (Admin only).""" - failure_count = 0 - instances_to_delete = [] - for instance in args.instance: - try: - instance_ref = _find_share_instance(cs, instance) - instances_to_delete.append(instance_ref) - instance_ref.force_delete() - except Exception as e: - failure_count += 1 - print( - f"Delete for share instance {instance} failed: {e}", - file=sys.stderr, - ) - if failure_count == len(args.instance): - raise exceptions.CommandError( - "Unable to force delete any of specified share instances." - ) - if args.wait: - for instance in instances_to_delete: - try: - _wait_for_resource_status( - cs, - instance, - resource_type='share_instance', - expected_status='deleted', - ) - except exceptions.CommandError as e: - print(e, file=sys.stderr) - - -@cliutils.arg( - 'instance', - metavar='', - help='Name or ID of the share instance to modify.', -) -@cliutils.arg( - '--state', - metavar='', - 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='', help='Name or ID of the share instance.' -) -@cliutils.arg( - '--columns', - metavar='', - 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='', help='Name or ID of the share instance.' -) -@cliutils.arg( - 'export_location', - metavar='', - 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='', - type=str, - default=None, - help='Filter results by name.', -) -@cliutils.arg( - '--description', - metavar='', - type=str, - default=None, - help='Filter results by description. ' - 'Available only for microversion >= 2.36.', -) -@cliutils.arg( - '--status', - metavar='', - default=None, - help='Filter results by status.', -) -@cliutils.arg( - '--share-id', - '--share_id', # alias - metavar='', - 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='', - type=int, - default=None, - help='Maximum number of share snapshots to return. ' - 'OPTIONAL: Default=None.', -) -@cliutils.arg( - '--offset', - metavar='', - 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='', - type=str, - default=None, - action='single_alias', - help=( - f'Key to be sorted, available keys are ' - f'{constants.SNAPSHOT_SORT_KEY_VALUES}. ' - 'Default=None.' - ), -) -@cliutils.arg( - '--sort-dir', - '--sort_dir', # alias - metavar='', - type=str, - default=None, - action='single_alias', - help=f'Sort direction, available values are {constants.SORT_DIR_VALUES}. ' - 'OPTIONAL: Default=None.', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,name".', -) -@cliutils.arg( - '--name~', - metavar='', - type=str, - default=None, - help='Filter results matching a share snapshot name pattern. ' - 'Available only for microversion >= 2.36.', -) -@cliutils.arg( - '--description~', - metavar='', - type=str, - default=None, - help='Filter results matching a share snapshot description pattern. ' - 'Available only for microversion >= 2.36.', -) -@cliutils.arg( - '--metadata', - metavar='', - type=str, - default=None, - nargs='*', - help='Filters results by a metadata key and value. OPTIONAL: ' - 'Default=None, Available only for microversion >= 2.73. ', -) -@cliutils.arg( - '--count', - dest='count', - metavar='', - choices=['True', 'False'], - default=False, - help='Display total number of share snapshots to return. ' - 'Available only for microversion >= 2.79.', -) -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, - 'metadata': _extract_metadata(args), - } - 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 args.count and cs.api_version.matches( - api_versions.APIVersion(), api_versions.APIVersion("2.78") - ): - raise exceptions.CommandError( - "Display total number of share snapshots is only " - "available with manila API version >= 2.79" - ) - - total_count = 0 - if strutils.bool_from_string(args.count, strict=True): - search_opts['with_count'] = args.count - snapshots, total_count = cs.share_snapshots.list( - search_opts=search_opts, - sort_key=args.sort_key, - sort_dir=args.sort_dir, - ) - else: - snapshots = cs.share_snapshots.list( - search_opts=search_opts, - sort_key=args.sort_key, - sort_dir=args.sort_dir, - ) - - cliutils.print_list(snapshots, list_of_keys, sortby_index=None) - if args.count: - print(f"Share snapshots in total: {total_count}") - - -@cliutils.arg( - 'snapshot', metavar='', help='Name or ID of the snapshot.' -) -def do_snapshot_show(cs, args): - """Show details about a snapshot.""" - snapshot = _find_share_snapshot(cs, args.snapshot) - export_locations = cs.share_snapshot_export_locations.list( - snapshot=snapshot - ) - snapshot._info['export_locations'] = export_locations - _print_share_snapshot(cs, snapshot) - - -@api_versions.wraps("2.32") -@cliutils.arg( - 'snapshot', metavar='', help='Name or ID of the snapshot.' -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,path".', -) -def do_snapshot_export_location_list(cs, args): - """List export locations of a given snapshot.""" - if args.columns is not None: - list_of_keys = _split_columns(columns=args.columns) - else: - list_of_keys = [ - 'ID', - 'Path', - ] - snapshot = _find_share_snapshot(cs, args.snapshot) - export_locations = cs.share_snapshot_export_locations.list(snapshot) - cliutils.print_list(export_locations, list_of_keys) - - -@api_versions.wraps("2.32") -@cliutils.arg( - 'instance', - metavar='', - help='Name or ID of the snapshot instance.', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,path,is_admin_only".', -) -def do_snapshot_instance_export_location_list(cs, args): - """List export locations of a given snapshot instance.""" - if args.columns is not None: - list_of_keys = _split_columns(columns=args.columns) - else: - list_of_keys = [ - 'ID', - 'Path', - 'Is Admin only', - ] - instance = _find_share_snapshot_instance(cs, args.instance) - export_locations = cs.share_snapshot_instance_export_locations.list( - instance - ) - cliutils.print_list(export_locations, list_of_keys) - - -@api_versions.wraps("2.32") -@cliutils.arg( - 'snapshot', metavar='', help='Name or ID of the snapshot.' -) -@cliutils.arg( - 'export_location', - metavar='', - help='ID of the share snapshot export location.', -) -def do_snapshot_export_location_show(cs, args): - """Show export location of the share snapshot.""" - snapshot = _find_share_snapshot(cs, args.snapshot) - export_location = cs.share_snapshot_export_locations.get( - args.export_location, snapshot - ) - view_data = export_location._info.copy() - cliutils.print_dict(view_data) - - -@api_versions.wraps("2.32") -@cliutils.arg( - 'snapshot_instance', - metavar='', - help='ID of the share snapshot instance.', -) -@cliutils.arg( - 'export_location', - metavar='', - help='ID of the share snapshot instance export location.', -) -def do_snapshot_instance_export_location_show(cs, args): - """Show export location of the share instance snapshot.""" - snapshot_instance = _find_share_snapshot_instance( - cs, args.snapshot_instance - ) - export_location = cs.share_snapshot_instance_export_locations.get( - args.export_location, snapshot_instance - ) - - view_data = export_location._info.copy() - cliutils.print_dict(view_data) - - -@cliutils.arg( - 'share', metavar='', help='Name or ID of the share to snapshot.' -) -@cliutils.arg( - '--force', - metavar='', - help='Optional flag to indicate whether ' - 'to snapshot a share even if it\'s busy. ' - '(Default=False)', - default=False, -) -@cliutils.arg( - '--name', - metavar='', - default=None, - help='Optional snapshot name. (Default=None)', -) -@cliutils.arg( - '--description', - metavar='', - default=None, - help='Optional snapshot description. (Default=None)', -) -@cliutils.arg( - '--wait', - action='store_true', - default=False, - help='Wait for snapshot to be created.', -) -def do_snapshot_create(cs, args): - """Add a new snapshot.""" - share = _find_share(cs, args.share) - snapshot = cs.share_snapshots.create( - share, args.force, args.name, args.description - ) - if args.wait: - try: - _wait_for_snapshot_status( - cs, snapshot, expected_status='available' - ) - except exceptions.CommandError as e: - print(e, file=sys.stderr) - _print_share_snapshot(cs, snapshot) - - -@cliutils.arg( - 'share', metavar='', help='Name or ID of the share to rename.' -) -@cliutils.arg( - '--name', metavar='', default=None, help='New name for the share.' -) -@cliutils.arg( - '--description', - metavar='', - help='Optional share description. (Default=None)', - default=None, -) -@cliutils.arg( - '--is-public', - '--is_public', # alias - metavar='', - default=None, - type=str, - action="single_alias", - help='Public share is visible for all projects.', -) -def do_update(cs, args): - """Rename a share.""" - kwargs = {} - - if args.name is not None: - kwargs['display_name'] = args.name - if args.description is not None: - kwargs['display_description'] = args.description - if args.is_public is not None: - kwargs['is_public'] = strutils.bool_from_string( - args.is_public, strict=True - ) - if not kwargs: - msg = "Must supply name, description or is_public value." - raise exceptions.CommandError(msg) - _find_share(cs, args.share).update(**kwargs) - - -@cliutils.arg( - 'snapshot', - metavar='', - help='Name or ID of the snapshot to rename.', -) -@cliutils.arg( - 'name', nargs='?', metavar='', help='New name for the snapshot.' -) -@cliutils.arg( - '--description', - metavar='', - help='Optional snapshot description. (Default=None)', - default=None, -) -def do_snapshot_rename(cs, args): - """Rename a snapshot.""" - kwargs = {} - - if args.name is not None: - kwargs['display_name'] = args.name - if args.description is not None: - kwargs['display_description'] = args.description - if not kwargs: - msg = "Must supply either name or description." - raise exceptions.CommandError(msg) - _find_share_snapshot(cs, args.snapshot).update(**kwargs) - - -@cliutils.arg( - 'snapshot', - metavar='', - nargs='+', - help='Name or ID of the snapshot(s) to delete.', -) -@cliutils.arg( - '--wait', - action='store_true', - default=False, - help='Wait for snapshot to be deleted', -) -def do_snapshot_delete(cs, args): - """Remove one or more snapshots.""" - failure_count = 0 - - for snapshot in args.snapshot: - try: - snapshot_ref = _find_share_snapshot(cs, snapshot) - cs.share_snapshots.delete(snapshot_ref) - if args.wait: - _wait_for_snapshot_status( - cs, snapshot, expected_status='deleted' - ) - except Exception as e: - failure_count += 1 - print( - f"Delete for snapshot {snapshot} failed: {e}", - file=sys.stderr, - ) - - if failure_count == len(args.snapshot): - raise exceptions.CommandError( - "Unable to delete any of the specified snapshots." - ) - - -@cliutils.arg( - 'snapshot', - metavar='', - nargs='+', - help='Name or ID of the snapshot(s) to force delete.', -) -@cliutils.arg( - '--wait', action='store_true', help='Wait for snapshot to delete' -) -@cliutils.service_type('sharev2') -def do_snapshot_force_delete(cs, args): - """Attempt force-deletion of one or more snapshots. - - Regardless of the state (Admin only). - """ - failure_count = 0 - snapshots_to_delete = [] - - for snapshot in args.snapshot: - try: - snapshot_ref = _find_share_snapshot(cs, snapshot) - snapshots_to_delete.append(snapshot_ref) - snapshot_ref.force_delete() - except Exception as e: - failure_count += 1 - print( - f"Delete for snapshot {snapshot} failed: {e}", - file=sys.stderr, - ) - - if failure_count == len(args.snapshot): - raise exceptions.CommandError( - "Unable to force delete any of the specified snapshots." - ) - if args.wait: - for snapshot in snapshots_to_delete: - try: - _wait_for_resource_status( - cs, - snapshot, - resource_type='snapshot', - expected_status='deleted', - ) - except exceptions.CommandError as e: - print(e, file=sys.stderr) - - -@cliutils.arg( - 'snapshot', - metavar='', - help='Name or ID of the snapshot to modify.', -) -@cliutils.arg( - '--state', - metavar='', - default='available', - help=( - 'Indicate which state to assign the snapshot. ' - 'Options include available, error, creating, deleting, ' - 'error_deleting. If no state is provided, ' - 'available will be used.' - ), -) -def do_snapshot_reset_state(cs, args): - """Explicitly update the state of a snapshot (Admin only).""" - snapshot = _find_share_snapshot(cs, args.snapshot) - snapshot.reset_state(args.state) - - -@api_versions.wraps("2.19") -@cliutils.arg( - '--snapshot', - metavar='', - default=None, - help='Filter results by share snapshot ID.', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id".', -) -@cliutils.arg( - '--detailed', - metavar='', - default=False, - help='Show detailed information about snapshot instances. (Default=False)', -) -def do_snapshot_instance_list(cs, args): - """List share snapshot instances.""" - snapshot = ( - _find_share_snapshot(cs, args.snapshot) if args.snapshot else None - ) - if args.columns is not None: - list_of_keys = _split_columns(columns=args.columns) - elif args.detailed: - list_of_keys = [ - 'ID', - 'Snapshot ID', - 'Status', - 'Created_at', - 'Updated_at', - 'Share_id', - 'Share_instance_id', - 'Progress', - 'Provider_location', - ] - else: - list_of_keys = ['ID', 'Snapshot ID', 'Status'] - - instances = cs.share_snapshot_instances.list( - detailed=args.detailed, snapshot=snapshot - ) - - cliutils.print_list(instances, list_of_keys) - - -@api_versions.wraps("2.19") -@cliutils.arg( - 'snapshot_instance', - metavar='', - help='ID of the share snapshot instance.', -) -def do_snapshot_instance_show(cs, args): - """Show details about a share snapshot instance.""" - snapshot_instance = _find_share_snapshot_instance( - cs, args.snapshot_instance - ) - export_locations = cs.share_snapshot_instance_export_locations.list( - snapshot_instance - ) - snapshot_instance._info['export_locations'] = export_locations - _print_share_snapshot(cs, snapshot_instance) - - -@cliutils.arg( - 'snapshot_instance', - metavar='', - help='ID of the snapshot instance to modify.', -) -@cliutils.arg( - '--state', - metavar='', - default='available', - help=( - 'Indicate which state to assign the snapshot instance. ' - 'Options include available, error, creating, deleting, ' - 'error_deleting. If no state is provided, available ' - 'will be used.' - ), -) -@api_versions.wraps("2.19") -def do_snapshot_instance_reset_state(cs, args): - """Explicitly update the state of a share snapshot instance.""" - snapshot_instance = _find_share_snapshot_instance( - cs, args.snapshot_instance - ) - snapshot_instance.reset_state(args.state) - - -@cliutils.arg( - 'share', metavar='', help='Name or ID of the share to modify.' -) -@cliutils.arg( - '--state', - metavar='', - default='available', - help=( - 'Indicate which state to assign the share. Options include ' - 'available, error, creating, deleting, error_deleting. If no ' - 'state is provided, available will be used.' - ), -) -def do_reset_state(cs, args): - """Explicitly update the state of a share (Admin only).""" - share = _find_share(cs, args.share) - share.reset_state(args.state) - - -@api_versions.wraps("1.0", "2.25") -@cliutils.arg( - '--neutron-net-id', - '--neutron-net_id', - '--neutron_net_id', - '--neutron_net-id', - metavar='', - default=None, - action='single_alias', - help="Neutron network ID. Used to set up network for share servers.", -) -@cliutils.arg( - '--neutron-subnet-id', - '--neutron-subnet_id', - '--neutron_subnet_id', - '--neutron_subnet-id', - metavar='', - default=None, - action='single_alias', - help="Neutron subnet ID. Used to set up network for share servers. " - "This subnet should belong to specified neutron network.", -) -@cliutils.arg( - '--name', metavar='', default=None, help="Share network name." -) -@cliutils.arg( - '--description', - metavar='', - default=None, - help="Share network description.", -) -def do_share_network_create(cs, args): - """Create a share network to export shares to.""" - values = { - 'neutron_net_id': args.neutron_net_id, - 'neutron_subnet_id': args.neutron_subnet_id, - 'name': args.name, - 'description': args.description, - } - share_network = cs.share_networks.create(**values) - info = share_network._info.copy() - cliutils.print_dict(info) - - -@api_versions.wraps("2.26") # noqa -@cliutils.arg( - '--neutron-net-id', - '--neutron-net_id', - '--neutron_net_id', - '--neutron_net-id', - metavar='', - default=None, - action='single_alias', - help="Neutron network ID. Used to set up network for share servers.", -) -@cliutils.arg( - '--neutron-subnet-id', - '--neutron-subnet_id', - '--neutron_subnet_id', - '--neutron_subnet-id', - metavar='', - default=None, - action='single_alias', - help="Neutron subnet ID. Used to set up network for share servers. " - "This subnet should belong to specified neutron network.", -) -@cliutils.arg( - '--name', metavar='', default=None, help="Share network name." -) -@cliutils.arg( - '--description', - metavar='', - default=None, - help="Share network description.", -) -@cliutils.arg( - '--availability-zone', - '--availability_zone', - '--az', - metavar='', - default=None, - action='single_alias', - help="Availability zone in which the subnet should be created. Share " - "networks can have one or more subnets in different availability " - "zones when the driver is operating with " - "'driver_handles_share_servers' extra_spec set to True. Available " - "only for microversion >= 2.51. (Default=None)", -) -def do_share_network_create(cs, args): # noqa - """Create a share network to export shares to.""" - values = { - 'neutron_net_id': args.neutron_net_id, - 'neutron_subnet_id': args.neutron_subnet_id, - 'name': args.name, - 'description': args.description, - } - if cs.api_version >= api_versions.APIVersion("2.51"): - values['availability_zone'] = args.availability_zone - elif args.availability_zone: - raise exceptions.CommandError( - "Creating share networks with a given az is only " - "available with manila API version >= 2.51" - ) - share_network = cs.share_networks.create(**values) - info = share_network._info.copy() - cliutils.print_dict(info) - - -@api_versions.wraps("1.0", "2.25") -@cliutils.arg( - 'share_network', - metavar='', - help='Name or ID of share network to update.', -) -@cliutils.arg( - '--neutron-net-id', - '--neutron-net_id', - '--neutron_net_id', - '--neutron_net-id', - metavar='', - default=None, - action='single_alias', - help="Neutron network ID. Used to set up network for share servers.", -) -@cliutils.arg( - '--neutron-subnet-id', - '--neutron-subnet_id', - '--neutron_subnet_id', - '--neutron_subnet-id', - metavar='', - default=None, - action='single_alias', - help="Neutron subnet ID. Used to set up network for share servers. " - "This subnet should belong to specified neutron network.", -) -@cliutils.arg( - '--name', metavar='', default=None, help="Share network name." -) -@cliutils.arg( - '--description', - metavar='', - default=None, - help="Share network description.", -) -def do_share_network_update(cs, args): - """Update share network data.""" - values = { - 'neutron_net_id': args.neutron_net_id, - 'neutron_subnet_id': args.neutron_subnet_id, - 'name': args.name, - 'description': args.description, - } - share_network = _find_share_network(cs, args.share_network).update( - **values - ) - info = share_network._info.copy() - cliutils.print_dict(info) - - -@api_versions.wraps("2.26") # noqa -@cliutils.arg( - 'share_network', - metavar='', - help='Name or ID of share network to update.', -) -@cliutils.arg( - '--neutron-net-id', - '--neutron-net_id', - '--neutron_net_id', - '--neutron_net-id', - metavar='', - default=None, - action='single_alias', - help="Neutron network ID. Used to set up network for share servers. " - "This option is deprecated for microversion >= 2.51.", -) -@cliutils.arg( - '--neutron-subnet-id', - '--neutron-subnet_id', - '--neutron_subnet_id', - '--neutron_subnet-id', - metavar='', - default=None, - action='single_alias', - help="Neutron subnet ID. Used to set up network for share servers. " - "This subnet should belong to specified neutron network. " - "This option is deprecated for microversion >= 2.51.", -) -@cliutils.arg( - '--name', metavar='', default=None, help="Share network name." -) -@cliutils.arg( - '--description', - metavar='', - default=None, - help="Share network description.", -) -def do_share_network_update(cs, args): # noqa - """Update share network data.""" - - values = { - 'neutron_net_id': args.neutron_net_id, - 'neutron_subnet_id': args.neutron_subnet_id, - 'name': args.name, - 'description': args.description, - } - share_network = _find_share_network(cs, args.share_network).update( - **values - ) - info = share_network._info.copy() - cliutils.print_dict(info) - - -@cliutils.arg( - 'share_network', - metavar='', - help='Name or ID of the share network to show.', -) -def do_share_network_show(cs, args): - """Retrieve details for a share network.""" - share_network = _find_share_network(cs, args.share_network) - info = share_network._info.copy() - cliutils.print_dict(info) - - -@api_versions.wraps("1.0", "2.25") -@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( - '--project-id', - '--project_id', # alias - metavar='', - action='single_alias', - default=None, - help='Filter results by project ID.', -) -@cliutils.arg( - '--name', metavar='', default=None, help='Filter results by name.' -) -@cliutils.arg( - '--created-since', - '--created_since', # alias - metavar='', - action='single_alias', - default=None, - help='''Return only share networks created since given date. ''' - '''The date is in the format 'yyyy-mm-dd'.''', -) -@cliutils.arg( - '--created-before', - '--created_before', # alias - metavar='', - action='single_alias', - default=None, - help='''Return only share networks created until given date. ''' - '''The date is in the format 'yyyy-mm-dd'.''', -) -@cliutils.arg( - '--security-service', - '--security_service', # alias - metavar='', - action='single_alias', - default=None, - help='Filter results by attached security service.', -) -@cliutils.arg( - '--neutron-net-id', - '--neutron_net_id', - '--neutron_net-id', - '--neutron-net_id', # aliases - metavar='', - action='single_alias', - default=None, - help='Filter results by neutron net ID.', -) -@cliutils.arg( - '--neutron-subnet-id', - '--neutron_subnet_id', - '--neutron-subnet_id', # aliases - '--neutron_subnet-id', # alias - metavar='', - action='single_alias', - default=None, - help='Filter results by neutron subnet ID.', -) -@cliutils.arg( - '--network-type', - '--network_type', # alias - metavar='', - action='single_alias', - default=None, - help='Filter results by network type.', -) -@cliutils.arg( - '--segmentation-id', - '--segmentation_id', # alias - metavar='', - type=int, - action='single_alias', - default=None, - help='Filter results by segmentation ID.', -) -@cliutils.arg( - '--cidr', metavar='', default=None, help='Filter results by CIDR.' -) -@cliutils.arg( - '--ip-version', - '--ip_version', # alias - metavar='', - type=int, - action='single_alias', - default=None, - help='Filter results by IP version.', -) -@cliutils.arg( - '--offset', - metavar='', - type=int, - default=None, - help='Start position of share networks listing.', -) -@cliutils.arg( - '--limit', - metavar='', - type=int, - default=None, - help='Number of share networks to return per request.', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id".', -) -def do_share_network_list(cs, args): - """Get a list of network info.""" - all_projects = int( - os.environ.get( - "ALL_TENANTS", os.environ.get("ALL_PROJECTS", args.all_projects) - ) - ) - search_opts = { - 'all_tenants': all_projects, - 'project_id': args.project_id, - 'name': args.name, - 'created_since': args.created_since, - 'created_before': args.created_before, - 'neutron_net_id': args.neutron_net_id, - 'neutron_subnet_id': args.neutron_subnet_id, - 'network_type': args.network_type, - 'segmentation_id': args.segmentation_id, - 'cidr': args.cidr, - 'ip_version': args.ip_version, - 'offset': args.offset, - 'limit': args.limit, - } - if args.security_service: - search_opts['security_service_id'] = _find_security_service( - cs, args.security_service - ).id - share_networks = cs.share_networks.list(search_opts=search_opts) - fields = ['id', 'name'] - - if args.columns is not None: - fields = _split_columns(columns=args.columns) - - cliutils.print_list(share_networks, fields=fields) - - -@api_versions.wraps("2.26") # noqa -@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( - '--project-id', - '--project_id', # alias - metavar='', - action='single_alias', - default=None, - help='Filter results by project ID.', -) -@cliutils.arg( - '--name', - metavar='', - type=str, - default=None, - help='Filter results by name.', -) -@cliutils.arg( - '--description', - metavar='', - type=str, - default=None, - help='Filter results by description. ' - 'Available only for microversion >= 2.36.', -) -@cliutils.arg( - '--created-since', - '--created_since', # alias - metavar='', - action='single_alias', - default=None, - help='''Return only share networks created since given date. ''' - '''The date is in the format 'yyyy-mm-dd'.''', -) -@cliutils.arg( - '--created-before', - '--created_before', # alias - metavar='', - action='single_alias', - default=None, - help='''Return only share networks created until given date. ''' - '''The date is in the format 'yyyy-mm-dd'.''', -) -@cliutils.arg( - '--security-service', - '--security_service', # alias - metavar='', - action='single_alias', - default=None, - help='Filter results by attached security service.', -) -@cliutils.arg( - '--neutron-net-id', - '--neutron_net_id', - '--neutron_net-id', - '--neutron-net_id', # aliases - metavar='', - action='single_alias', - default=None, - help='Filter results by neutron net ID.', -) -@cliutils.arg( - '--neutron-subnet-id', - '--neutron_subnet_id', - '--neutron-subnet_id', # aliases - '--neutron_subnet-id', # alias - metavar='', - action='single_alias', - default=None, - help='Filter results by neutron subnet ID.', -) -@cliutils.arg( - '--network-type', - '--network_type', # alias - metavar='', - action='single_alias', - default=None, - help='Filter results by network type.', -) -@cliutils.arg( - '--segmentation-id', - '--segmentation_id', # alias - metavar='', - type=int, - action='single_alias', - default=None, - help='Filter results by segmentation ID.', -) -@cliutils.arg( - '--cidr', metavar='', default=None, help='Filter results by CIDR.' -) -@cliutils.arg( - '--ip-version', - '--ip_version', # alias - metavar='', - type=int, - action='single_alias', - default=None, - help='Filter results by IP version.', -) -@cliutils.arg( - '--offset', - metavar='', - type=int, - default=None, - help='Start position of share networks listing.', -) -@cliutils.arg( - '--limit', - metavar='', - type=int, - default=None, - help='Number of share networks to return per request.', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id".', -) -@cliutils.arg( - '--name~', - metavar='', - type=str, - default=None, - help='Filter results matching a share network name pattern. ' - 'Available only for microversion >= 2.36.', -) -@cliutils.arg( - '--description~', - metavar='', - type=str, - default=None, - help='Filter results matching a share network description pattern. ' - 'Available only for microversion >= 2.36.', -) -def do_share_network_list(cs, args): # noqa - """Get a list of share networks""" - all_projects = int( - os.environ.get( - "ALL_TENANTS", os.environ.get("ALL_PROJECTS", args.all_projects) - ) - ) - search_opts = { - 'all_tenants': all_projects, - 'project_id': args.project_id, - 'name': args.name, - 'created_since': args.created_since, - 'created_before': args.created_before, - 'neutron_net_id': args.neutron_net_id, - 'neutron_subnet_id': args.neutron_subnet_id, - 'network_type': args.network_type, - 'segmentation_id': args.segmentation_id, - 'cidr': args.cidr, - 'ip_version': args.ip_version, - 'offset': args.offset, - 'limit': args.limit, - } - 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 args.security_service: - search_opts['security_service_id'] = _find_security_service( - cs, args.security_service - ).id - share_networks = cs.share_networks.list(search_opts=search_opts) - fields = ['id', 'name'] - - if args.columns is not None: - fields = _split_columns(columns=args.columns) - - cliutils.print_list(share_networks, fields=fields) - - -@cliutils.arg( - 'share_network', - metavar='', - help='Share network name or ID.', -) -@cliutils.arg( - 'security_service', - metavar='', - help='Security service name or ID to associate with.', -) -def do_share_network_security_service_add(cs, args): - """Associate security service with share network.""" - share_network = _find_share_network(cs, args.share_network) - security_service = _find_security_service(cs, args.security_service) - cs.share_networks.add_security_service(share_network, security_service) - - -@cliutils.arg( - 'share_network', - metavar='', - help='Share network name or ID.', -) -@cliutils.arg( - 'security_service', - metavar='', - help='Security service name or ID to associate with.', -) -@cliutils.arg( - '--reset', - metavar='', - choices=['True', 'False'], - required=False, - default=False, - help='Reset and restart the check operation.(Optional, Default=False)', -) -@api_versions.wraps("2.63") -def do_share_network_security_service_add_check(cs, args): - """Associate security service with share network.""" - share_network = _find_share_network(cs, args.share_network) - security_service = _find_security_service(cs, args.security_service) - add_sec_service_result = cs.share_networks.add_security_service_check( - share_network, security_service, reset_operation=args.reset - ) - # result[0] is response code, result[1] is dict body - cliutils.print_dict(add_sec_service_result[1]) - - -@cliutils.arg( - 'share_network', - metavar='', - help='Share network name or ID.', -) -@cliutils.arg( - 'security_service', - metavar='', - help='Security service name or ID to dissociate.', -) -def do_share_network_security_service_remove(cs, args): - """Dissociate security service from share network.""" - share_network = _find_share_network(cs, args.share_network) - security_service = _find_security_service(cs, args.security_service) - cs.share_networks.remove_security_service(share_network, security_service) - - -@cliutils.arg( - 'share_network', - metavar='', - help='Share network name or ID.', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,name".', -) -def do_share_network_security_service_list(cs, args): - """Get list of security services associated with a given share network.""" - share_network = _find_share_network(cs, args.share_network) - search_opts = { - 'share_network_id': share_network.id, - } - security_services = cs.security_services.list(search_opts=search_opts) - fields = [ - 'id', - 'name', - 'status', - 'type', - ] - - if args.columns is not None: - fields = _split_columns(columns=args.columns) - - cliutils.print_list(security_services, fields=fields) - - -@cliutils.arg( - 'share_network', - metavar='', - help='Share network name or ID.', -) -@cliutils.arg( - 'current_security_service', - metavar='', - help='Current security service name or ID.', -) -@cliutils.arg( - 'new_security_service', - metavar='', - help='New security service name or ID.', -) -@api_versions.wraps("2.63") -def do_share_network_security_service_update(cs, args): - """Update a current security service to a new security service.""" - share_network = _find_share_network(cs, args.share_network) - current_security_service = _find_security_service( - cs, args.current_security_service - ) - new_security_service = _find_security_service( - cs, args.new_security_service - ) - cs.share_networks.update_share_network_security_service( - share_network, current_security_service, new_security_service - ) - - -@cliutils.arg( - 'share_network', - metavar='', - help='Share network name or ID.', -) -@cliutils.arg( - 'current_security_service', - metavar='', - help='Current security service name or ID.', -) -@cliutils.arg( - 'new_security_service', - metavar='', - help='New security service name or ID.', -) -@cliutils.arg( - '--reset', - metavar='', - choices=['True', 'False'], - required=False, - default=False, - help='Reset and start again the check operation.(Optional, Default=False)', -) -@api_versions.wraps("2.63") -def do_share_network_security_service_update_check(cs, args): - """Check if a security service update on the share network is supported. - - This call can be repeated until a successful result is obtained. - """ - share_network = _find_share_network(cs, args.share_network) - current_security_service = _find_security_service( - cs, args.current_security_service - ) - new_security_service = _find_security_service( - cs, args.new_security_service - ) - share_network_update_check = ( - cs.share_networks.update_share_network_security_service_check( - share_network, - current_security_service, - new_security_service, - reset_operation=args.reset, - ) - ) - # result[0] is response code, result[1] is dict body - cliutils.print_dict(share_network_update_check[1]) - - -@cliutils.arg( - 'share_network', - metavar='', - help='Share network name or ID.', -) -@cliutils.arg( - '--state', - metavar='', - default=constants.STATUS_ACTIVE, - help=( - 'Indicate which state to assign the share network. Options include ' - 'active, error, network change. If no state is provided, active ' - 'will be used.' - ), -) -@api_versions.wraps("2.63") -def do_share_network_reset_state(cs, args): - """Explicitly update the state of a share network (Admin only).""" - share_network = _find_share_network(cs, args.share_network) - cs.share_networks.reset_state(share_network, args.state) - - -@cliutils.arg( - 'share_network', - metavar='', - help='Share network name or ID.', -) -@cliutils.arg( - '--neutron-net-id', - '--neutron-net_id', - '--neutron_net_id', - '--neutron_net-id', - metavar='', - default=None, - action='single_alias', - help="Neutron network ID. Used to set up network for share servers. " - "Optional, Default = None.", -) -@cliutils.arg( - '--neutron-subnet-id', - '--neutron-subnet_id', - '--neutron_subnet_id', - '--neutron_subnet-id', - metavar='', - default=None, - action='single_alias', - help="Neutron subnet ID. Used to set up network for share servers. " - "This subnet should belong to specified neutron network. " - "Optional, Default = None.", -) -@cliutils.arg( - '--availability-zone', - '--availability_zone', - '--az', - default=None, - action='single_alias', - metavar='', - help='Optional availability zone that the subnet is available within ' - '(Default=None). If None, the subnet will be considered as being ' - 'available across all availability zones.', -) -def do_share_network_subnet_create(cs, args): - """Add a new subnet into a share network.""" - if xor(bool(args.neutron_net_id), bool(args.neutron_subnet_id)): - raise exceptions.CommandError( - "Both neutron_net_id and neutron_subnet_id should be specified. " - "Alternatively, neither of them should be specified." - ) - - share_network = _find_share_network(cs, args.share_network) - values = { - 'share_network_id': share_network.id, - 'neutron_net_id': args.neutron_net_id, - 'neutron_subnet_id': args.neutron_subnet_id, - 'availability_zone': args.availability_zone, - } - share_network_subnet = cs.share_network_subnets.create(**values) - info = share_network_subnet._info.copy() - cliutils.print_dict(info) - - -@cliutils.arg( - 'share_network', - metavar='', - help='Share network name or ID.', -) -@cliutils.arg( - '--neutron-net-id', - '--neutron-net_id', - '--neutron_net_id', - '--neutron_net-id', - metavar='', - default=None, - action='single_alias', - help="Neutron network ID. Used to set up network for share servers. " - "Optional, Default = None.", -) -@cliutils.arg( - '--neutron-subnet-id', - '--neutron-subnet_id', - '--neutron_subnet_id', - '--neutron_subnet-id', - metavar='', - default=None, - action='single_alias', - help="Neutron subnet ID. Used to set up network for share servers. " - "This subnet should belong to specified neutron network. " - "Optional, Default = None.", -) -@cliutils.arg( - '--availability-zone', - '--availability_zone', - '--az', - default=None, - action='single_alias', - metavar='', - help='Optional availability zone that the subnet is available within ' - '(Default=None). If None, the subnet will be considered as being ' - 'available across all availability zones.', -) -@cliutils.arg( - '--reset', - metavar='', - choices=['True', 'False'], - required=False, - default=False, - help='Reset and start again the check operation.(Optional, Default=False)', -) -@api_versions.wraps("2.70") -def do_share_network_subnet_create_check(cs, args): - """Check if a new subnet can be added to a share network.""" - if xor(bool(args.neutron_net_id), bool(args.neutron_subnet_id)): - raise exceptions.CommandError( - "Both neutron_net_id and neutron_subnet_id should be specified. " - "Alternatively, neither of them should be specified." - ) - - share_network = _find_share_network(cs, args.share_network) - values = { - 'neutron_net_id': args.neutron_net_id, - 'neutron_subnet_id': args.neutron_subnet_id, - 'availability_zone': args.availability_zone, - 'reset_operation': args.reset, - } - subnet_create_check = cs.share_networks.share_network_subnet_create_check( - share_network.id, **values - ) - # result[0] is response code, result[1] is dict body - cliutils.print_dict(subnet_create_check[1]) - - -@cliutils.arg( - 'share_network', - metavar='', - help='Share network name or ID.', -) -@cliutils.arg( - 'share_network_subnet', - metavar='', - nargs='+', - help='Name or ID of share network subnet(s) to be deleted.', -) -def do_share_network_subnet_delete(cs, args): - """Delete one or more share network subnets.""" - failure_count = 0 - share_network_ref = _find_share_network(cs, args.share_network) - - for subnet in args.share_network_subnet: - try: - cs.share_network_subnets.delete(share_network_ref, subnet) - except Exception as e: - failure_count += 1 - print( - f"Deletion of share network subnet {subnet} failed: {e}", - file=sys.stderr, - ) - - if failure_count == len(args.share_network_subnet): - raise exceptions.CommandError( - "Unable to delete any of the specified share network subnets." - ) - - -@cliutils.arg( - 'share_network', - metavar='', - help='Name or ID of share network(s) to which the subnet belongs.', -) -@cliutils.arg( - 'share_network_subnet', - metavar='', - help='Share network subnet ID to show.', -) -def do_share_network_subnet_show(cs, args): - """Show share network subnet.""" - share_network = _find_share_network(cs, args.share_network) - share_network_subnet = cs.share_network_subnets.get( - share_network.id, args.share_network_subnet - ) - view_data = share_network_subnet._info.copy() - cliutils.print_dict(view_data) - - -@cliutils.arg( - 'share_network', - metavar='', - nargs='+', - help='Name or ID of share network(s) to be deleted.', -) -def do_share_network_delete(cs, args): - """Delete one or more share networks.""" - failure_count = 0 - - for share_network in args.share_network: - try: - share_ref = _find_share_network(cs, share_network) - cs.share_networks.delete(share_ref) - except Exception as e: - failure_count += 1 - print( - f"Delete for share network {share_network} failed: {e}", - file=sys.stderr, - ) - - if failure_count == len(args.share_network): - raise exceptions.CommandError( - "Unable to delete any of the specified share networks." - ) - - -@cliutils.arg( - 'type', - metavar='', - help="Security service type: 'ldap', 'kerberos' or 'active_directory'.", -) -@cliutils.arg( - '--dns-ip', - metavar='', - default=None, - help="DNS IP address used inside project's network.", -) -@cliutils.arg( - '--ou', - metavar='', - default=None, - help="Security service OU (Organizational Unit). Available only for " - "microversion >= 2.44.", -) -@cliutils.arg( - '--server', - metavar='', - default=None, - help="Security service IP address or hostname.", -) -@cliutils.arg( - '--domain', - metavar='', - default=None, - help="Security service domain.", -) -@cliutils.arg( - '--user', - metavar='', - default=None, - help="Security service user or group used by project.", -) -@cliutils.arg( - '--password', - metavar='', - default=None, - help="Password used by user.", -) -@cliutils.arg( - '--name', metavar='', default=None, help="Security service name." -) -@cliutils.arg( - '--default-ad-site', - metavar='', - dest='default_ad_site', - default=None, - help="Default AD site. Available only for microversion >= 2.76. Can " - "be provided in the place of '--server' but not along with it.", -) -@cliutils.arg( - '--description', - metavar='', - default=None, - help="Security service description.", -) -def do_security_service_create(cs, args): - """Create security service used by project.""" - values = { - 'dns_ip': args.dns_ip, - 'server': args.server, - 'domain': args.domain, - 'user': args.user, - 'password': args.password, - 'name': args.name, - 'description': args.description, - } - - if cs.api_version.matches( - api_versions.APIVersion("2.44"), api_versions.APIVersion() - ): - values['ou'] = args.ou - elif args.ou: - raise exceptions.CommandError( - "Security service Organizational Unit (ou) option " - "is only available with manila API version >= 2.44" - ) - - if cs.api_version.matches( - api_versions.APIVersion("2.76"), api_versions.APIVersion() - ): - values['default_ad_site'] = args.default_ad_site - elif args.default_ad_site: - raise exceptions.CommandError( - "Default AD site option is only available with " - "manila API version >= 2.76" - ) - - if args.type == 'active_directory': - if args.server and args.default_ad_site: - raise exceptions.CommandError( - "Cannot create security service because both " - "server and 'default_ad_site' were provided. " - "Specify either server or 'default_ad_site'." - ) - - security_service = cs.security_services.create(args.type, **values) - info = security_service._info.copy() - cliutils.print_dict(info) - - -@cliutils.arg( - 'security_service', - metavar='', - help='Security service name or ID to update.', -) -@cliutils.arg( - '--dns-ip', - metavar='', - default=None, - help="DNS IP address used inside project's network.", -) -@cliutils.arg( - '--ou', - metavar='', - default=None, - help="Security service OU (Organizational Unit). Available only for " - "microversion >= 2.44.", -) -@cliutils.arg( - '--server', - metavar='', - default=None, - help="Security service IP address or hostname.", -) -@cliutils.arg( - '--domain', - metavar='', - default=None, - help="Security service domain.", -) -@cliutils.arg( - '--user', - metavar='', - default=None, - help="Security service user or group used by project.", -) -@cliutils.arg( - '--password', - metavar='', - default=None, - help="Password used by user.", -) -@cliutils.arg( - '--name', metavar='', default=None, help="Security service name." -) -@cliutils.arg( - '--default-ad-site', - metavar='', - dest='default_ad_site', - default=None, - help="Default AD site. Available only for microversion >= 2.76.", -) -@cliutils.arg( - '--description', - metavar='', - default=None, - help="Security service description.", -) -def do_security_service_update(cs, args): - """Update security service.""" - values = { - 'dns_ip': args.dns_ip, - 'server': args.server, - 'domain': args.domain, - 'user': args.user, - 'password': args.password, - 'name': args.name, - 'description': args.description, - } - - if cs.api_version.matches( - api_versions.APIVersion("2.44"), api_versions.APIVersion() - ): - values['ou'] = args.ou - elif args.ou: - raise exceptions.CommandError( - "Security service Organizational Unit (ou) option " - "is only available with manila API version >= 2.44" - ) - - if cs.api_version.matches( - api_versions.APIVersion("2.76"), api_versions.APIVersion() - ): - values['default_ad_site'] = args.default_ad_site - elif args.default_ad_site: - raise exceptions.CommandError( - "Default AD site option is only available with " - "manila API version >= 2.76" - ) - - security_service = _find_security_service( - cs, args.security_service - ).update(**values) - cliutils.print_dict(security_service._info) - - -@cliutils.arg( - 'security_service', - metavar='', - help='Security service name or ID to show.', -) -def do_security_service_show(cs, args): - """Show security service.""" - security_service = _find_security_service(cs, args.security_service) - info = security_service._info.copy() - cliutils.print_dict(info) - - -@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( - '--share-network', - '--share_network', # alias - metavar='', - action='single_alias', - default=None, - help='Filter results by share network id or name.', -) -@cliutils.arg( - '--status', - metavar='', - default=None, - help='Filter results by status.', -) -@cliutils.arg( - '--name', metavar='', default=None, help='Filter results by name.' -) -@cliutils.arg( - '--type', metavar='', default=None, help='Filter results by type.' -) -@cliutils.arg( - '--user', - metavar='', - default=None, - help='Filter results by user or group used by projects.', -) -@cliutils.arg( - '--dns-ip', - '--dns_ip', # alias - metavar='', - action='single_alias', - default=None, - help="Filter results by DNS IP address used inside project's network.", -) -@cliutils.arg( - '--ou', - metavar='', - default=None, - help="Filter results by security service OU (Organizational Unit)." - " Available only for microversion >= 2.44.", -) -@cliutils.arg( - '--server', - metavar='', - default=None, - help="Filter results by security service IP address or hostname.", -) -@cliutils.arg( - '--domain', - metavar='', - default=None, - help="Filter results by domain.", -) -@cliutils.arg( - '--detailed', - dest='detailed', - metavar='<0|1>', - nargs='?', - type=int, - const=1, - default=0, - help="Show detailed information about filtered security services.", -) -@cliutils.arg( - '--offset', - metavar="", - default=None, - help='Start position of security services listing.', -) -@cliutils.arg( - '--limit', - metavar="", - default=None, - help='Number of security services to return per request.', -) -@cliutils.arg( - '--default-ad-site', - metavar='', - dest='default_ad_site', - default=None, - help="Default AD site. Available only for microversion >= 2.76.", -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "name,type".', -) -def do_security_service_list(cs, args): - """Get a list of security services.""" - all_projects = int( - os.environ.get( - "ALL_TENANTS", os.environ.get("ALL_PROJECTS", args.all_projects) - ) - ) - search_opts = { - 'all_tenants': all_projects, - 'status': args.status, - 'name': args.name, - 'type': args.type, - 'user': args.user, - 'dns_ip': args.dns_ip, - 'server': args.server, - 'domain': args.domain, - 'offset': args.offset, - 'limit': args.limit, - } - - if cs.api_version.matches( - api_versions.APIVersion("2.44"), api_versions.APIVersion() - ): - search_opts['ou'] = args.ou - elif args.ou: - raise exceptions.CommandError( - "Security service Organizational Unit (ou) option " - "is only available with manila API version >= 2.44" - ) - - if cs.api_version.matches( - api_versions.APIVersion("2.76"), api_versions.APIVersion() - ): - search_opts['default_ad_site'] = args.default_ad_site - elif args.ou: - raise exceptions.CommandError( - "Security service Default AD site option " - "is only available with manila API version >= 2.76" - ) - - if args.share_network: - search_opts['share_network_id'] = _find_share_network( - cs, args.share_network - ).id - security_services = cs.security_services.list( - search_opts=search_opts, detailed=args.detailed - ) - fields = [ - 'id', - 'name', - 'status', - 'type', - ] - if args.columns is not None: - fields = _split_columns(columns=args.columns) - - if args.detailed: - fields.append('share_networks') - cliutils.print_list(security_services, fields=fields) - - -@cliutils.arg( - 'security_service', - metavar='', - nargs='+', - help='Name or ID of the security service(s) to delete.', -) -def do_security_service_delete(cs, args): - """Delete one or more security services.""" - failure_count = 0 - - for security_service in args.security_service: - try: - security_ref = _find_security_service(cs, security_service) - cs.security_services.delete(security_ref) - except Exception as e: - failure_count += 1 - print( - f"Delete for security service {security_service} failed: {e}", - file=sys.stderr, - ) - - if failure_count == len(args.security_service): - raise exceptions.CommandError( - "Unable to delete any of the specified security services." - ) - - -@cliutils.arg( - '--host', - metavar='', - default=None, - help='Filter results by name of host.', -) -@cliutils.arg( - '--status', - metavar='', - default=None, - help='Filter results by status.', -) -@cliutils.arg( - '--share-network', - metavar='', - default=None, - help='Filter results by share network.', -) -@cliutils.arg( - '--project-id', - metavar='', - default=None, - help='Filter results by project ID.', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,host,status".', -) -@cliutils.arg( - '--share-network-subnet', - '--share_network_subnet', - type=str, - metavar='', - help="Filter results by share network subnet that the share server's " - "network allocation exists whithin. Available for micro version " - ">= 2.51 (Optional, Default=None).", - default=None, -) -def do_share_server_list(cs, args): - """List all share servers (Admin only).""" - search_opts = { - "host": args.host, - "share_network": args.share_network, - "status": args.status, - "project_id": args.project_id, - } - fields = [ - "Id", - "Host", - "Status", - "Share Network", - "Project Id", - "Updated_at", - ] - - 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" - ) - elif cs.api_version < api_versions.APIVersion("2.70"): - search_opts.update( - {'share_network_subnet_id': args.share_network_subnet} - ) - fields.append("Share Network Subnet Id") - else: - search_opts.update( - {'share_network_subnet_id': args.share_network_subnet} - ) - fields.append("Share Network Subnet IDs") - - if args.columns is not None: - fields = _split_columns(columns=args.columns) - - share_servers = cs.share_servers.list(search_opts=search_opts) - cliutils.print_list(share_servers, fields=fields) - - -@cliutils.arg('id', metavar='', type=str, help='ID of share server.') -def do_share_server_show(cs, args): - """Show share server info (Admin only).""" - share_server = cs.share_servers.get(args.id) - # All 'backend_details' data already present as separated strings, - # so remove big dict from view. - if "backend_details" in share_server._info: - del share_server._info["backend_details"] - cliutils.print_dict(share_server._info) - - -@cliutils.arg('id', metavar='', type=str, help='ID of share server.') -def do_share_server_details(cs, args): - """Show share server details (Admin only).""" - details = cs.share_servers.details(args.id) - cliutils.print_dict(details._info) - - -@cliutils.arg( - 'id', - metavar='', - nargs='+', - type=str, - help='ID of the share server(s) to delete.', -) -@cliutils.arg( - '--wait', action='store_true', help='Wait for share server to delete' -) -@cliutils.service_type('sharev2') -def do_share_server_delete(cs, args): - """Delete one or more share servers (Admin only).""" - - failure_count = 0 - share_servers_to_delete = [] - - for server_id in args.id: - try: - id_ref = _find_share_server(cs, server_id) - share_servers_to_delete.append(id_ref) - id_ref.delete() - except Exception as e: - failure_count += 1 - print( - f"Delete for share server {server_id} failed: {e}", - file=sys.stderr, - ) - - if failure_count == len(args.id): - raise exceptions.CommandError( - "Unable to delete any of the specified share servers." - ) - - if args.wait: - for share_server in share_servers_to_delete: - try: - _wait_for_resource_status( - cs, - share_server, - resource_type='share_server', - expected_status='deleted', - ) - except exceptions.CommandError as e: - print(e, file=sys.stderr) - - -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,name".', -) -def do_availability_zone_list(cs, args): - """List all availability zones.""" - - if args.columns is not None: - fields = _split_columns(columns=args.columns) - else: - fields = ("Id", "Name", "Created_At", "Updated_At") - - availability_zones = cs.availability_zones.list() - cliutils.print_list(availability_zones, fields=fields) - - -@cliutils.arg( - '--host', metavar='', default=None, help='Name of host.' -) -@cliutils.arg( - '--binary', metavar='', default=None, help='Service binary.' -) -@cliutils.arg( - '--status', - metavar='', - default=None, - help='Filter results by status.', -) -@cliutils.arg( - '--state', metavar='', default=None, help='Filter results by state.' -) -@cliutils.arg( - '--zone', metavar='', default=None, help='Availability zone.' -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,host".', -) -def do_service_list(cs, args): - """List all services (Admin only).""" - search_opts = { - 'status': args.status, - 'host': args.host, - 'binary': args.binary, - 'zone': args.zone, - 'state': args.state, - } - fields = ["Id", "Binary", "Host", "Zone", "Status", "State", "Updated_at"] - - if args.columns is not None: - fields = _split_columns(columns=args.columns) - - services = cs.services.list(search_opts=search_opts) - cliutils.print_list(services, fields=fields) - - -@cliutils.arg( - 'host', - metavar='', - help="Host name as 'example_host@example_backend'.", -) -@cliutils.arg( - 'binary', - metavar='', - help="Service binary, could be 'manila-share' or 'manila-scheduler'.", -) -def do_service_enable(cs, args): - """Enables 'manila-share' or 'manila-scheduler' services (Admin only).""" - columns = ("Host", "Binary", "Enabled") - result = cs.services.enable(args.host, args.binary) - result.enabled = not result.disabled - cliutils.print_list([result], columns) - - -@cliutils.arg( - 'host', - metavar='', - help="Host name as 'example_host@example_backend'.", -) -@cliutils.arg( - 'binary', - metavar='', - help="Service binary, could be 'manila-share' or 'manila-scheduler'.", -) -def do_service_disable(cs, args): - """Disables 'manila-share' or 'manila-scheduler' services (Admin only).""" - columns = ("Host", "Binary", "Enabled") - result = cs.services.disable(args.host, args.binary) - result.enabled = not result.disabled - cliutils.print_list([result], columns) - - -def _print_dict(data_dict): - formatted_data = [] - - for date in data_dict: - formatted_data.append(f"{date} : {data_dict[date]}") - - return "\n".join(formatted_data) - - -@cliutils.arg( - '--host', - metavar='', - type=str, - default='.*', - help='Filter results by host name. Regular expressions are supported.', -) -@cliutils.arg( - '--backend', - metavar='', - type=str, - default='.*', - help='Filter results by backend name. Regular expressions are supported.', -) -@cliutils.arg( - '--pool', - metavar='', - type=str, - default='.*', - help='Filter results by pool name. Regular expressions are supported.', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "name,host".', -) -@cliutils.arg( - '--detail', - '--detailed', - action='store_true', - help='Show detailed information about pools. If this parameter is set ' - 'to True, --columns parameter will be ignored if present. ' - '(Default=False)', -) -@cliutils.arg( - '--share-type', - '--share_type', - '--share-type-id', - '--share_type_id', - metavar='', - type=str, - default=None, - action='single_alias', - help='Filter results by share type name or ID. (Default=None)' - 'Available only for microversion >= 2.23.', -) -def do_pool_list(cs, args): - """List all backend storage pools known to the scheduler (Admin only).""" - - search_opts = { - 'host': args.host, - 'backend': args.backend, - 'pool': args.pool, - 'share_type': args.share_type, - } - - if args.detail: - fields = ["Name", "Host", "Backend", "Pool", "Capabilities"] - else: - fields = ["Name", "Host", "Backend", "Pool"] - - pools = cs.pools.list(detailed=args.detail, search_opts=search_opts) - if args.columns is not None: - fields = _split_columns(columns=args.columns) - pools = cs.pools.list(detailed=True, search_opts=search_opts) - - if args.detail: - for info in pools: - backend = dict() - backend['name'] = info.name - backend.update(info.capabilities) - cliutils.print_dict(backend) - else: - cliutils.print_list(pools, fields=fields) - - -@cliutils.arg( - 'share', metavar='', help='Name or ID of share to extend.' -) -@cliutils.arg( - 'new_size', - metavar='', - type=int, - help='New size of share, in GiBs.', -) -@cliutils.arg('--wait', action='store_true', help='Wait for share extension') -@cliutils.arg( - '--force', - action='store_true', - help='Force attempt the extension of the share, only available with ' - 'microversion 2.64 and higher. (admin only)', -) -@cliutils.service_type('sharev2') -def do_extend(cs, args): - """Increases the size of an existing share.""" - share = _find_share(cs, args.share) - force = False - if args.force: - if cs.api_version < api_versions.APIVersion("2.64"): - raise exceptions.CommandError( - "args 'force' is available only starting with " - "'2.64' API microversion." - ) - force = True - if force: - cs.shares.extend(share, args.new_size, force=force) - else: - cs.shares.extend(share, args.new_size) - - if args.wait: - share = _wait_for_share_status(cs, share) - else: - share = _find_share(cs, args.share) - _print_share(cs, share) - - -@cliutils.arg( - 'share', metavar='', help='Name or ID of share to shrink.' -) -@cliutils.arg( - 'new_size', - metavar='', - type=int, - help='New size of share, in GiBs.', -) -@cliutils.arg('--wait', action='store_true', help='Wait for share shrinkage') -@cliutils.service_type('sharev2') -def do_shrink(cs, args): - """Decreases the size of an existing share.""" - share = _find_share(cs, args.share) - cs.shares.shrink(share, args.new_size) - - if args.wait: - share = _wait_for_share_status(cs, share) - else: - share = _find_share(cs, args.share) - _print_share(cs, share) - - -############################################################################## -# -# Share types -# -############################################################################## - - -def _print_type_extra_specs(share_type): - """Prints share type extra specs or share group type specs.""" - try: - return _print_dict(share_type.get_keys()) - except exceptions.NotFound: - return None - - -def _print_type_required_extra_specs(share_type): - try: - return _print_dict(share_type.get_required_keys()) - except exceptions.NotFound: - return "N/A" - - -def _print_type_optional_extra_specs(share_type): - try: - return _print_dict(share_type.get_optional_keys()) - except exceptions.NotFound: - return "N/A" - - -def _is_share_type_public(share_type): - return 'public' if share_type.is_public else 'private' - - -def _print_share_type_list( - stypes, default_share_type=None, columns=None, description=False -): - def _is_default(share_type): - if hasattr(share_type, 'is_default'): - return 'YES' if share_type.is_default else '-' - elif default_share_type: - default = default_share_type.id - return 'YES' if share_type.id == default else '-' - else: - return '-' - - formatters = { - 'visibility': _is_share_type_public, - 'is_default': _is_default, - 'required_extra_specs': _print_type_required_extra_specs, - 'optional_extra_specs': _print_type_optional_extra_specs, - } - - for stype in stypes: - stype = stype.to_dict() - stype['visibility'] = stype.pop('is_public', 'unknown') - - fields = [ - 'ID', - 'Name', - 'visibility', - 'is_default', - 'required_extra_specs', - 'optional_extra_specs', - ] - if description: - fields.append('Description') - if columns is not None: - fields = _split_columns(columns=columns, title=False) - - cliutils.print_list(stypes, fields, formatters) - - -def _print_share_type(stype, default_share_type=None, show_des=False): - def _is_default(share_type): - if hasattr(share_type, 'is_default'): - return 'YES' if share_type.is_default else '-' - return '-' - - stype_dict = { - 'ID': stype.id, - 'Name': stype.name, - 'Visibility': _is_share_type_public(stype), - 'is_default': _is_default(stype), - 'required_extra_specs': _print_type_required_extra_specs(stype), - 'optional_extra_specs': _print_type_optional_extra_specs(stype), - } - if show_des: - stype_dict['Description'] = stype.description - cliutils.print_dict(stype_dict) - - -def _print_type_and_extra_specs_list(stypes, columns=None): - """Prints extra specs for a list of share types or share group types.""" - formatters = { - 'all_extra_specs': _print_type_extra_specs, - } - fields = ['ID', 'Name', 'all_extra_specs'] - - if columns is not None: - fields = _split_columns(columns=columns, title=False) - - cliutils.print_list(stypes, fields, formatters) - - -def _find_share_type(cs, stype): - """Get a share type by name or ID.""" - return apiclient_utils.find_resource(cs.share_types, stype) - - -@cliutils.arg( - '--all', - dest='all', - action='store_true', - default=False, - help='Display all share types whatever public or private ' - 'OPTIONAL: Default=False. (Admin only).', -) -@cliutils.arg( - '--extra-specs', - '--extra_specs', - type=str, - nargs='*', - metavar='', - action='single_alias', - default=None, - help='Filters results by a extra specs key and value of share type that ' - 'was used for share creation. Available only for microversion >= ' - '2.43. OPTIONAL: Default=None.', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,name".', -) -def do_type_list(cs, args): - """Print a list of available 'share types'.""" - search_opts = None - show_all = args.all - extra_specs = _extract_extra_specs(args) - if extra_specs: - if cs.api_version < api_versions.APIVersion("2.43"): - raise exceptions.CommandError( - "Filter by 'extra_specs' is available only starting with " - "'2.43' API microversion." - ) - search_opts = {'extra_specs': extra_specs} - - share_types = cs.share_types.list( - show_all=show_all, search_opts=search_opts - ) - default = None - if share_types and not hasattr(share_types[0], 'is_default'): - if ( - args.columns and 'is_default' in args.columns - ) or args.columns is None: - default = cs.share_types.get() - - show_des = cs.api_version.matches( - api_versions.APIVersion("2.41"), api_versions.APIVersion() - ) - _print_share_type_list( - share_types, - default_share_type=default, - columns=args.columns, - description=show_des, - ) - - -@cliutils.arg( - 'share_type', metavar='', help='Name or ID of the share type.' -) -def do_type_show(cs, args): - """Show share type details.""" - share_type = _find_share_type(cs, args.share_type) - default = None - if share_type and not hasattr(share_type, 'is_default'): - default = cs.share_types.get() - _print_type_show(share_type, default_share_type=default) - - -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,name".', -) -def do_extra_specs_list(cs, args): - """Print a list of current 'share types and extra specs' (Admin Only).""" - stypes = cs.share_types.list() - _print_type_and_extra_specs_list(stypes, columns=args.columns) - - -@cliutils.arg('name', metavar='', help="Name of the new share type.") -@cliutils.arg( - 'spec_driver_handles_share_servers', - metavar='', - type=str, - help="Required extra specification. " - "Valid values are 'true'/'1' and 'false'/'0'.", -) -@cliutils.arg( - '--description', - metavar='', - type=str, - default=None, - help='Filter results by description. ' - 'Available only for microversion >= 2.41.', -) -@cliutils.arg( - '--snapshot_support', - '--snapshot-support', - metavar='', - action='single_alias', - help="Boolean extra spec used for filtering of back ends by their " - "capability to create share snapshots.", -) -@cliutils.arg( - '--create_share_from_snapshot_support', - '--create-share-from-snapshot-support', - metavar='', - action='single_alias', - help="Boolean extra spec used for filtering of back ends by their " - "capability to create shares from snapshots.", -) -@cliutils.arg( - '--revert_to_snapshot_support', - '--revert-to-snapshot-support', - metavar='', - action='single_alias', - help="Boolean extra spec used for filtering of back ends by their " - "capability to revert shares to snapshots. (Default is False).", -) -@cliutils.arg( - '--mount_snapshot_support', - '--mount-snapshot-support', - metavar='', - action='single_alias', - help="Boolean extra spec used for filtering of back ends by their " - "capability to mount share snapshots. (Default is False).", -) -@cliutils.arg( - '--extra-specs', - '--extra_specs', # alias - type=str, - nargs='*', - metavar='', - action='single_alias', - help="Extra specs key and value of share type that will be" - " used for share type creation. OPTIONAL: Default=None." - " example --extra-specs thin_provisioning=' True', " - "replication_type=readable.", - default=None, -) -@cliutils.arg( - '--is_public', - '--is-public', - metavar='', - action='single_alias', - help="Make type accessible to the public (default true).", -) -def do_type_create(cs, args): - """Create a new share type (Admin only).""" - kwargs = { - "name": args.name, - "is_public": strutils.bool_from_string(args.is_public, default=True), - } - try: - kwargs['spec_driver_handles_share_servers'] = ( - strutils.bool_from_string( - args.spec_driver_handles_share_servers, strict=True - ) - ) - except ValueError as e: - msg = ( - "Argument spec_driver_handles_share_servers " - f"argument is not valid: {str(e)}" - ) - raise exceptions.CommandError(msg) - - kwargs['extra_specs'] = _extract_extra_specs(args) - - if 'driver_handles_share_servers' in kwargs['extra_specs']: - msg = ( - "Argument 'driver_handles_share_servers' is already " - "set via positional argument." - ) - raise exceptions.CommandError(msg) - - show_des = False - if cs.api_version.matches( - api_versions.APIVersion("2.41"), api_versions.APIVersion() - ): - show_des = True - kwargs['description'] = getattr(args, 'description') - elif getattr(args, 'description'): - raise exceptions.CommandError( - "Pattern based option (description)" - " is only available with manila API version >= 2.41" - ) - - boolean_keys = ( - 'snapshot_support', - 'create_share_from_snapshot_support', - 'revert_to_snapshot_support', - 'mount_snapshot_support', - ) - for key in boolean_keys: - value = getattr(args, key) - - if value is not None and key in kwargs['extra_specs']: - msg = f"Argument '{key}' value specified twice." - raise exceptions.CommandError(msg) - - try: - if value: - kwargs['extra_specs'][key] = strutils.bool_from_string( - value, strict=True - ) - elif key in kwargs['extra_specs']: - kwargs['extra_specs'][key] = strutils.bool_from_string( - kwargs['extra_specs'][key], strict=True - ) - except ValueError as e: - msg = ( - f"Argument '{key}' is of boolean " - f"type and has invalid value: {str(e)}" - ) - raise exceptions.CommandError(msg) - - stype = cs.share_types.create(**kwargs) - _print_share_type(stype, show_des=show_des) - - -@cliutils.arg( - 'id', metavar='', help="Name or ID of the share type to update." -) -@cliutils.arg( - '--name', metavar='', type=str, help="New name of share type." -) -@cliutils.arg( - '--description', - metavar='', - type=str, - default=None, - help="New description of share type.", -) -@cliutils.arg( - '--is-public', - '--is_public', - metavar='', - action='single_alias', - help="New visibility of the share type. If set to True, share type will " - "be available to all projects in the cloud.", -) -@api_versions.wraps("2.50") -def do_type_update(cs, args): - """Update share type name, description, and/or visibility. (Admin only).""" - name = getattr(args, 'name') - description = getattr(args, 'description') - is_public = getattr(args, 'is_public') - if not name and description is None and is_public is None: - msg = ( - "A description and/or non-empty name and/or boolean is_public " - "must be supplied to update the respective attributes of the " - "share type." - ) - raise exceptions.CommandError(msg) - kwargs = {} - kwargs['name'] = name - if is_public: - try: - kwargs['is_public'] = strutils.bool_from_string( - is_public, strict=True - ) - except ValueError as e: - raise exceptions.CommandError( - "The value of 'is_public' is invalid: %s", str(e) - ) - - kwargs['description'] = description - stype = _find_share_type(cs, args.id) - stype = stype.update(**kwargs) - _print_share_type(stype, show_des=True) - - -@cliutils.arg( - 'id', - metavar='', - nargs='+', - help="Name or ID of the share type(s) to delete.", -) -def do_type_delete(cs, args): - """Delete one or more specific share types (Admin only).""" - - failure_count = 0 - - for name_or_id in args.id: - try: - id_ref = _find_share_type(cs, name_or_id) - cs.share_types.delete(id_ref) - except Exception as e: - failure_count += 1 - print( - f"Delete for share type {name_or_id} failed: {e}", - file=sys.stderr, - ) - - if failure_count == len(args.id): - raise exceptions.CommandError( - "Unable to delete any of the specified share types." - ) - - -@cliutils.arg('stype', metavar='', help="Name or ID of the share type.") -@cliutils.arg( - 'action', - metavar='', - choices=['set', 'unset'], - help="Actions: 'set' or 'unset'.", -) -@cliutils.arg( - 'metadata', - metavar='', - nargs='*', - default=None, - help='Extra_specs to set or unset (only key is necessary to unset).', -) -def do_type_key(cs, args): - """Set or unset extra_spec for a share type (Admin only).""" - stype = _find_share_type(cs, args.stype) - - if args.metadata is not None: - keypair = _extract_metadata(args) - - if args.action == 'set': - stype.set_keys(keypair) - elif args.action == 'unset': - stype.unset_keys(list(keypair)) - - -@cliutils.arg( - 'share_type', - metavar='', - help="Filter results by share type name or ID.", -) -def do_type_access_list(cs, args): - """Print access information about the given share type (Admin only).""" - share_type = _find_share_type(cs, args.share_type) - if share_type.is_public: - raise exceptions.CommandError( - "Forbidden to get access list for public share type." - ) - access_list = cs.share_type_access.list(share_type) - - columns = ['Project_ID'] - cliutils.print_list(access_list, columns) - - -@cliutils.arg( - 'share_type', - metavar='', - help="Share type name or ID to add access for the given project.", -) -@cliutils.arg( - 'project_id', - metavar='', - help='Project ID to add share type access for.', -) -def do_type_access_add(cs, args): - """Adds share type access for the given project (Admin only).""" - vtype = _find_share_type(cs, args.share_type) - cs.share_type_access.add_project_access(vtype, args.project_id) - - -@cliutils.arg( - 'share_type', - metavar='', - help=('Share type name or ID to remove access for the given project.'), -) -@cliutils.arg( - 'project_id', - metavar='', - help='Project ID to remove share type access for.', -) -def do_type_access_remove(cs, args): - """Removes share type access for the given project (Admin only).""" - vtype = _find_share_type(cs, args.share_type) - cs.share_type_access.remove_project_access(vtype, args.project_id) - - -############################################################################## -# -# Share group types -# -############################################################################## - - -def _print_share_group_type_list( - share_group_types, default_share_group_type=None, columns=None -): - def _is_default(share_group_type): - if hasattr(share_group_type, 'is_default'): - return 'YES' if share_group_type.is_default else '-' - elif default_share_group_type: - default = default_share_group_type.id - return 'YES' if share_group_type.id == default else '-' - else: - return '-' - - formatters = { - 'visibility': _is_share_type_public, - 'is_default': _is_default, - } - - for sg_type in share_group_types: - sg_type = sg_type.to_dict() - sg_type['visibility'] = sg_type.pop('is_public', 'unknown') - - fields = [ - 'ID', - 'Name', - 'visibility', - 'is_default', - ] - if columns is not None: - fields = _split_columns(columns=columns, title=False) - - cliutils.print_list( - share_group_types, fields, formatters, sortby_index=None - ) - - -def _print_share_group_type(share_group_type, default_share_type=None): - def _is_default(share_group_type): - if hasattr(share_group_type, 'is_default'): - return 'YES' if share_group_type.is_default else '-' - return '-' - - share_group_type_dict = { - 'ID': share_group_type.id, - 'Name': share_group_type.name, - 'Visibility': _is_share_type_public(share_group_type), - 'is_default': _is_default(share_group_type), - } - cliutils.print_dict(share_group_type_dict) - - -def _find_share_group_type(cs, sg_type): - """Get a share group type by name or ID.""" - return apiclient_utils.find_resource(cs.share_group_types, sg_type) - - -@cliutils.arg( - '--all', - dest='all', - action='store_true', - default=False, - help='Display all share group types (Admin only).', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,name".', -) -@cliutils.service_type('sharev2') -def do_share_group_type_list(cs, args): - """Print a list of available 'share group types'.""" - - sg_types = cs.share_group_types.list(show_all=args.all) - - default = None - if sg_types and not hasattr(sg_types[0], 'is_default'): - if ( - args.columns and 'is_default' in args.columns - ) or args.columns is None: - default = cs.share_group_types.get() - - _print_share_group_type_list( - sg_types, default_share_group_type=default, columns=args.columns - ) - - -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,name".', -) -@cliutils.service_type('sharev2') -def do_share_group_type_specs_list(cs, args): - """Print a list of 'share group types specs' (Admin Only).""" - - sg_types = cs.share_group_types.list() - _print_type_and_extra_specs_list(sg_types, columns=args.columns) - - -@cliutils.arg( - 'name', metavar='', help='Name of the new share group type.' -) -@cliutils.arg( - 'share_types', - metavar='', - type=str, - help='Comma-separated list of share type names or IDs.', -) -@cliutils.arg( - '--is_public', - '--is-public', - metavar='', - action='single_alias', - help='Make type accessible to the public (default true).', -) -@cliutils.arg( - '--group-specs', - '--group_specs', - metavar='', - type=str, - nargs='*', - action='single_alias', - default=None, - help='Share Group type extra specs by key and value. ' - 'OPTIONAL: Default=None. ' - 'Example: "--group-specs consistent_snapshot_support=host".', -) -@cliutils.service_type('sharev2') -def do_share_group_type_create(cs, args): - """Create a new share group type (Admin only).""" - - share_types = [ - _find_share_type(cs, share_type) - for share_type in args.share_types.split(',') - ] - - kwargs = { - 'share_types': share_types, - 'name': args.name, - 'is_public': strutils.bool_from_string(args.is_public, default=True), - } - if args.group_specs is not None: - kwargs['group_specs'] = _extract_group_specs(args) - - sg_type = cs.share_group_types.create(**kwargs) - _print_share_group_type(sg_type) - - -@cliutils.arg( - 'id', metavar='', help="Name or ID of the share group type to delete." -) -@cliutils.service_type('sharev2') -def do_share_group_type_delete(cs, args): - """Delete a specific share group type (Admin only).""" - share_group_type = _find_share_group_type(cs, args.id) - cs.share_group_types.delete(share_group_type) - - -@cliutils.arg( - 'share_group_type', - metavar='', - help="Name or ID of the share group type.", -) -@cliutils.arg( - 'action', - metavar='', - choices=['set', 'unset'], - help="Actions: 'set' or 'unset'.", -) -@cliutils.arg( - 'group_specs', - metavar='', - nargs='*', - default=None, - help='Group specs to set or unset (only key is necessary to unset).', -) -@cliutils.service_type('sharev2') -def do_share_group_type_key(cs, args): - """Set or unset group_spec for a share group type (Admin only).""" - sg_type = _find_share_group_type(cs, args.share_group_type) - if args.group_specs is not None: - keypair = _extract_group_specs(args) - if args.action == 'set': - sg_type.set_keys(keypair) - elif args.action == 'unset': - sg_type.unset_keys(list(keypair)) - - -@cliutils.arg( - 'share_group_type', - metavar='', - help="Filter results by share group type name or ID.", -) -def do_share_group_type_access_list(cs, args): - """Print access information about a share group type (Admin only).""" - share_group_type = _find_share_group_type(cs, args.share_group_type) - if share_group_type.is_public: - raise exceptions.CommandError( - "Forbidden to get access list for public share group type." - ) - access_list = cs.share_group_type_access.list(share_group_type) - columns = ['Project_ID'] - cliutils.print_list(access_list, columns) - - -@cliutils.arg( - 'share_group_type', - metavar='', - help='Share group type name or ID to add access for the given project.', -) -@cliutils.arg( - 'project_id', - metavar='', - help='Project ID to add share group type access for.', -) -def do_share_group_type_access_add(cs, args): - """Adds share group type access for the given project (Admin only).""" - share_group_type = _find_share_group_type(cs, args.share_group_type) - cs.share_group_type_access.add_project_access( - share_group_type, args.project_id - ) - - -@cliutils.arg( - 'share_group_type', - metavar='', - help='Share group type name or ID to remove access for the given project.', -) -@cliutils.arg( - 'project_id', - metavar='', - help='Project ID to remove share group type access for.', -) -def do_share_group_type_access_remove(cs, args): - """Removes share group type access for the given project (Admin only).""" - share_group_type = _find_share_group_type(cs, args.share_group_type) - cs.share_group_type_access.remove_project_access( - share_group_type, args.project_id - ) - - -############################################################################## -# -# Share groups -# -############################################################################## - - -@cliutils.arg( - '--name', - metavar='', - help='Optional share group name. (Default=None)', - default=None, -) -@cliutils.arg( - '--description', - metavar='', - help='Optional share group description. (Default=None)', - default=None, -) -@cliutils.arg( - '--share-types', - '--share_types', - metavar='', - type=str, - default=None, - action='single_alias', - help='Comma-separated list of share types. (Default=None)', -) -@cliutils.arg( - '--share-group-type', - '--share_group_type', - '--type', - metavar='', - type=str, - default=None, - action='single_alias', - help="Share group type name or ID of the share group to be created. " - "(Default=None)", -) -@cliutils.arg( - '--share-network', - '--share_network', - metavar='', - type=str, - default=None, - action='single_alias', - help='Specify share network name or id.', -) -@cliutils.arg( - '--source-share-group-snapshot', - '--source_share_group_snapshot', - metavar='', - type=str, - action='single_alias', - help='Optional share group snapshot name or ID to create the share group ' - 'from. (Default=None)', - default=None, -) -@cliutils.arg( - '--availability-zone', - '--availability_zone', - '--az', - default=None, - action='single_alias', - metavar='', - help='Optional availability zone in which group should be created. ' - '(Default=None)', -) -@cliutils.arg( - '--wait', - action='store_true', - default=False, - help='Wait for share group to create', -) -@cliutils.service_type('sharev2') -def do_share_group_create(cs, args): - """Creates a new share group.""" - share_types = [] - if args.share_types: - s_types = args.share_types.split(',') - for s_type in s_types: - share_type = _find_share_type(cs, s_type) - share_types.append(share_type) - - share_group_type = None - if args.share_group_type: - share_group_type = _find_share_group_type(cs, args.share_group_type) - - share_network = None - if args.share_network: - share_network = _find_share_network(cs, args.share_network) - - share_group_snapshot = None - if args.source_share_group_snapshot: - share_group_snapshot = _find_share_group_snapshot( - cs, args.source_share_group_snapshot - ) - - kwargs = { - 'share_group_type': share_group_type, - 'share_types': share_types or None, - 'name': args.name, - 'description': args.description, - 'availability_zone': args.availability_zone, - 'source_share_group_snapshot': share_group_snapshot, - 'share_network': share_network, - } - - share_group = cs.share_groups.create(**kwargs) - - if args.wait: - try: - share_group = _wait_for_resource_status( - cs, - share_group, - resource_type='share_group', - expected_status='available', - ) - except exceptions.CommandError as e: - print(e, file=sys.stderr) - - _print_share_group(cs, share_group) - - -@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='', - type=str, - default=None, - help='Filter results by name.', -) -@cliutils.arg( - '--description', - metavar='', - type=str, - default=None, - help='Filter results by description. ' - 'Available only for microversion >= 2.36.', -) -@cliutils.arg( - '--status', - metavar='', - type=str, - default=None, - help='Filter results by status.', -) -@cliutils.arg( - '--share-server-id', - '--share-server_id', - '--share_server-id', - '--share_server_id', - metavar='', - type=str, - default=None, - action='single_alias', - help='Filter results by share server ID (Admin only).', -) -@cliutils.arg( - '--share-group-type', - '--share-group-type-id', - '--share_group_type', - '--share_group_type_id', - metavar='', - type=str, - default=None, - action='single_alias', - help='Filter results by a share group type ID or name that was used for ' - 'share group creation.', -) -@cliutils.arg( - '--snapshot', - metavar='', - type=str, - default=None, - help='Filter results by share group snapshot name or ID that was used to ' - 'create the share group.', -) -@cliutils.arg( - '--host', metavar='', default=None, help='Filter results by host.' -) -@cliutils.arg( - '--share-network', - '--share_network', - metavar='', - type=str, - default=None, - action='single_alias', - help='Filter results by share-network name or ID.', -) -@cliutils.arg( - '--project-id', - '--project_id', - metavar='', - type=str, - default=None, - action='single_alias', - help="Filter results by project ID. Useful with set key '--all-projects'.", -) -@cliutils.arg( - '--limit', - metavar='', - type=int, - default=None, - help='Maximum number of share groups to return. (Default=None)', -) -@cliutils.arg( - '--offset', - metavar="", - default=None, - help='Start position of share group listing.', -) -@cliutils.arg( - '--sort-key', - '--sort_key', - metavar='', - type=str, - default=None, - action='single_alias', - help=( - f'Key to be sorted, available keys are ' - f'{constants.SHARE_GROUP_SORT_KEY_VALUES}. Default=None.' - ), -) -@cliutils.arg( - '--sort-dir', - '--sort_dir', - metavar='', - type=str, - default=None, - action='single_alias', - help=f'Sort direction, available values are {constants.SORT_DIR_VALUES}. ' - 'OPTIONAL: Default=None.', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,name".', -) -@cliutils.arg( - '--name~', - metavar='', - type=str, - default=None, - help='Filter results matching a share group name pattern. ' - 'Available only for microversion >= 2.36.', -) -@cliutils.arg( - '--description~', - metavar='', - type=str, - default=None, - help='Filter results matching a share group description pattern. ' - 'Available only for microversion >= 2.36.', -) -@cliutils.service_type('sharev2') -def do_share_group_list(cs, args): - """List share groups with filters.""" - if args.columns is not None: - list_of_keys = _split_columns(columns=args.columns) - else: - list_of_keys = ('ID', 'Name', 'Status', 'Description') - - all_projects = int( - os.environ.get( - "ALL_TENANTS", os.environ.get("ALL_PROJECTS", args.all_projects) - ) - ) - empty_obj = type('Empty', (object,), {'id': None}) - sg_type = ( - _find_share_group_type(cs, args.share_group_type) - if args.share_group_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 - ) - - search_opts = { - 'offset': args.offset, - 'limit': args.limit, - 'all_tenants': all_projects, - 'name': args.name, - 'status': args.status, - 'share_server_id': args.share_server_id, - 'share_group_type_id': sg_type.id, - 'source_share_group_snapshot_id': snapshot.id, - 'host': args.host, - 'share_network_id': share_network.id, - 'project_id': args.project_id, - } - 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" - ) - - share_groups = cs.share_groups.list( - search_opts=search_opts, sort_key=args.sort_key, sort_dir=args.sort_dir - ) - cliutils.print_list(share_groups, fields=list_of_keys, sortby_index=None) - - -@cliutils.arg( - 'share_group', - metavar='', - help='Name or ID of the share group.', -) -@cliutils.service_type('sharev2') -def do_share_group_show(cs, args): - """Show details about a share group.""" - share_group = _find_share_group(cs, args.share_group) - _print_share_group(cs, share_group) - - -@cliutils.arg( - 'share_group', - metavar='', - help='Name or ID of the share group to update.', -) -@cliutils.arg( - '--name', - metavar='', - default=None, - help='Optional new name for the share group. (Default=None)', -) -@cliutils.arg( - '--description', - metavar='', - help='Optional share group description. (Default=None)', - default=None, -) -@cliutils.service_type('sharev2') -def do_share_group_update(cs, args): - """Update a share group.""" - kwargs = {} - - if args.name is not None: - kwargs['name'] = args.name - if args.description is not None: - kwargs['description'] = args.description - - if not kwargs: - msg = "Must supply name and/or description" - raise exceptions.CommandError(msg) - share_group = _find_share_group(cs, args.share_group) - share_group = cs.share_groups.update(share_group, **kwargs) - _print_share_group(cs, share_group) - - -@cliutils.arg( - 'share_group', - metavar='', - nargs='+', - help='Name or ID of the share group(s).', -) -@cliutils.arg( - '--force', - action='store_true', - default=False, - help='Attempt to force delete the share group (Default=False)' - ' (Admin only).', -) -@cliutils.arg( - '--wait', - action='store_true', - default=False, - help='Wait for share group to delete', -) -@cliutils.service_type('sharev2') -def do_share_group_delete(cs, args): - """Delete one or more share groups.""" - failure_count = 0 - share_group_to_delete = [] - - for share_group in args.share_group: - try: - share_group_ref = _find_share_group(cs, share_group) - share_group_to_delete.append(share_group_ref) - share_group_ref.delete(args.force) - except Exception as e: - failure_count += 1 - print( - f"Delete for share group {share_group} failed: {e}", - file=sys.stderr, - ) - - if failure_count == len(args.share_group): - raise exceptions.CommandError( - "Unable to delete any of the specified share groups." - ) - - if args.wait: - for share_group in share_group_to_delete: - try: - _wait_for_resource_status( - cs, - share_group, - resource_type='share_group', - expected_status='deleted', - ) - except exceptions.CommandError as e: - print(e, file=sys.stderr) - - -@cliutils.arg( - 'share_group', - metavar='', - help='Name or ID of the share group to modify.', -) -@cliutils.arg( - '--state', - metavar='', - default='available', - help=( - 'Indicate which state to assign the share group. ' - 'Options include available, error, creating, deleting, ' - 'error_deleting. If no state is provided, ' - 'available will be used.' - ), -) -@cliutils.service_type('sharev2') -def do_share_group_reset_state(cs, args): - """Explicitly update the state of a share group - - (Admin only). - """ - share_group = _find_share_group(cs, args.share_group) - cs.share_groups.reset_state(share_group, args.state) - - -############################################################################## -# -# Share group snapshots -# -############################################################################## - - -@cliutils.arg( - 'share_group', - metavar='', - help='Name or ID of the share group.', -) -@cliutils.arg( - '--name', - metavar='', - help='Optional share group snapshot name. (Default=None)', - default=None, -) -@cliutils.arg( - '--description', - metavar='', - help='Optional share group snapshot description. (Default=None)', - default=None, -) -@cliutils.arg( - '--wait', - action='store_true', - default=False, - help='Wait for share group snapshot to be created', -) -@cliutils.service_type('sharev2') -def do_share_group_snapshot_create(cs, args): - """Creates a new share group snapshot.""" - kwargs = {'name': args.name, 'description': args.description} - share_group = _find_share_group(cs, args.share_group) - sg_snapshot = cs.share_group_snapshots.create(share_group.id, **kwargs) - if args.wait: - _wait_for_resource_status( - cs, - sg_snapshot, - resource_type='share_group_snapshot', - expected_status='available', - ) - _print_share_group_snapshot(cs, sg_snapshot) - - -@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='', default=None, help='Filter results by name.' -) -@cliutils.arg( - '--status', - metavar='', - default=None, - help='Filter results by status.', -) -@cliutils.arg( - '--share-group-id', - '--share_group_id', - metavar='', - default=None, - action='single_alias', - help='Filter results by share group ID.', -) -@cliutils.arg( - '--limit', - metavar='', - type=int, - default=None, - help='Maximum number of share group snapshots to return. (Default=None)', -) -@cliutils.arg( - '--offset', - metavar="", - default=None, - help='Start position of share group snapshot listing.', -) -@cliutils.arg( - '--sort-key', - '--sort_key', - metavar='', - type=str, - default=None, - action='single_alias', - help=( - f'Key to be sorted, available keys are ' - f'{constants.SHARE_GROUP_SNAPSHOT_SORT_KEY_VALUES}. Default=None.' - ), -) -@cliutils.arg( - '--sort-dir', - '--sort_dir', - metavar='', - type=str, - default=None, - action='single_alias', - help=f'Sort direction, available values are {constants.SORT_DIR_VALUES}. ' - 'OPTIONAL: Default=None.', -) -@cliutils.arg( - '--detailed', - dest='detailed', - default=True, - help='Show detailed information about share group snapshots.', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,name".', -) -@cliutils.service_type('sharev2') -def do_share_group_snapshot_list(cs, args): - """List share group snapshots with filters.""" - if args.columns is not None: - list_of_keys = _split_columns(columns=args.columns) - else: - list_of_keys = ('id', 'name', 'status', 'description') - - all_projects = int( - os.environ.get( - "ALL_TENANTS", os.environ.get("ALL_PROJECTS", args.all_projects) - ) - ) - - search_opts = { - 'offset': args.offset, - 'limit': args.limit, - 'all_tenants': all_projects, - 'name': args.name, - 'status': args.status, - 'share_group_id': args.share_group_id, - } - share_group_snapshots = cs.share_group_snapshots.list( - detailed=args.detailed, - search_opts=search_opts, - sort_key=args.sort_key, - sort_dir=args.sort_dir, - ) - cliutils.print_list( - share_group_snapshots, fields=list_of_keys, sortby_index=None - ) - - -@cliutils.arg( - 'share_group_snapshot', - metavar='', - help='Name or ID of the share group snapshot.', -) -@cliutils.service_type('sharev2') -def do_share_group_snapshot_show(cs, args): - """Show details about a share group snapshot.""" - sg_snapshot = _find_share_group_snapshot(cs, args.share_group_snapshot) - _print_share_group_snapshot(cs, sg_snapshot) - - -@cliutils.arg( - 'share_group_snapshot', - metavar='', - help='Name or ID of the share group snapshot.', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,name".', -) -@cliutils.service_type('sharev2') -def do_share_group_snapshot_list_members(cs, args): - """List members of a share group snapshot.""" - if args.columns is not None: - list_of_keys = _split_columns(columns=args.columns) - else: - list_of_keys = ('Share ID', 'Size') - - sg_snapshot = _find_share_group_snapshot(cs, args.share_group_snapshot) - members = [ - type('ShareGroupSnapshotMember', (object,), member) - for member in sg_snapshot._info.get('members', []) - ] - cliutils.print_list(members, fields=list_of_keys) - - -@cliutils.arg( - '--state', - metavar='', - default='available', - help=( - 'Indicate which state to assign the share group snapshot. ' - 'Options include available, error, creating, deleting, ' - 'error_deleting. If no state is provided, ' - 'available will be used.' - ), -) -@cliutils.arg( - 'share_group_snapshot', - metavar='', - help='Name or ID of the share group snapshot.', -) -@cliutils.service_type('sharev2') -def do_share_group_snapshot_reset_state(cs, args): - """Explicitly update the state of a share group snapshot - - (Admin only). - """ - sg_snapshot = _find_share_group_snapshot(cs, args.share_group_snapshot) - cs.share_group_snapshots.reset_state(sg_snapshot, args.state) - - -@cliutils.arg( - 'share_group_snapshot', - metavar='', - help='Name or ID of the share group snapshot to update.', -) -@cliutils.arg( - '--name', - metavar='', - default=None, - help='Optional new name for the share group snapshot. (Default=None)', -) -@cliutils.arg( - '--description', - metavar='', - help='Optional share group snapshot description. (Default=None)', - default=None, -) -@cliutils.service_type('sharev2') -def do_share_group_snapshot_update(cs, args): - """Update a share group snapshot.""" - kwargs = {} - - if args.name is not None: - kwargs['name'] = args.name - - if args.description is not None: - kwargs['description'] = args.description - - if not kwargs: - msg = "Must supply name and/or description" - raise exceptions.CommandError(msg) - - sg_snapshot = _find_share_group_snapshot(cs, args.share_group_snapshot) - cs.share_group_snapshots.update(sg_snapshot, **kwargs) - - -@cliutils.arg( - 'share_group_snapshot', - metavar='', - nargs='+', - help='Name or ID of the share group snapshot(s) to delete.', -) -@cliutils.arg( - '--force', - action='store_true', - default=False, - help='Attempt to force delete the share group snapshot(s) (Default=False)' - ' (Admin only).', -) -@cliutils.arg( - '--wait', - action='store_true', - default=False, - help='Wait for share group snapshot to be deleted', -) -@cliutils.service_type('sharev2') -def do_share_group_snapshot_delete(cs, args): - """Remove one or more share group snapshots.""" - failure_count = 0 - kwargs = {} - - kwargs['force'] = args.force - - for sg_snapshot in args.share_group_snapshot: - try: - sg_snapshot_ref = _find_share_group_snapshot(cs, sg_snapshot) - cs.share_group_snapshots.delete(sg_snapshot_ref, **kwargs) - if args.wait: - _wait_for_resource_status( - cs, - sg_snapshot, - resource_type='share_group_snapshot', - expected_status='deleted', - ) - except Exception as e: - failure_count += 1 - print( - f"Delete for share group snapshot {sg_snapshot} failed: {e}", - file=sys.stderr, - ) - - if failure_count == len(args.share_group_snapshot): - raise exceptions.CommandError( - "Unable to delete any of the specified share group snapshots." - ) - - -############################################################################## -# -# Share replicas -# -############################################################################## - - -@cliutils.arg( - '--share-id', - '--share_id', - '--si', # alias - metavar='', - default=None, - action='single_alias', - help='List replicas belonging to share.', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "replica_state,id".', -) -@api_versions.wraps("2.11") -def do_share_replica_list(cs, args): - """List share replicas.""" - share = _find_share(cs, args.share_id) if args.share_id else None - - if args.columns is not None: - list_of_keys = _split_columns(columns=args.columns) - else: - list_of_keys = [ - 'ID', - 'Status', - 'Replica State', - 'Share ID', - 'Host', - 'Availability Zone', - 'Updated At', - ] - - if share: - replicas = cs.share_replicas.list(share) - else: - replicas = cs.share_replicas.list() - - cliutils.print_list(replicas, list_of_keys) - - -@api_versions.wraps("2.11", "2.66") -@cliutils.arg( - 'share', metavar='', help='Name or ID of the share to replicate.' -) -@cliutils.arg( - '--availability-zone', - '--availability_zone', - '--az', - default=None, - action='single_alias', - metavar='', - help='Optional Availability zone in which replica should be created.', -) -def do_share_replica_create(cs, args): - """Create a share replica.""" - share = _find_share(cs, args.share) - - body = { - 'share': share, - 'availability_zone': args.availability_zone, - } - replica = cs.share_replicas.create(**body) - _print_share_replica(cs, replica) - - -@api_versions.wraps("2.67", "2.71") -@cliutils.arg( - 'share', metavar='', help='Name or ID of the share to replicate.' -) -@cliutils.arg( - '--availability-zone', - '--availability_zone', - '--az', - default=None, - action='single_alias', - metavar='', - help='Optional Availability zone in which replica should be created.', -) -@cliutils.arg( - '--scheduler-hints', - '--scheduler_hints', - '--sh', - metavar='', - nargs='*', - help='Scheduler hints for the share replica as key=value pairs, ' - 'Supported key is only_host. Available for microversion >= 2.67. ', - default=None, -) -def do_share_replica_create(cs, args): # noqa - """Create a share replica.""" - share = _find_share(cs, args.share) - - scheduler_hints = {} - if args.scheduler_hints: - hints = _extract_key_value_options(args, 'scheduler_hints') - if 'only_host' not in hints.keys() or len(hints) > 1: - raise exceptions.CommandError( - "The only valid key supported with the --scheduler-hints " - "argument is 'only_host'." - ) - scheduler_hints['only_host'] = hints.get('only_host') - - body = { - 'share': share, - 'availability_zone': args.availability_zone, - } - if scheduler_hints: - body['scheduler_hints'] = scheduler_hints - - replica = cs.share_replicas.create(**body) - _print_share_replica(cs, replica) - - -@api_versions.wraps("2.72") -@cliutils.arg( - 'share', metavar='', help='Name or ID of the share to replicate.' -) -@cliutils.arg( - '--availability-zone', - '--availability_zone', - '--az', - default=None, - action='single_alias', - metavar='', - help='Optional Availability zone in which replica should be created.', -) -@cliutils.arg( - '--scheduler-hints', - '--scheduler_hints', - '--sh', - metavar='', - nargs='*', - help='Scheduler hints for the share replica as key=value pairs, ' - 'Supported key is only_host. Available for microversion >= 2.67. ', - default=None, -) -@cliutils.arg( - '--share-network', - '--share_network', - metavar='', - default=None, - action='single_alias', - help='Optional network info ID or name. ' - 'Available only for microversion >= 2.72', -) -@cliutils.arg( - '--wait', - action='store_true', - default=False, - help='Wait for share replica to be created', -) -def do_share_replica_create(cs, args): # noqa - """Create a share replica.""" - share = _find_share(cs, args.share) - - scheduler_hints = {} - if args.scheduler_hints: - hints = _extract_key_value_options(args, 'scheduler_hints') - if 'only_host' not in hints.keys() or len(hints) > 1: - raise exceptions.CommandError( - "The only valid key supported with the --scheduler-hints " - "argument is 'only_host'." - ) - scheduler_hints['only_host'] = hints.get('only_host') - - share_network = None - if args.share_network: - share_network = _find_share_network(cs, args.share_network) - - body = { - 'share': share, - 'availability_zone': args.availability_zone, - } - if scheduler_hints: - body['scheduler_hints'] = scheduler_hints - - if share_network: - body['share_network'] = share_network - - replica = cs.share_replicas.create(**body) - if args.wait: - _wait_for_resource_status( - cs, - replica, - resource_type='share_replica', - expected_status='available', - ) - _print_share_replica(cs, replica) - - -@cliutils.arg('replica', metavar='', help='ID of the share replica.') -@api_versions.wraps("2.11", "2.46") -def do_share_replica_show(cs, args): - """Show details about a replica.""" - - replica = cs.share_replicas.get(args.replica) - _print_share_replica(cs, replica) - - -@api_versions.wraps("2.47") # noqa -@cliutils.arg('replica', metavar='', help='ID of the share replica.') -def do_share_replica_show(cs, args): # noqa - """Show details about a replica.""" - - replica = cs.share_replicas.get(args.replica) - export_locations = cs.share_replica_export_locations.list(replica) - replica._info['export_locations'] = export_locations - _print_share_replica(cs, replica) - - -@cliutils.arg( - 'replica', metavar='', nargs='+', help='ID of the share replica.' -) -@cliutils.arg( - '--force', - action='store_true', - default=False, - help='Attempt to force deletion of a replica on its backend. Using ' - 'this option will purge the replica from Manila even if it ' - 'is not cleaned up on the backend. Defaults to False.', -) -@cliutils.arg( - '--wait', - action='store_true', - default=False, - help='Wait for share replica to be deleted', -) -@api_versions.wraps("2.11") -def do_share_replica_delete(cs, args): - """Remove one or more share replicas.""" - failure_count = 0 - kwargs = {"force": args.force} - - for replica in args.replica: - try: - replica_ref = _find_share_replica(cs, replica) - cs.share_replicas.delete(replica_ref, **kwargs) - if args.wait: - _wait_for_resource_status( - cs, - replica_ref, - resource_type='share_replica', - expected_status='deleted', - ) - except Exception as e: - failure_count += 1 - print( - f"Delete for share replica {replica} failed: {e}", - file=sys.stderr, - ) - - if failure_count == len(args.replica): - raise exceptions.CommandError( - "Unable to delete any of the specified replicas." - ) - - -@cliutils.arg('replica', metavar='', help='ID of the share replica.') -@api_versions.wraps("2.11", "2.74") -def do_share_replica_promote(cs, args): - """Promote specified replica to 'active' replica_state.""" - replica = _find_share_replica(cs, args.replica) - cs.share_replicas.promote(replica) - - -@cliutils.arg('replica', metavar='', help='ID of the share replica.') -@cliutils.arg( - '--quiesce-wait-time', - metavar='', - default=None, - help='Quiesce wait time in seconds. Available for microversion >= 2.75', -) -@cliutils.arg( - '--wait', - action='store_true', - default=False, - help='Wait for share replica to be promoted', -) -@api_versions.wraps("2.75") # noqa -def do_share_replica_promote(cs, args): # noqa - """Promote specified replica to 'active' replica_state.""" - replica = _find_share_replica(cs, args.replica) - - quiesce_wait_time = None - if args.quiesce_wait_time: - quiesce_wait_time = args.quiesce_wait_time - cs.share_replicas.promote(replica, quiesce_wait_time) - if args.wait: - _wait_for_resource_status( - cs, - replica, - resource_type='share_replica', - expected_status='active', - status_attr='replica_state', - ) - - -@api_versions.wraps("2.47") -@cliutils.arg('replica', metavar='', help='ID of the share replica.') -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,path,replica_state".', -) -def do_share_replica_export_location_list(cs, args): - """List export locations of a share replica.""" - if args.columns is not None: - list_of_keys = _split_columns(columns=args.columns) - else: - list_of_keys = [ - 'ID', - 'Availability Zone', - 'Replica State', - 'Preferred', - 'Path', - ] - replica = _find_share_replica(cs, args.replica) - export_locations = cs.share_replica_export_locations.list(replica) - cliutils.print_list(export_locations, list_of_keys) - - -@api_versions.wraps("2.47") -@cliutils.arg( - 'replica', metavar='', help='Name or ID of the share replica.' -) -@cliutils.arg( - 'export_location', - metavar='', - help='ID of the share replica export location.', -) -def do_share_replica_export_location_show(cs, args): - """Show details of a share replica's export location.""" - replica = _find_share_replica(cs, args.replica) - export_location = cs.share_replica_export_locations.get( - replica, args.export_location - ) - view_data = export_location._info.copy() - cliutils.print_dict(view_data) - - -@cliutils.arg( - 'replica', metavar='', help='ID of the share replica to modify.' -) -@cliutils.arg( - '--state', - metavar='', - default='available', - help=( - 'Indicate which state to assign the replica. Options include ' - 'available, error, creating, deleting, error_deleting. If no ' - 'state is provided, available will be used.' - ), -) -@api_versions.wraps("2.11") -def do_share_replica_reset_state(cs, args): - """Explicitly update the 'status' of a share replica.""" - replica = _find_share_replica(cs, args.replica) - cs.share_replicas.reset_state(replica, args.state) - - -@cliutils.arg( - 'replica', metavar='', help='ID of the share replica to modify.' -) -@cliutils.arg( - '--replica-state', - '--replica_state', - '--state', # alias for user sanity - metavar='', - default='out_of_sync', - action='single_alias', - help=( - 'Indicate which replica_state to assign the replica. Options ' - 'include in_sync, out_of_sync, active, error. If no ' - 'state is provided, out_of_sync will be used.' - ), -) -@api_versions.wraps("2.11") -def do_share_replica_reset_replica_state(cs, args): - """Explicitly update the 'replica_state' of a share replica.""" - replica = _find_share_replica(cs, args.replica) - cs.share_replicas.reset_replica_state(replica, args.replica_state) - - -@cliutils.arg( - 'replica', metavar='', help='ID of the share replica to resync.' -) -@api_versions.wraps("2.11") -def do_share_replica_resync(cs, args): - """Attempt to update the share replica with its 'active' mirror.""" - replica = _find_share_replica(cs, args.replica) - cs.share_replicas.resync(replica) - - -############################################################################## -# -# Share Transfer -# -############################################################################## - - -def _print_share_transfer(transfer): - info = transfer._info.copy() - info.pop('links', None) - - cliutils.print_dict(info) - - -@api_versions.wraps("2.77") -@cliutils.arg( - 'share', metavar='', help='Name or ID of share to transfer.' -) -@cliutils.arg( - '--name', - metavar='', - default=None, - help='Transfer name. Default=None.', -) -def do_share_transfer_create(cs, args): - """Creates a share transfer.""" - share = _find_share(cs, args.share) - transfer = cs.transfers.create(share.id, args.name) - _print_share_transfer(transfer) - - -@api_versions.wraps("2.77") -@cliutils.arg( - 'transfer', - metavar='', - nargs='+', - help='ID or name of the transfer(s).', -) -def do_share_transfer_delete(cs, args): - """Remove one or more transfers.""" - failure_count = 0 - - for transfer in args.transfer: - try: - transfer_ref = _find_share_transfer(cs, transfer) - transfer_ref.delete() - except Exception as e: - failure_count += 1 - print( - f"Delete for share transfer {transfer} failed: {e}", - file=sys.stderr, - ) - - if failure_count == len(args.transfer): - raise exceptions.CommandError( - "Unable to delete any of the specified transfers." - ) - - -@api_versions.wraps("2.77") -@cliutils.arg( - 'transfer', metavar='', help='ID of transfer to accept.' -) -@cliutils.arg( - 'auth_key', - metavar='', - help='Authentication key of transfer to accept.', -) -@cliutils.arg( - '--clear-rules', - '--clear_rules', - dest='clear_rules', - action='store_true', - default=False, - help="Whether manila should clean up the access rules after the " - "transfer is complete. (Default=False)", -) -def do_share_transfer_accept(cs, args): - """Accepts a share transfer.""" - cs.transfers.accept( - args.transfer, args.auth_key, clear_access_rules=args.clear_rules - ) - - -@api_versions.wraps("2.77") -@cliutils.arg( - '--all-tenants', - '--all-projects', - action='single_alias', - dest='all_projects', - metavar='<0|1>', - nargs='?', - type=int, - const=1, - default=0, - help='Shows details for all tenants. (Admin only).', -) -@cliutils.arg( - '--name', - metavar='', - default=None, - action='single_alias', - help='Transfer name. Default=None.', -) -@cliutils.arg( - '--id', - metavar='', - default=None, - action='single_alias', - help='Transfer ID. Default=None.', -) -@cliutils.arg( - '--resource-type', - '--resource_type', - metavar='', - default=None, - action='single_alias', - help='Transfer type, which can be share or network. Default=None.', -) -@cliutils.arg( - '--resource-id', - '--resource_id', - metavar='', - default=None, - action='single_alias', - help='Transfer resource id. Default=None.', -) -@cliutils.arg( - '--source-project-id', - '--source_project_id', - metavar='', - default=None, - action='single_alias', - help='Transfer source project id. Default=None.', -) -@cliutils.arg( - '--limit', - metavar='', - type=int, - default=None, - help='Maximum number of messages to return. (Default=None)', -) -@cliutils.arg( - '--offset', - metavar="", - default=None, - help='Start position of message listing.', -) -@cliutils.arg( - '--sort-key', - '--sort_key', - metavar='', - type=str, - default=None, - action='single_alias', - help=( - f'Key to be sorted, available keys are ' - f'{constants.SHARE_TRANSFER_SORT_KEY_VALUES}. Default=None.' - ), -) -@cliutils.arg( - '--sort-dir', - '--sort_dir', - metavar='', - type=str, - default=None, - action='single_alias', - help=f'Sort direction, available values are {constants.SORT_DIR_VALUES}. ' - 'Optional: Default=None.', -) -@cliutils.arg( - '--detailed', - dest='detailed', - metavar='<0|1>', - nargs='?', - type=int, - const=1, - default=0, - help="Show detailed information about filtered share transfers.", -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "id,resource_id".', -) -def do_share_transfer_list(cs, args): - """Lists all transfers.""" - if args.columns is not None: - list_of_keys = _split_columns(columns=args.columns) - else: - list_of_keys = ['ID', 'Name', 'Resource Type', 'Resource Id'] - - if args.detailed: - list_of_keys.extend( - [ - 'Created At', - 'Expires At', - 'Source Project Id', - 'Destination Project Id', - 'Accepted', - ] - ) - - all_projects = int( - os.environ.get( - "ALL_TENANTS", os.environ.get("ALL_PROJECTS", args.all_projects) - ) - ) - - search_opts = { - 'offset': args.offset, - 'limit': args.limit, - 'all_tenants': all_projects, - 'id': args.id, - 'name': args.name, - 'resource_type': args.resource_type, - 'resource_id': args.resource_id, - 'source_project_id': args.source_project_id, - } - share_transfers = cs.transfers.list( - detailed=args.detailed, - search_opts=search_opts, - sort_key=args.sort_key, - sort_dir=args.sort_dir, - ) - cliutils.print_list( - share_transfers, fields=list_of_keys, sortby_index=None - ) - - -@api_versions.wraps("2.77") -@cliutils.arg( - 'transfer', metavar='', help='Name or ID of transfer to show.' -) -def do_share_transfer_show(cs, args): - """Delete a transfer.""" - transfer = _find_share_transfer(cs, args.transfer) - _print_share_transfer(transfer) - - -############################################################################## -# -# User Messages -# -############################################################################## - - -@api_versions.wraps("2.37") -@cliutils.arg( - '--resource_id', - '--resource-id', - '--resource', - metavar='', - default=None, - action='single_alias', - help='Filters results by a resource uuid. Default=None.', -) -@cliutils.arg( - '--resource_type', - '--resource-type', - metavar='', - default=None, - action='single_alias', - help='Filters results by a resource type. Default=None. ' - 'Example: "manila message-list --resource_type share"', -) -@cliutils.arg( - '--action_id', - '--action-id', - '--action', - metavar='', - default=None, - action='single_alias', - help='Filters results by action id. Default=None.', -) -@cliutils.arg( - '--detail_id', - '--detail-id', - '--detail', - metavar='', - default=None, - action='single_alias', - help='Filters results by detail id. Default=None.', -) -@cliutils.arg( - '--request_id', - '--request-id', - '--request', - metavar='', - default=None, - action='single_alias', - help='Filters results by request id. Default=None.', -) -@cliutils.arg( - '--level', - '--message_level', - '--message-level', - metavar='', - default=None, - action='single_alias', - help='Filters results by the message level. Default=None. ' - 'Example: "manila message-list --level ERROR".', -) -@cliutils.arg( - '--limit', - metavar='', - type=int, - default=None, - help='Maximum number of messages to return. (Default=None)', -) -@cliutils.arg( - '--offset', - metavar="", - default=None, - help='Start position of message listing.', -) -@cliutils.arg( - '--sort-key', - '--sort_key', - metavar='', - type=str, - default=None, - action='single_alias', - help=( - f'Key to be sorted, available keys are ' - f'{constants.MESSAGE_SORT_KEY_VALUES}. Default=desc.' - ), -) -@cliutils.arg( - '--sort-dir', - '--sort_dir', - metavar='', - type=str, - default=None, - action='single_alias', - help=f'Sort direction, available values are {constants.SORT_DIR_VALUES}. ' - 'OPTIONAL: Default=None.', -) -@cliutils.arg( - '--columns', - metavar='', - type=str, - default=None, - help='Comma separated list of columns to be displayed ' - 'example --columns "resource_id,user_message".', -) -@cliutils.arg( - '--since', - metavar='', - default=None, - help='Return only user messages created since given date. ' - 'The date format must be conforming to ISO8601. ' - 'Available only for microversion >= 2.52.', -) -@cliutils.arg( - '--before', - metavar='', - default=None, - help='Return only user messages created before given date. ' - 'The date format must be conforming to ISO8601. ' - 'Available only for microversion >= 2.52.', -) -def do_message_list(cs, args): - """Lists all messages.""" - if args.columns is not None: - list_of_keys = _split_columns(columns=args.columns) - else: - list_of_keys = [ - 'ID', - 'Resource Type', - 'Resource ID', - 'Action ID', - 'User Message', - 'Detail ID', - 'Created At', - ] - - search_opts = { - 'offset': args.offset, - 'limit': args.limit, - 'request_id': args.request_id, - 'resource_type': args.resource_type, - 'resource_id': args.resource_id, - 'action_id': args.action_id, - 'detail_id': args.detail_id, - 'message_level': args.level, - } - if cs.api_version < api_versions.APIVersion("2.52"): - msg = ( - "Filtering messages by 'since' and 'before' is possible only " - "with Manila API version >=2.52" - ) - if getattr(args, 'since') or getattr(args, 'before'): - raise exceptions.CommandError(msg) - else: - search_opts['created_since'] = args.since - search_opts['created_before'] = args.before - - messages = cs.messages.list( - search_opts=search_opts, sort_key=args.sort_key, sort_dir=args.sort_dir - ) - cliutils.print_list(messages, fields=list_of_keys, sortby_index=None) - - -@cliutils.arg('message', metavar='', help='ID of the message.') -@api_versions.wraps("2.37") -def do_message_show(cs, args): - """Show details about a message.""" - - message = cs.messages.get(args.message) - _print_message(message) - - -@api_versions.wraps("2.37") -@cliutils.arg( - 'message', metavar='', nargs='+', help='ID of the message(s).' -) -def do_message_delete(cs, args): - """Remove one or more messages.""" - failure_count = 0 - - for message in args.message: - try: - message_ref = _find_message(cs, message) - cs.messages.delete(message_ref) - except Exception as e: - failure_count += 1 - print( - f"Delete for message {message} failed: {e}", - file=sys.stderr, - ) - - if failure_count == len(args.message): - raise exceptions.CommandError( - "Unable to delete any of the specified messages." - ) - - -def _print_message(message): - message_dict = { - 'id': message.id, - 'resource_type': message.resource_type, - 'resource_id': message.resource_id, - 'action_id': message.action_id, - 'user_message': message.user_message, - 'message_level': message.message_level, - 'detail_id': message.detail_id, - 'created_at': message.created_at, - 'expires_at': message.expires_at, - 'request_id': message.request_id, - } - cliutils.print_dict(message_dict) diff --git a/pyproject.toml b/pyproject.toml index 7b114c9b7..27125510e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,9 +30,6 @@ classifiers = [ Homepage = "https://docs.openstack.org/python-manilaclient/" Repository = "https://opendev.org/openstack/python-manilaclient/" -[project.scripts] -manila = "manilaclient.shell:main" - [project.entry-points."oslo.config.opts"] "manilaclient.config" = "manilaclient.config:list_opts" diff --git a/releasenotes/notes/remove-python-client-a142b99ccc89731c.yaml b/releasenotes/notes/remove-python-client-a142b99ccc89731c.yaml new file mode 100644 index 000000000..7dd6380eb --- /dev/null +++ b/releasenotes/notes/remove-python-client-a142b99ccc89731c.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + The "manila" CLI utility has been removed in this release. The + mapping/decoder documentation still exists to provide transition + to users that are unfamiliar with the "openstack" CLI. diff --git a/tools/manila.bash_completion b/tools/manila.bash_completion deleted file mode 100644 index 8491e8ab4..000000000 --- a/tools/manila.bash_completion +++ /dev/null @@ -1,15 +0,0 @@ -_manila() -{ - local cur prev opts - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - - opts="$(manila bash_completion)" - - COMPLETION_CACHE=~/.cache/manilaclient/*/*-cache - opts+=" "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ') - - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) -} -complete -F _manila manila