python-manilaclient-2.1.0/0000775000175000017500000000000013644133466015525 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/HACKING0000664000175000017500000000610213644133413016503 0ustar zuulzuul00000000000000Manila Style Commandments ========================= Step 1: Read https://www.python.org/dev/peps/pep-0008/ Step 2: Read https://www.python.org/dev/peps/pep-0008/ again Step 3: Read on Imports ------- - thou shalt not import objects, only modules - thou shalt not import more than one module per line - thou shalt not make relative imports - thou shalt organize your imports according to the following template :: # vim: tabstop=4 shiftwidth=4 softtabstop=4 {{stdlib imports in human alphabetical order}} \n {{manila imports in human alphabetical order}} \n \n {{begin your code}} General ------- - thou shalt put two newlines twixt toplevel code (funcs, classes, etc) - thou shalt put one newline twixt methods in classes and anywhere else - thou shalt not write "except:", use "except Exception:" at the very least - thou shalt include your name with TODOs as in "TODO(termie)" - thou shalt not name anything the same name as a builtin or reserved word - thou shalt not violate causality in our time cone, or else Human Alphabetical Order Examples --------------------------------- :: import httplib import logging import random import StringIO import time import unittest from manila.auth import users from manila.endpoint import api from manila.endpoint import cloud from manila import flags from manila import test Docstrings ---------- """A one line docstring looks like this and ends in a period.""" """A multiline docstring has a one-line summary, less than 80 characters. Then a new paragraph after a newline that explains in more detail any general information about the function, class or method. Example usages are also great to have here if it is a complex class for function. After you have finished your descriptions add an extra newline and close the quotations. When writing the docstring for a class, an extra line should be placed after the closing quotations. For more in-depth explanations for these decisions see https://www.python.org/dev/peps/pep-0257/ If you are going to describe parameters and return values, use Sphinx, the appropriate syntax is as follows. :param foo: the foo parameter :param bar: the bar parameter :returns: description of the return value """ Text encoding ------------- - All text within python code should be of type 'unicode'. WRONG: >>> s = 'foo' >>> s 'foo' >>> type(s) RIGHT: >>> u = u'foo' >>> u u'foo' >>> type(u) - Transitions between internal unicode and external strings should always be immediately and explicitly encoded or decoded. - All external text that is not explicitly encoded (database storage, commandline arguments, etc.) should be presumed to be encoded as utf-8. WRONG: mystring = infile.readline() myreturnstring = do_some_magic_with(mystring) outfile.write(myreturnstring) RIGHT: mystring = infile.readline() mytext = s.decode('utf-8') returntext = do_some_magic_with(mytext) returnstring = returntext.encode('utf-8') outfile.write(returnstring) python-manilaclient-2.1.0/playbooks/0000775000175000017500000000000013644133466017530 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/playbooks/legacy/0000775000175000017500000000000013644133466020774 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/playbooks/legacy/manilaclient-dsvm-neutron-functional/0000775000175000017500000000000013644133466030233 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/playbooks/legacy/manilaclient-dsvm-neutron-functional/run.yaml0000664000175000017500000000431113644133413031712 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-manilaclient-dsvm-neutron-functional from old job gate-manilaclient-dsvm-neutron-functional-ubuntu-xenial tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x cat << 'EOF' >>"/tmp/dg-local.conf" [[local|localrc]] enable_plugin manila https://opendev.org/openstack/manila EOF executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export PYTHONUNBUFFERED=true export DEVSTACK_GATE_NEUTRON=1 export DEVSTACK_PROJECT_FROM_GIT="python-manilaclient" export PROJECTS="openstack/manila-tempest-plugin $PROJECTS" # Keep localrc to be able to set some vars in pre_test_hook export KEEP_LOCALRC=1 export DEVSTACK_GATE_USE_PYTHON3=True export PYTHON3_VERSION="3.6" function pre_test_hook { source $BASE/new/python-manilaclient/contrib/ci/pre_test_hook.sh } export -f pre_test_hook function post_test_hook { # Configure and run functional tests source $BASE/new/python-manilaclient/contrib/ci/post_test_hook.sh } export -f post_test_hook cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' python-manilaclient-2.1.0/playbooks/legacy/manilaclient-dsvm-neutron-functional/post.yaml0000664000175000017500000000161513644133413032077 0ustar zuulzuul00000000000000- hosts: primary roles: - role: fetch-tox-output zuul_work_dir: "{{ ansible_user_dir }}/workspace/python-manilaclient" tox_envlist: functional - role: fetch-subunit-output zuul_work_dir: "{{ ansible_user_dir }}/workspace/python-manilaclient" tox_envlist: functional tasks: - name: Copy job config files synchronize: src: "{{ ansible_user_dir }}/workspace/python-manilaclient/etc" dest: "{{ zuul.executor.log_root }}" mode: pull verify_host: true - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: "{{ ansible_user_dir }}/workspace/" dest: "{{ zuul.executor.log_root }}" mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs python-manilaclient-2.1.0/.zuul.yaml0000664000175000017500000000166413644133413017465 0ustar zuulzuul00000000000000- project: templates: - publish-openstack-docs-pti - openstack-cover-jobs - openstack-lower-constraints-jobs - openstack-python3-ussuri-jobs - check-requirements - release-notes-jobs-python3 check: jobs: - manilaclient-dsvm-neutron-functional - rally-dsvm-manila-multibackend: voting: false - rally-dsvm-manila-multibackend-no-ss: voting: false gate: jobs: - manilaclient-dsvm-neutron-functional - job: name: manilaclient-dsvm-neutron-functional parent: legacy-dsvm-base run: playbooks/legacy/manilaclient-dsvm-neutron-functional/run.yaml post-run: playbooks/legacy/manilaclient-dsvm-neutron-functional/post.yaml timeout: 5400 required-projects: - openstack/devstack-gate - openstack/manila - openstack/neutron - openstack/python-manilaclient - openstack/manila-tempest-plugin python-manilaclient-2.1.0/AUTHORS0000664000175000017500000001207713644133465016603 0ustar zuulzuul00000000000000119Vik Aleks Chirko Alexander Pugachev Alexey Ovchinnikov Andreas Jaeger Andreas Jaeger Andrei V. Ostapenko Andrey Kurilin Ankit Agrawal Asha Saravanamohan Ben Swartzlander Bertrand Lallau Bob Callaway Cao Xuan Hoang ChangBo Guo(gcb) Chen Chris MacNaughton Christian Berendt Chuck Fouts Clinton Knight Corey Bryant Daniel Gonzalez Daniel Russell David Sariel Deepak C Shetty Denis Cavalcante Dirk Mueller Dirk Müller Doug Hellmann Douglas Viroel Dustin Schoenbrun Faiz Abidi Flavio Percoco Gage Hugo Ghanshyam Mann Goutham Pacha Ravi Goutham Pacha Ravi Gábor Antal Hangdong Zhang Hongbin Lu Ian Wienand Igor Malinovskiy Jacek Tomasiak James E. Blair Jan Provaznik Jeremy Liu Jeremy Stanley Jiao Pengju Joe Gordon John Spray Julia Varlamova Kudyukin Dmitry LiuNanke Luigi Toscano M V P Nitesh Maari Tamm Marc Koderer Matt Riedemann Nguyen Hai Nguyen Hung Phuong OTSUKA, Yuanying Ondřej Nový OpenStack Release Bot Pete Zaitcev Ramana Raja Rodrigo Barbieri Rodrigo Barbieri Sascha Peilicke Sean McGinnis Shane Wang Shuquan Huang Spyros Trigazis Stephen Finucane Sun Jun Swapnil Kulkarni (coolsvap) Thomas Bechtold Tom Barron Tom Patzig Tony Breeds Tony Xu Valeriy Ponomaryov Victoria Martinez de la Cruz Vieri <15050873171@163.com> Vincent Untz Vladyslav Drok Vu Cong Tuan Xing Yang Your Name Yulia Portnova andrebeltrami binean bswartz cFouts chenxiangui daiki kato deepak_mourya devin dineshbhor drngsl e haixin houming-wang howardlee iswarya_vakati jacky06 ji-xuepeng junboli kayrus kedy lijunbo lingyongxu liusheng liuyamin luke.li mark.sturdevant melissaml nidhimittalhada pengyuesheng qingszhao ricolin shu-mutou silvacarloss sonu.kumar sriram ramakrishnan sunjia ting.wang tpsilva venkatamahesh vik vkmc vponomaryov wangzhenyu wu.chunyang xiaozhuangqing yanjun.fu yogesh yuyafei zhiguo.li zhongjun zhongjun2 python-manilaclient-2.1.0/setup.cfg0000664000175000017500000000466713644133466017363 0ustar zuulzuul00000000000000[metadata] name = python-manilaclient summary = Client library for OpenStack Manila API. description-file = README.rst author = OpenStack author-email = openstack-discuss@lists.openstack.org home-page = https://docs.openstack.org/python-manilaclient/latest/ python-requires = >=3.6 classifier = Development Status :: 5 - Production/Stable Environment :: Console Environment :: OpenStack Intended Audience :: Developers Intended Audience :: Information Technology License :: OSI Approved :: Apache Software License Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 [files] packages = manilaclient [entry_points] console_scripts = manila = manilaclient.shell:main oslo.config.opts = manilaclient.config = manilaclient.config:list_opts openstack.cli.extension = share = manilaclient.osc.plugin openstack.share.v2 = share_list = manilaclient.osc.v2.share:ListShare share_create = manilaclient.osc.v2.share:CreateShare share_delete = manilaclient.osc.v2.share:DeleteShare share_show = manilaclient.osc.v2.share:ShowShare share_set = manilaclient.osc.v2.share:SetShare share_unset = manilaclient.osc.v2.share:UnsetShare share_access_create = manilaclient.osc.v2.share_access_rules:ShareAccessAllow share_access_delete = manilaclient.osc.v2.share_access_rules:ShareAccessDeny share_access_list = manilaclient.osc.v2.share_access_rules:ListShareAccess share_access_show = manilaclient.osc.v2.share_access_rules:ShowShareAccess share_access_set = manilaclient.osc.v2.share_access_rules:SetShareAccess share_access_unset = manilaclient.osc.v2.share_access_rules:UnsetShareAccess share_type_create = manilaclient.osc.v2.share_types:CreateShareType share_type_delete = manilaclient.osc.v2.share_types:DeleteShareType share_type_set = manilaclient.osc.v2.share_types:SetShareType share_type_unset = manilaclient.osc.v2.share_types:UnsetShareType share_type_list = manilaclient.osc.v2.share_types:ListShareType share_type_show = manilaclient.osc.v2.share_types:ShowShareType share_type_access_create = manilaclient.osc.v2.share_type_access:ShareTypeAccessAllow share_type_access_list = manilaclient.osc.v2.share_type_access:ListShareTypeAccess share_type_access_delete = manilaclient.osc.v2.share_type_access:ShareTypeAccessDeny [coverage:run] omit = manilaclient/tests/* branch = true [egg_info] tag_build = tag_date = 0 python-manilaclient-2.1.0/manilaclient/0000775000175000017500000000000013644133466020165 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/exceptions.py0000664000175000017500000000234113644133413022710 0ustar zuulzuul00000000000000# Copyright 2010 Jacob Kaplan-Moss # 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. """ Exception definitions. """ from manilaclient.common.apiclient.exceptions import * # noqa class NoTokenLookupException(ClientException): # noqa: F405 """No support for looking up endpoints. This form of authentication does not support looking up endpoints from an existing token. """ pass class VersionNotFoundForAPIMethod(Exception): msg_fmt = "API version '%(vers)s' is not supported on '%(method)s' method." def __init__(self, version, method): self.version = version self.method = method def __str__(self): return self.msg_fmt % {"vers": self.version, "method": self.method} python-manilaclient-2.1.0/manilaclient/extension.py0000664000175000017500000000257513644133413022554 0ustar zuulzuul00000000000000# Copyright 2011 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 manilaclient import base from manilaclient import utils class Extension(utils.HookableMixin): """Extension descriptor.""" SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__') def __init__(self, name, module): self.name = name self.module = module self._parse_extension_module() def _parse_extension_module(self): self.manager_class = None for attr_name, attr_value in list(self.module.__dict__.items()): if attr_name in self.SUPPORTED_HOOKS: self.add_hook(attr_name, attr_value) elif utils.safe_issubclass(attr_value, base.Manager): self.manager_class = attr_value def __repr__(self): return "" % self.name python-manilaclient-2.1.0/manilaclient/osc/0000775000175000017500000000000013644133466020751 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/osc/v2/0000775000175000017500000000000013644133466021300 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/osc/v2/share_types.py0000664000175000017500000004044213644133413024174 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging import six from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as oscutils from oslo_utils import strutils from manilaclient import api_versions from manilaclient.common._i18n import _ from manilaclient.common.apiclient import utils as apiutils from manilaclient.common import constants from manilaclient.osc import utils LOG = logging.getLogger(__name__) ATTRIBUTES = [ 'id', 'name', 'visibility', 'is_default', 'required_extra_specs', 'optional_extra_specs', 'description' ] def format_share_type(share_type): # share_type_access:is_public (true/false) --> visibility (public/private) is_public = 'share_type_access:is_public' visibility = 'public' if share_type._info.get(is_public) else 'private' share_type._info.pop(is_public, None) # optional_extra_specs --> extra_specs without required_extra_specs # required_extra_specs are displayed separately optional_extra_specs = share_type.extra_specs for key in share_type.required_extra_specs.keys(): optional_extra_specs.pop(key, None) share_type._info.update( { 'visibility': visibility, 'required_extra_specs': utils.format_properties( share_type.required_extra_specs), 'optional_extra_specs': utils.format_properties( optional_extra_specs), } ) return share_type class CreateShareType(command.ShowOne): """Create new share type.""" _description = _( "Create new share type") log = logging.getLogger(__name__ + ".CreateShareType") def get_parser(self, prog_name): parser = super(CreateShareType, self).get_parser(prog_name) parser.add_argument( 'name', metavar="", default=None, help=_('Share type name') ) parser.add_argument( 'spec_driver_handles_share_servers', metavar="", default=None, help=_("Required extra specification. " "Valid values are 'true' and 'false'") ) parser.add_argument( "--description", metavar="", default=None, help=_("Share type description. " "Available only for microversion >= 2.41."), ) parser.add_argument( "--snapshot-support", metavar="", default=None, help=_("Boolean extra spec used for filtering of back ends " "by their capability to create share snapshots."), ) parser.add_argument( "--create-share-from-snapshot-support", metavar="", default=None, help=_("Boolean extra spec used for filtering of back ends " "by their capability to create shares from snapshots."), ) parser.add_argument( "--revert-to-snapshot-support", metavar="", default=False, help=_("Boolean extra spec used for filtering of back ends " "by their capability to revert shares to snapshots. " "(Default is False)."), ) parser.add_argument( "--mount-snapshot-support", metavar="", default=False, help=_("Boolean extra spec used for filtering of back ends " "by their capability to mount share snapshots. " "(Default is False)."), ) parser.add_argument( "--extra-specs", type=str, nargs='*', metavar='', default=None, 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."), ) parser.add_argument( '--public', metavar="", default=True, help=_('Make type accessible to the public (default true).') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share kwargs = { 'name': parsed_args.name } try: kwargs['spec_driver_handles_share_servers'] = ( strutils.bool_from_string( parsed_args.spec_driver_handles_share_servers, strict=True)) except ValueError as e: msg = ("Argument spec_driver_handles_share_servers " "argument is not valid: %s" % six.text_type(e)) raise exceptions.CommandError(msg) if parsed_args.description: if share_client.api_version.matches( api_versions.APIVersion("2.41"), api_versions.APIVersion()): kwargs['description'] = parsed_args.description else: raise exceptions.CommandError( "Adding description to share type " "is only available with API microversion >= 2.41") if parsed_args.public: kwargs['is_public'] = strutils.bool_from_string( parsed_args.public, default=True) extra_specs = {} if parsed_args.extra_specs: for item in parsed_args.extra_specs: (key, value) = item.split('=', 1) if key == 'driver_handles_share_servers': msg = ("'driver_handles_share_servers' " "is already set via positional argument.") raise exceptions.CommandError(msg) else: extra_specs = utils.extract_extra_specs( extra_specs, [item]) for key in constants.BOOL_SPECS: value = getattr(parsed_args, key) if value: extra_specs = utils.extract_extra_specs( extra_specs, [key + '=' + value]) kwargs['extra_specs'] = extra_specs share_type = share_client.share_types.create(**kwargs) formatted_type = format_share_type(share_type) return (ATTRIBUTES, oscutils.get_dict_properties( formatted_type._info, ATTRIBUTES)) class DeleteShareType(command.Command): """Delete a share type.""" _description = _("Delete a share type") log = logging.getLogger(__name__ + ".DeleteShareType") def get_parser(self, prog_name): parser = super(DeleteShareType, self).get_parser(prog_name) parser.add_argument( 'share_types', metavar="", nargs="+", help=_("Name or ID of the share type(s) to delete") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for share_type in parsed_args.share_types: try: share_type_obj = apiutils.find_resource( share_client.share_types, share_type) share_client.share_types.delete(share_type_obj) except Exception as e: result += 1 LOG.error(_( "Failed to delete share type with " "name or ID '%(share_type)s': %(e)s"), {'share_type': share_type, 'e': e}) if result > 0: total = len(parsed_args.share_types) msg = (_("%(result)s of %(total)s share types failed " "to delete.") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) class SetShareType(command.Command): """Set share type properties.""" _description = _("Set share type properties") log = logging.getLogger(__name__ + ".SetShareType") def get_parser(self, prog_name): parser = super(SetShareType, self).get_parser(prog_name) parser.add_argument( 'share_type', metavar="", help=_("Name or ID of the share type to modify") ) parser.add_argument( "--extra-specs", type=str, nargs='*', metavar='', default=None, 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."), ) parser.add_argument( '--public', metavar="", default=None, help=_('New visibility of the share type. If set to True, ' 'share type will be available to all projects ' 'in the cloud. ' 'Available only for microversion >= 2.50') ) parser.add_argument( "--description", metavar="", default=None, help=_("New description of share type. " "Available only for microversion >= 2.50"), ) parser.add_argument( '--name', metavar="", default=None, help=_('New name of share type. ' 'Available only for microversion >= 2.50') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_type = apiutils.find_resource( share_client.share_types, parsed_args.share_type) can_update = ( share_client.api_version >= api_versions.APIVersion('2.50')) kwargs = {} if parsed_args.name is not None: if can_update: kwargs['name'] = parsed_args.name else: raise exceptions.CommandError( "Setting (new) name to share type " "is only available with API microversion >= 2.50") if parsed_args.description is not None: if can_update: kwargs['description'] = parsed_args.description else: raise exceptions.CommandError( "Setting (new) description to share type " "is only available with API microversion >= 2.50") if parsed_args.public is not None: if can_update: kwargs['is_public'] = strutils.bool_from_string( parsed_args.public, default=True) else: raise exceptions.CommandError( "Setting visibility to share type " "is only available with API microversion >= 2.50") if kwargs: share_type.update(**kwargs) if parsed_args.extra_specs: extra_specs = utils.extract_extra_specs( extra_specs={}, specs_to_add=parsed_args.extra_specs) try: share_type.set_keys(extra_specs) except Exception as e: raise exceptions.CommandError( "Failed to set share type key: %s" % e) class UnsetShareType(command.Command): """Unset share type extra specs.""" _description = _("Unset share type extra specs") log = logging.getLogger(__name__ + ".UnsetShareType") def get_parser(self, prog_name): parser = super(UnsetShareType, self).get_parser(prog_name) parser.add_argument( 'share_type', metavar="", help=_("Name or ID of the share type to modify") ) parser.add_argument( 'extra_specs', metavar='', nargs='+', help=_('Remove extra_specs from this share type'), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_type = apiutils.find_resource( share_client.share_types, parsed_args.share_type) if parsed_args.extra_specs: try: share_type.unset_keys(parsed_args.extra_specs) except Exception as e: raise exceptions.CommandError( "Failed to remove share type extra spec: %s" % e) class ListShareType(command.Lister): """List Share Types.""" _description = _("List share types") log = logging.getLogger(__name__ + ".ListShareType") def get_parser(self, prog_name): parser = super(ListShareType, self).get_parser(prog_name) parser.add_argument( '--all', action='store_true', default=False, help=_('Display all share types whatever public or private. ' 'Default=False. (Admin only)'), ) parser.add_argument( '--extra-specs', type=str, nargs='*', metavar='', default=None, help=_('Filter share types with extra specs (key=value). ' 'Available only for API microversion >= 2.43. ' 'OPTIONAL: Default=None.'), ) parser.add_argument( '--columns', metavar='', help=_('Comma separated list of columns to be displayed ' 'example --columns "id,name".'), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share search_opts = {} if parsed_args.extra_specs: if share_client.api_version < api_versions.APIVersion("2.43"): raise exceptions.CommandError( "Filtering by 'extra_specs' is available only with " "API microversion '2.43' and above.") search_opts = { 'extra_specs': utils.extract_extra_specs( extra_specs={}, specs_to_add=parsed_args.extra_specs) } share_types = share_client.share_types.list( search_opts=search_opts, show_all=parsed_args.all) if parsed_args.columns: columns = parsed_args.columns.split(',') for column in columns: if column not in ATTRIBUTES: msg = ("No column named '%s'. " "Possible columns are: 'id', 'name', 'visibility', " "is_default', 'required_extra_specs', " "'optional_extra_specs', 'description'." % column) raise exceptions.CommandError(msg) else: columns = ATTRIBUTES formatted_types = [] for share_type in share_types: formatted_types.append(format_share_type(share_type)) values = (oscutils.get_dict_properties( s._info, columns) for s in formatted_types) return (columns, values) class ShowShareType(command.ShowOne): """Show a share type.""" _description = _("Display share type details") def get_parser(self, prog_name): parser = super(ShowShareType, self).get_parser(prog_name) parser.add_argument( 'share_type', metavar="", help=_("Share type to display (name or ID)") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_type = apiutils.find_resource( share_client.share_types, parsed_args.share_type) formatted_type = format_share_type(share_type) return (ATTRIBUTES, oscutils.get_dict_properties( formatted_type._info, ATTRIBUTES)) python-manilaclient-2.1.0/manilaclient/osc/v2/share.py0000664000175000017500000005537513644133413022763 0ustar zuulzuul00000000000000# Copyright 2019 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from openstackclient.identity import common as identity_common from osc_lib.cli import format_columns from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient.common._i18n import _ from manilaclient.common.apiclient import utils as apiutils from manilaclient.common import cliutils from manilaclient.osc import utils LOG = logging.getLogger(__name__) SHARE_ATTRIBUTES = [ 'id', 'name', 'size', 'share_proto', 'status', 'is_public', 'share_type_name', 'availability_zone', 'description', 'share_network_id', 'share_server_id', 'share_type', 'share_group_id', 'host', 'user_id', 'project_id', 'access_rules_status', 'snapshot_id', 'snapshot_support', 'create_share_from_snapshot_support', 'mount_snapshot_support', 'revert_to_snapshot_support', 'task_state', 'source_share_group_snapshot_member_id', 'replication_type', 'has_replicas', 'created_at', 'metadata' ] SHARE_ATTRIBUTES_HEADERS = [ 'ID', 'Name', 'Size', 'Share Protocol', 'Status', 'Is Public', 'Share Type Name', 'Availability Zone', 'Description', 'Share Network ID', 'Share Server ID', 'Share Type', 'Share Group ID', 'Host', 'User ID', 'Project ID', 'Access Rules Status', 'Source Snapshot ID', 'Supports Creating Snapshots', 'Supports Cloning Snapshots', 'Supports Mounting snapshots', 'Supports Reverting to Snapshot', 'Migration Task Status', 'Source Share Group Snapshot Member ID', 'Replication Type', 'Has Replicas', 'Created At', 'Properties', ] class CreateShare(command.ShowOne): """Create a new share.""" _description = _("Create new share") log = logging.getLogger(__name__ + ".CreateShare") def get_parser(self, prog_name): parser = super(CreateShare, self).get_parser(prog_name) parser.add_argument( 'share_proto', metavar="", help=_('Share protocol (NFS, CIFS, CephFS, GlusterFS or HDFS)') ) parser.add_argument( 'size', metavar="", type=int, help=_('Share size in GiB.') ) parser.add_argument( '--name', metavar="", default=None, help=_('Optional share name. (Default=None)') ) parser.add_argument( '--snapshot-id', metavar="", default=None, help=_("Optional snapshot ID to create the share from." " (Default=None)") ) # NOTE(vkmc) --property replaces --metadata in osc parser.add_argument( "--property", metavar="", default={}, action=parseractions.KeyValueAction, help=_("Set a property to this share " "(repeat option to set multiple properties)"), ) parser.add_argument( '--share-network', metavar='', default=None, help=_('Optional network info ID or name.'), ) parser.add_argument( '--description', metavar='', default=None, help=_('Optional share description. (Default=None)') ) parser.add_argument( '--public', metavar='', default=False, help=_('Level of visibility for share. ' 'Defines whether other tenants are able to see it or not. ' '(Default = False)') ) parser.add_argument( '--share-type', metavar='', default=None, help=_('Optional share type. Use of optional shares type ' 'is deprecated. (Default=Default)') ) parser.add_argument( '--availability-zone', metavar='', default=None, help=_('Availability zone in which share should be created.') ) parser.add_argument( '--share-group', metavar='', default=None, help=_('Optional share group name or ID in which to create ' 'the share. (Default=None).') ) return parser def take_action(self, parsed_args): # TODO(s0ru): the table shows 'Field', 'Value' share_client = self.app.client_manager.share share_type = None if parsed_args.share_type: share_type = apiutils.find_resource(share_client.share_types, parsed_args.share_type).id share_network = None if parsed_args.share_network: share_network = apiutils.find_resource( share_client.share_networks, parsed_args.share_network).id share_group = None if parsed_args.share_group: share_group = apiutils.find_resource(share_client.share_groups, parsed_args.share_group).id size = parsed_args.size snapshot_id = None if parsed_args.snapshot_id: snapshot = apiutils.find_resource(share_client.share_snapshots, parsed_args.snapshot_id) size = max(size or 0, snapshot.size) body = { 'share_proto': parsed_args.share_proto, 'size': parsed_args.size, 'snapshot_id': snapshot_id, 'name': parsed_args.name, 'description': parsed_args.description, 'metadata': parsed_args.property, 'share_network': share_network, 'share_type': share_type, 'is_public': parsed_args.public, 'availability_zone': parsed_args.availability_zone, 'share_group_id': share_group } share = share_client.shares.create(**body) printable_share = share._info printable_share.pop('links', None) printable_share.pop('shares_type', None) return self.dict2columns(printable_share) class DeleteShare(command.Command): """Delete a share.""" _description = _("Delete a share") def get_parser(self, prog_name): parser = super(DeleteShare, self).get_parser(prog_name) parser.add_argument( "shares", metavar="", nargs="+", help=_("Share(s) to delete (name or ID)") ) parser.add_argument( "--share-group", metavar="", default=None, help=_("Optional share group (name or ID)" "which contains the share") ) parser.add_argument( "--force", action='store_true', default=False, help=_("Attempt forced removal of share(s), regardless of state " "(defaults to False)") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for share in parsed_args.shares: try: share_obj = apiutils.find_resource( share_client.shares, share ) share_group_id = (share_group_id if parsed_args.share_group else None) if parsed_args.force: share_client.shares.force_delete(share_obj) else: share_client.shares.delete(share_obj, share_group_id) except Exception as exc: result += 1 LOG.error(_("Failed to delete share with " "name or ID '%(share)s': %(e)s"), {'share': share, 'e': exc}) if result > 0: total = len(parsed_args.shares) msg = (_("%(result)s of %(total)s shares failed " "to delete.") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) class ListShare(command.Lister): """List Shared file systems (shares).""" _description = _("List shares") def get_parser(self, prog_name): parser = super(ListShare, self).get_parser(prog_name) parser.add_argument( '--name', metavar="", help=_('Filter shares by share name') ) parser.add_argument( '--status', metavar="", help=_('Filter shares by status') ) parser.add_argument( '--snapshot', metavar='', help=_('Filter shares by snapshot name or id.'), ) parser.add_argument( '--public', action='store_true', default=False, help=_('Include public shares'), ) parser.add_argument( '--share-network', metavar='', help=_('Filter shares exported on a given share network'), ) parser.add_argument( '--share-type', metavar='', help=_('Filter shares of a given share type'), ) parser.add_argument( '--share-group', metavar='', help=_('Filter shares belonging to a given share group'), ) parser.add_argument( '--host', metavar='', help=_('Filter shares belonging to a given host (admin only)'), ) parser.add_argument( '--share-server', metavar='', help=_('Filter shares exported via a given share server ' '(admin only)'), ) parser.add_argument( '--project', metavar='', help=_('Filter shares by project (name or ID) (admin only)') ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--user', metavar='', help=_('Filter results by user (name or ID) (admin only)') ) identity_common.add_user_domain_option_to_parser(parser) parser.add_argument( '--all-projects', action='store_true', default=False, help=_('Include all projects (admin only)'), ) parser.add_argument( '--property', metavar='', action=parseractions.KeyValueAction, help=_('Filter shares having a given metadata key=value property ' '(repeat option to filter by multiple properties)'), ) parser.add_argument( '--extra-spec', metavar='', action=parseractions.KeyValueAction, help=_('Filter shares with extra specs (key=value) of the share ' 'type that they belong to. ' '(repeat option to filter by multiple extra specs)'), ) parser.add_argument( '--long', action='store_true', default=False, help=_('List additional fields in output'), ) parser.add_argument( '--sort', metavar="[:]", default='name:asc', help=_("Sort output by selected keys and directions(asc or desc) " "(default: name:asc), multiple keys and directions can be " "specified separated by comma"), ) parser.add_argument( '--limit', metavar="", type=int, action=parseractions.NonNegativeAction, help=_('Maximum number of shares to display'), ) parser.add_argument( '--marker', metavar="", help=_('The last share ID of the previous page'), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share identity_client = self.app.client_manager.identity # TODO(gouthamr): Add support for ~name, ~description # export_location filtering if parsed_args.long: columns = SHARE_ATTRIBUTES column_headers = SHARE_ATTRIBUTES_HEADERS else: columns = [ 'id', 'name', 'size', 'share_proto', 'status', 'is_public', 'share_type_name', 'host', 'availability_zone' ] column_headers = [ 'ID', 'Name', 'Size', 'Share Proto', 'Status', 'Is Public', 'Share Type Name', 'Host', 'Availability Zone' ] project_id = None if parsed_args.project: project_id = identity_common.find_project( identity_client, parsed_args.project, parsed_args.project_domain).id user_id = None if parsed_args.user: user_id = identity_common.find_user(identity_client, parsed_args.user, parsed_args.user_domain).id # set value of 'all_tenants' when using project option all_tenants = bool(parsed_args.project) or parsed_args.all_projects share_type_id = None if parsed_args.share_type: share_type_id = apiutils.find_resource(share_client.share_types, parsed_args.share_type).id snapshot_id = None if parsed_args.snapshot: snapshot_id = apiutils.find_resource(share_client.share_snapshots, parsed_args.snapshot).id share_network_id = None if parsed_args.share_network: share_network_id = apiutils.find_resource( share_client.share_networks, parsed_args.share_network).id share_group_id = None if parsed_args.share_group: share_group_id = apiutils.find_resource(share_client.share_groups, parsed_args.share_group).id share_server_id = None if parsed_args.share_server: share_server_id = apiutils.find_resource( share_client.share_servers, parsed_args.share_server).id search_opts = { 'all_tenants': all_tenants, 'is_public': parsed_args.public, 'metadata': utils.extract_key_value_options( parsed_args.property), 'extra_specs': utils.extract_key_value_options( parsed_args.extra_spec), 'limit': parsed_args.limit, 'name': parsed_args.name, 'status': parsed_args.status, 'host': parsed_args.host, 'share_server_id': share_server_id, 'share_network_id': share_network_id, 'share_type_id': share_type_id, 'snapshot_id': snapshot_id, 'share_group_id': share_group_id, 'project_id': project_id, 'user_id': user_id, 'offset': parsed_args.marker, } # NOTE(vkmc) We implemented sorting and filtering in manilaclient # but we will use the one provided by osc data = share_client.shares.list(search_opts=search_opts) data = oscutils.sort_items(data, parsed_args.sort, str) return (column_headers, (oscutils.get_item_properties (s, columns, formatters={'Metadata': oscutils.format_dict},) for s in data)) class ShowShare(command.ShowOne): """Show a share.""" _description = _("Display share details") def get_parser(self, prog_name): parser = super(ShowShare, self).get_parser(prog_name) parser.add_argument( 'share', metavar="", help=_('Share to display (name or ID)') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_obj = apiutils.find_resource(share_client.shares, parsed_args.share) export_locations = share_client.share_export_locations.list(share_obj) export_locations = ( cliutils.transform_export_locations_to_string_view( export_locations)) data = share_obj._info data['export_locations'] = export_locations # Special mapping for columns to make the output easier to read: # 'metadata' --> 'properties' data.update( { 'properties': format_columns.DictColumn(data.pop('metadata', {})), }, ) # Remove key links from being displayed data.pop("links", None) data.pop("shares_type", None) return self.dict2columns(data) class SetShare(command.Command): """Set share properties.""" _description = _("Set share properties") def get_parser(self, prog_name): parser = super(SetShare, self).get_parser(prog_name) parser.add_argument( 'share', metavar="", help=_('Share to modify (name or ID)') ) # 'metadata' --> 'properties' parser.add_argument( "--property", metavar="", default={}, action=parseractions.KeyValueAction, help=_("Set a property to this share " "(repeat option to set multiple properties)"), ) parser.add_argument( '--name', metavar="", default=None, help=_('New share name. (Default=None)') ) parser.add_argument( '--description', metavar='', default=None, help=_('New share description. (Default=None)') ) parser.add_argument( '--public', metavar='', help=_('Level of visibility for share. ' 'Defines whether other tenants are able to see it or not. ') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_obj = apiutils.find_resource(share_client.shares, parsed_args.share) result = 0 if parsed_args.property: try: share_client.shares.set_metadata( share_obj.id, parsed_args.property) except Exception as e: LOG.error(_("Failed to set share properties " "'%(properties)s': %(exception)s"), {'properties': parsed_args.property, 'exception': e}) result += 1 kwargs = {} if parsed_args.name is not None: kwargs['display_name'] = parsed_args.name if parsed_args.description is not None: kwargs['display_description'] = parsed_args.description if parsed_args.public is not None: kwargs['is_public'] = parsed_args.public if kwargs: try: share_client.shares.update(share_obj.id, **kwargs) except Exception as e: LOG.error(_("Failed to update share display name, visibility " "or display description: %s"), e) result += 1 if result > 0: raise exceptions.CommandError(_("One or more of the " "set operations failed")) class UnsetShare(command.Command): """Unset share properties.""" _description = _("Unset share properties") def get_parser(self, prog_name): parser = super(UnsetShare, self).get_parser(prog_name) parser.add_argument( 'share', metavar="", help=_('Share to modify (name or ID)') ) # 'metadata' --> 'properties' parser.add_argument( '--property', metavar='', action='append', help=_('Remove a property from share ' '(repeat option to remove multiple properties)'), ) parser.add_argument( '--name', action='store_true', help=_('Unset share name.') ) parser.add_argument( '--description', action='store_true', help=_('Unset share description.') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_obj = apiutils.find_resource(share_client.shares, parsed_args.share) result = 0 kwargs = {} if parsed_args.name: kwargs['display_name'] = None if parsed_args.description: kwargs['display_description'] = None if kwargs: try: share_client.shares.update(share_obj.id, **kwargs) except Exception as e: LOG.error(_("Failed to unset share display name " "or display description"), e) result += 1 if parsed_args.property: for key in parsed_args.property: try: share_client.shares.delete_metadata( share_obj.id, [key]) except Exception as e: LOG.error(_("Failed to unset share property " "'%(key)s': %(e)s"), {'key': key, 'e': e}) result += 1 if result > 0: raise exceptions.CommandError(_( "One or more of the " "unset operations failed")) python-manilaclient-2.1.0/manilaclient/osc/v2/share_access_rules.py0000664000175000017500000003250713644133413025506 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.common._i18n import _ from manilaclient.common.apiclient import utils as apiutils from manilaclient.osc import utils LOG = logging.getLogger(__name__) ACCESS_RULE_ATTRIBUTES = [ 'id', 'share_id', 'access_level', 'access_to', 'access_type', 'state', 'access_key', 'created_at', 'updated_at', 'properties' ] class ShareAccessAllow(command.ShowOne): """Create a new share access rule.""" _description = _("Create new share access rule") log = logging.getLogger(__name__ + ".CreateShareAccess") def get_parser(self, prog_name): parser = super(ShareAccessAllow, self).get_parser(prog_name) parser.add_argument( 'share', metavar="", help=_('Name or ID of the NAS share to modify.') ) parser.add_argument( 'access_type', metavar="", help=_('Access rule type (only "ip", "user" (user or group), ' '"cert" or "cephx" are supported).') ) parser.add_argument( 'access_to', metavar="", help=_('Value that defines access.') ) # metadata --> properties in osc parser.add_argument( '--properties', type=str, nargs='*', metavar='', help=_('Space separated list of key=value pairs of properties. ' 'OPTIONAL: Default=None. ' 'Available only for API microversion >= 2.45.'), ) parser.add_argument( '--access-level', metavar="", type=str, default=None, choices=['rw', 'ro'], help=_('Share access level ("rw" and "ro" access levels ' 'are supported). Defaults to rw.') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share = apiutils.find_resource(share_client.shares, parsed_args.share) properties = {} if parsed_args.properties: if share_client.api_version >= api_versions.APIVersion("2.45"): properties = utils.extract_properties(parsed_args.properties) else: raise exceptions.CommandError( "Adding properties to access rules is supported only " "with API microversion 2.45 and beyond") try: share_access_rule = share.allow( access_type=parsed_args.access_type, access=parsed_args.access_to, access_level=parsed_args.access_level, metadata=properties ) share_access_rule.update( { 'properties': utils.format_properties( share_access_rule.pop('metadata', {})) } ) return (ACCESS_RULE_ATTRIBUTES, oscutils.get_dict_properties( share_access_rule, ACCESS_RULE_ATTRIBUTES)) except Exception as e: raise exceptions.CommandError( "Failed to create access to share " "'%s': %s" % (share, e)) class ShareAccessDeny(command.Command): """Delete a share access rule.""" _description = _("Delete a share access rule") log = logging.getLogger(__name__ + ".DeleteShareAccess") def get_parser(self, prog_name): parser = super(ShareAccessDeny, self).get_parser(prog_name) parser.add_argument( 'share', metavar="", help=_('Name or ID of the NAS share to modify.') ) parser.add_argument( 'id', metavar="", help=_('ID of the access rule to be deleted.') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share = apiutils.find_resource(share_client.shares, parsed_args.share) try: share.deny(parsed_args.id) except Exception as e: raise exceptions.CommandError( "Failed to delete share access rule for share " "'%s': %s" % (share, e)) class ListShareAccess(command.Lister): """List share access rules.""" _description = _("List share access rule") log = logging.getLogger(__name__ + ".ListShareAccess") def get_parser(self, prog_name): parser = super(ListShareAccess, self).get_parser(prog_name) parser.add_argument( 'share', metavar="", help=_('Name or ID of the share.') ) parser.add_argument( '--columns', metavar="", default=None, help=_('Comma separated list of columns to be displayed. ' 'Example --columns "access_type,access_to".') ) # metadata --> properties in osc parser.add_argument( '--properties', type=str, nargs='*', metavar='', default=None, help=_('Filters results by properties (key=value). ' 'OPTIONAL: Default=None. ' 'Available only for API microversion >= 2.45'), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share = apiutils.find_resource(share_client.shares, parsed_args.share) if share_client.api_version >= api_versions.APIVersion("2.45"): search_opts = {} if parsed_args.properties: search_opts = { 'metadata': utils.extract_properties( parsed_args.properties) } access_list = share_client.share_access_rules.access_list( share, search_opts) elif parsed_args.properties: raise exceptions.CommandError( "Filtering access rules by properties is supported only " "with API microversion 2.45 and beyond.") else: access_list = share.access_list() list_of_keys = [ 'id', 'access_type', 'access_to', 'access_level', 'state' ] if share_client.api_version >= api_versions.APIVersion("2.21"): list_of_keys.append('access_key') if share_client.api_version >= api_versions.APIVersion("2.33"): list_of_keys.append('created_at') list_of_keys.append('updated_at') if parsed_args.columns: columns = parsed_args.columns.split(',') for column in columns: if column not in list_of_keys: msg = ("No column named '%s'. Possible columns are: " "'id', 'access_type', 'access_to', " "'access_level', 'state', " "'access_key'(API microversion 2.21 and beyond), " "'created_at'(API microversion 2.33 and beyond), " "'updated_at'(API microversion 2.33 and beyond)." % column) raise exceptions.CommandError(msg) else: columns = list_of_keys values = (oscutils.get_item_properties( a, columns) for a in access_list) return (columns, values) class ShowShareAccess(command.ShowOne): """Display a share access rule.""" _description = _( "Display a share access rule. " "Available for API microversion 2.45 and higher") log = logging.getLogger(__name__ + ".ShowShareAccess") def get_parser(self, prog_name): parser = super(ShowShareAccess, self).get_parser(prog_name) parser.add_argument( 'access_id', metavar="", help=_('ID of the NAS share access rule.') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share if share_client.api_version >= api_versions.APIVersion("2.45"): access_rule = share_client.share_access_rules.get( parsed_args.access_id ) access_rule._info.update( { 'properties': utils.format_properties( access_rule._info.pop('metadata', {})) } ) return (ACCESS_RULE_ATTRIBUTES, oscutils.get_dict_properties( access_rule._info, ACCESS_RULE_ATTRIBUTES)) else: raise exceptions.CommandError( "Displaying share access rule details is only available " "with API microversion 2.45 and higher.") class SetShareAccess(command.Command): """Set properties to share access rule.""" _description = _( "Set properties to share access rule. " "Available for API microversion 2.45 and higher") log = logging.getLogger(__name__ + ".SetShareAccess") def get_parser(self, prog_name): parser = super(SetShareAccess, self).get_parser(prog_name) parser.add_argument( 'access_id', metavar="", help=_('ID of the NAS share access rule.') ) # metadata --> properties in osc parser.add_argument( '--property', metavar='', default={}, action=parseractions.KeyValueAction, help=_('Set a property to this share access rule. ' '(Repeat option to set multiple properties) ' 'Available only for API microversion >= 2.45.'), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share if share_client.api_version >= api_versions.APIVersion("2.45"): access_rule = share_client.share_access_rules.get( parsed_args.access_id ) try: share_client.share_access_rules.set_metadata( access_rule, parsed_args.property) except Exception as e: raise exceptions.CommandError( "Failed to set properties to share access rule with ID " "'%s': %s" % (access_rule.id, e)) else: raise exceptions.CommandError( "Setting properties to access rule is supported only " "with API microversion 2.45 and higher") class UnsetShareAccess(command.Command): """Unset properties of share access rule.""" _description = _( "Unset properties of share access rule. " "Available for API microversion 2.45 and higher") log = logging.getLogger(__name__ + ".UnsetShareAccess") def get_parser(self, prog_name): parser = super(UnsetShareAccess, self).get_parser(prog_name) parser.add_argument( 'access_id', metavar="", help=_('ID of the NAS share access rule.') ) # metadata --> properties in osc parser.add_argument( '--property', metavar='', action='append', help=_('Remove property from share access rule. ' '(Repeat option to remove multiple properties) ' 'Available only for API microversion >= 2.45.'), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share if share_client.api_version >= api_versions.APIVersion("2.45"): access_rule = share_client.share_access_rules.get( parsed_args.access_id ) if parsed_args.property: try: share_client.share_access_rules.unset_metadata( access_rule, parsed_args.property) except Exception as e: raise exceptions.CommandError( "Failed to unset properties for share access rule " "with ID '%s': %s" % (access_rule.id, e)) else: raise exceptions.CommandError( "Please specify '--property ' to unset a property. ") else: raise exceptions.CommandError( "Option to unset properties of access rule is available only " "for API microversion 2.45 and higher") python-manilaclient-2.1.0/manilaclient/osc/v2/share_type_access.py0000664000175000017500000000767213644133413025342 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient.common._i18n import _ from manilaclient.common.apiclient import utils as apiutils LOG = logging.getLogger(__name__) class ShareTypeAccessAllow(command.Command): """Add access for share type.""" _description = _("Add access for share type") def get_parser(self, prog_name): parser = super(ShareTypeAccessAllow, self).get_parser(prog_name) parser.add_argument( 'share_type', metavar="", help=_("Share type name or ID to add access to") ) parser.add_argument( 'project_id', metavar="", help=_("Project ID to add share type access for") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_type = apiutils.find_resource( share_client.share_types, parsed_args.share_type) try: share_client.share_type_access.add_project_access( share_type, parsed_args.project_id) except Exception as e: raise exceptions.CommandError( "Failed to add access to share type : %s" % e) class ListShareTypeAccess(command.Lister): """Get access list for share type.""" _description = _("Get access list for share type") def get_parser(self, prog_name): parser = super(ListShareTypeAccess, self).get_parser(prog_name) parser.add_argument( 'share_type', metavar="", help=_("Share type name or ID to get access list for") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_type = apiutils.find_resource( share_client.share_types, parsed_args.share_type) if share_type._info.get('share_type_access:is_public'): raise exceptions.CommandError( 'Forbidden to get access list for public share type.') data = share_client.share_type_access.list(share_type) columns = ['Project_ID'] values = (oscutils.get_item_properties(s, columns) for s in data) return (columns, values) class ShareTypeAccessDeny(command.Command): """Delete access from share type.""" _description = _("Delete access from share type") def get_parser(self, prog_name): parser = super(ShareTypeAccessDeny, self).get_parser(prog_name) parser.add_argument( 'share_type', metavar="", help=_("Share type name or ID to delete access from") ) parser.add_argument( 'project_id', metavar="", help=_("Project ID to delete share type access for") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_type = apiutils.find_resource( share_client.share_types, parsed_args.share_type) try: share_client.share_type_access.remove_project_access( share_type, parsed_args.project_id) except Exception as e: raise exceptions.CommandError( "Failed to remove access from share type : %s" % e) python-manilaclient-2.1.0/manilaclient/osc/v2/__init__.py0000664000175000017500000000000013644133413023367 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/osc/utils.py0000664000175000017500000000517313644133413022461 0ustar zuulzuul00000000000000# Copyright 2019 Red Hat, Inc. # # 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 six from oslo_utils import strutils from manilaclient.common import constants from manilaclient import exceptions def extract_key_value_options(pairs): result_dict = {} duplicate_options = [] pairs = pairs or {} for attr, value in pairs.items(): if attr not in result_dict: result_dict[attr] = value else: duplicate_options.append(attr) if pairs and len(duplicate_options) > 0: duplicate_str = ', '.join(duplicate_options) msg = "Following options were duplicated: %s" % duplicate_str raise exceptions.CommandError(msg) return result_dict def format_properties(properties): formatted_data = [] for item in properties: formatted_data.append("%s : %s" % (item, properties[item])) return "\n".join(formatted_data) def extract_properties(properties): result_dict = {} for item in properties: try: (key, value) = item.split('=', 1) if key in result_dict: raise exceptions.CommandError( "Argument '%s' is specified twice." % key) else: result_dict[key] = value except ValueError: raise exceptions.CommandError( "Parsing error, expected format 'key=value' for " + item ) return result_dict def extract_extra_specs(extra_specs, specs_to_add): for item in specs_to_add: (key, value) = item.split('=', 1) if key in extra_specs: msg = ("Argument '%s' value specified twice." % key) raise exceptions.CommandError(msg) elif key in constants.BOOL_SPECS: if strutils.is_valid_boolstr(value): extra_specs[key] = value.capitalize() else: msg = ("Argument '%s' is of boolean " "type and has invalid value: %s" % (key, six.text_type(value))) raise exceptions.CommandError(msg) else: extra_specs[key] = value return extra_specs python-manilaclient-2.1.0/manilaclient/osc/plugin.py0000664000175000017500000000755213644133413022622 0ustar zuulzuul00000000000000# Copyright 2019 Red Hat, Inc. # # 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. """OpenStackClient plugin for the Shared File System Service.""" import logging from osc_lib import utils from manilaclient import api_versions from manilaclient.common import constants from manilaclient import exceptions LOG = logging.getLogger(__name__) API_NAME = 'share' API_VERSION_OPTION = 'os_share_api_version' CLIENT_CLASS = 'manilaclient.v2.client.Client' LATEST_VERSION = api_versions.MAX_VERSION LATEST_MINOR_VERSION = api_versions.MAX_VERSION.split('.')[-1] API_VERSIONS = { '2.%d' % i: CLIENT_CLASS for i in range(0, int(LATEST_MINOR_VERSION) + 1) } def _get_manila_url_from_service_catalog(instance): service_type = constants.SFS_SERVICE_TYPE url = instance.get_endpoint_for_service_type( constants.SFS_SERVICE_TYPE, region_name=instance._region_name, interface=instance.interface) # Fallback if cloud is using an older service type name if not url: url = instance.get_endpoint_for_service_type( constants.V2_SERVICE_TYPE, region_name=instance._region_name, interface=instance.interface) service_type = constants.V2_SERVICE_TYPE if url is None: raise exceptions.EndpointNotFound( message="Could not find manila / shared-file-system endpoint in " "the service catalog.") return service_type, url def make_client(instance): """Returns a shared file system service client.""" requested_api_version = instance._api_version[API_NAME] shared_file_system_client = utils.get_client_class( API_NAME, requested_api_version, API_VERSIONS) # Cast the API version into an object for further processing requested_api_version = api_versions.APIVersion( version_str=requested_api_version) LOG.debug('Instantiating Shared File System (share) client: %s', shared_file_system_client) LOG.debug('Shared File System API version: %s', requested_api_version) service_type, manila_endpoint_url = _get_manila_url_from_service_catalog( instance) instance.setup_auth() debugging_enabled = instance._cli_options.debug client = shared_file_system_client(session=instance.session, service_catalog_url=manila_endpoint_url, endpoint_type=instance.interface, region_name=instance.region_name, service_type=service_type, auth=instance.auth, http_log_debug=debugging_enabled, api_version=requested_api_version) return client def build_option_parser(parser): """Hook to add global options.""" default_api_version = utils.env('OS_SHARE_API_VERSION') or LATEST_VERSION parser.add_argument( '--os-share-api-version', metavar='', default=default_api_version, choices=sorted( API_VERSIONS, key=lambda k: [int(x) for x in k.split('.')]), help='Shared File System API version, default=' + default_api_version + 'version supported by both the client and the server). ' '(Env: OS_SHARE_API_VERSION)', ) return parser python-manilaclient-2.1.0/manilaclient/osc/__init__.py0000664000175000017500000000000013644133413023040 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/api_versions.py0000664000175000017500000003552213644133413023237 0ustar zuulzuul00000000000000# Copyright 2015 Chuck Fouts # 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 functools import logging import re import warnings import manilaclient from manilaclient.common._i18n import _ from manilaclient.common import cliutils from manilaclient.common import constants from manilaclient import exceptions from manilaclient import utils LOG = logging.getLogger(__name__) MAX_VERSION = '2.55' MIN_VERSION = '2.0' DEPRECATED_VERSION = '1.0' _VERSIONED_METHOD_MAP = {} class APIVersion(object): """Top level object to support Manila API Versioning. This class represents an API Version with convenience methods for manipulation and comparison of version numbers that we need to do to implement microversions. """ TYPE_ERROR_MSG = _("'%(other)s' should be an instance of '%(cls)s'") def __init__(self, version_str=None): """Create an API version object.""" self.ver_major = 0 self.ver_minor = 0 if version_str is not None: match = re.match(r"^([1-9]\d*)\.([1-9]\d*|0)$", version_str) if match: self.ver_major = int(match.group(1)) self.ver_minor = int(match.group(2)) else: msg = _("Invalid format of client version '%s'. " "Expected format 'X.Y', where X is a major part and Y " "is a minor part of version.") % version_str raise exceptions.UnsupportedVersion(msg) def __str__(self): """Debug/Logging representation of object.""" return ("API Version Major: %s, Minor: %s" % (self.ver_major, self.ver_minor)) def __repr__(self): if self.is_null(): return "" else: return "" % self.get_string() def __lt__(self, other): if not isinstance(other, APIVersion): raise TypeError(self.TYPE_ERROR_MSG % {"other": other, "cls": self.__class__}) return ((self.ver_major, self.ver_minor) < (other.ver_major, other.ver_minor)) def __eq__(self, other): if not isinstance(other, APIVersion): raise TypeError(self.TYPE_ERROR_MSG % {"other": other, "cls": self.__class__}) return ((self.ver_major, self.ver_minor) == (other.ver_major, other.ver_minor)) def __gt__(self, other): if not isinstance(other, APIVersion): raise TypeError(self.TYPE_ERROR_MSG % {"other": other, "cls": self.__class__}) return ((self.ver_major, self.ver_minor) > (other.ver_major, other.ver_minor)) def __le__(self, other): return self < other or self == other def __ne__(self, other): return not self.__eq__(other) def __ge__(self, other): return self > other or self == other def is_null(self): return self.ver_major == 0 and self.ver_minor == 0 def is_latest(self): return self == manilaclient.API_MAX_VERSION def matches(self, min_version, max_version): """Determines if version is within a range. Returns whether the version object represents a version greater than or equal to the minimum version and less than or equal to the maximum version. :param min_version: Minimum acceptable version. :param max_version: Maximum acceptable version. :returns: boolean If min_version is null then there is no minimum limit. If max_version is null then there is no maximum limit. If self is null then raise ValueError """ if self.is_null(): raise ValueError(_("Null APIVersion doesn't support 'matches'.")) if max_version.is_null() and min_version.is_null(): return True elif max_version.is_null(): return min_version <= self elif min_version.is_null(): return self <= max_version else: return min_version <= self <= max_version def get_string(self): """String representation of an APIVersion object.""" if self.is_null(): raise ValueError( _("Null APIVersion cannot be converted to string.")) return "%s.%s" % (self.ver_major, self.ver_minor) def get_major_version(self): return "%s" % self.ver_major class VersionedMethod(object): def __init__(self, name, start_version, end_version, func): """Versioning information for a single method :param name: Name of the method :param start_version: Minimum acceptable version :param end_version: Maximum acceptable_version :param func: Method to call Minimum and maximums are inclusive """ self.name = name self.start_version = start_version self.end_version = end_version self.func = func def __str__(self): return ("Version Method %s: min: %s, max: %s" % (self.name, self.start_version, self.end_version)) def __repr__(self): return "" % self.name def check_version_supported(api_version): """Returns True if the API version is supported. :warn Sends warning if version is not supported. """ if (check_version_matches_min_max(api_version) or check_version_deprecated(api_version)): return True return False def check_version_matches_min_max(api_version): """Returns True if the API version is within the supported range.""" if (not api_version.matches( manilaclient.API_MIN_VERSION, manilaclient.API_MAX_VERSION)): msg = _("Invalid client version '%(version)s'. " "Current version range is '%(min)s' through " " '%(max)s'") % { "version": api_version.get_string(), "min": manilaclient.API_MIN_VERSION.get_string(), "max": manilaclient.API_MAX_VERSION.get_string()} warnings.warn(msg) return False return True def check_version_deprecated(api_version): """Returns True if API version is deprecated.""" if api_version == manilaclient.API_DEPRECATED_VERSION: msg = _("Client version '%(version)s' is deprecated.") % { "version": api_version.get_string()} warnings.warn(msg) return True return False def get_api_version(version_string): """Returns checked APIVersion object.""" version_string = str(version_string) api_version = APIVersion(version_string) check_version_supported(api_version) return api_version def _get_server_version_range(client): """Obtain version range from server.""" response = client.services.server_api_version('') server_version = None for resource in response: if hasattr(resource, 'version'): if resource.status == "CURRENT": server_version = resource break if not hasattr(server_version, 'version') or not server_version.version: return APIVersion(), APIVersion() min_version = APIVersion(server_version.min_version) max_version = APIVersion(server_version.version) return min_version, max_version def discover_version(client, requested_version): """Discovers the most recent version for client and API. Checks 'requested_version' and returns the most recent version supported by both the API and the client. If there is not a supported version then an UnsupportedVersion exception is thrown. :param client: client object :param requested_version: requested version represented by APIVersion obj :returns: APIVersion """ server_start_version, server_end_version = _get_server_version_range( client) valid_version = requested_version if server_start_version.is_null() and server_end_version.is_null(): msg = ("Server does not support microversions. Changing server " "version to %(min_version)s.") LOG.debug(msg, {"min_version": DEPRECATED_VERSION}) valid_version = APIVersion(DEPRECATED_VERSION) else: valid_version = _validate_requested_version( requested_version, server_start_version, server_end_version) _validate_server_version(server_start_version, server_end_version) return valid_version def _validate_requested_version(requested_version, server_start_version, server_end_version): """Validates the requested version. Checks 'requested_version' is within the min/max range supported by the server. If 'requested_version' is not within range then attempts to downgrade to 'server_end_version'. Otherwise an UnsupportedVersion exception is thrown. :param requested_version: requestedversion represented by APIVersion obj :param server_start_version: APIVersion object representing server min :param server_end_version: APIVersion object representing server max """ valid_version = requested_version if not requested_version.matches(server_start_version, server_end_version): if server_end_version <= requested_version: if (manilaclient.API_MIN_VERSION <= server_end_version and server_end_version <= manilaclient.API_MAX_VERSION): msg = _("Requested version %(requested_version)s is " "not supported. Downgrading requested version " "to %(server_end_version)s.") LOG.debug(msg, { "requested_version": requested_version, "server_end_version": server_end_version}) valid_version = server_end_version else: raise exceptions.UnsupportedVersion( _("The specified version isn't supported by server. The valid " "version range is '%(min)s' to '%(max)s'") % { "min": server_start_version.get_string(), "max": server_end_version.get_string()}) return valid_version def _validate_server_version(server_start_version, server_end_version): """Validates the server version. Checks that the 'server_end_version' is greater than the minimum version supported by the client. Then checks that the 'server_start_version' is less than the maximum version supported by the client. :param server_start_version: :param server_end_version: :return: """ if manilaclient.API_MIN_VERSION > server_end_version: raise exceptions.UnsupportedVersion( _("Server's version is too old. The client's valid version range " "is '%(client_min)s' to '%(client_max)s'. The server valid " "version range is '%(server_min)s' to '%(server_max)s'.") % { 'client_min': manilaclient.API_MIN_VERSION.get_string(), 'client_max': manilaclient.API_MAX_VERSION.get_string(), 'server_min': server_start_version.get_string(), 'server_max': server_end_version.get_string()}) elif manilaclient.API_MAX_VERSION < server_start_version: raise exceptions.UnsupportedVersion( _("Server's version is too new. The client's valid version range " "is '%(client_min)s' to '%(client_max)s'. The server valid " "version range is '%(server_min)s' to '%(server_max)s'.") % { 'client_min': manilaclient.API_MIN_VERSION.get_string(), 'client_max': manilaclient.API_MAX_VERSION.get_string(), 'server_min': server_start_version.get_string(), 'server_max': server_end_version.get_string()}) def add_versioned_method(versioned_method): _VERSIONED_METHOD_MAP.setdefault(versioned_method.name, []) _VERSIONED_METHOD_MAP[versioned_method.name].append(versioned_method) def get_versioned_methods(func_name, api_version=None): versioned_methods = _VERSIONED_METHOD_MAP.get(func_name, []) if api_version and not api_version.is_null(): return [m for m in versioned_methods if api_version.matches(m.start_version, m.end_version)] return versioned_methods def experimental_api(f): """Adds to HTTP Header to indicate this is an experimental API call.""" @functools.wraps(f) def _wrapper(*args, **kwargs): client = args[0] if (isinstance(client, manilaclient.v2.client.Client) or hasattr(client, 'client')): dh = client.client.default_headers dh[constants.EXPERIMENTAL_HTTP_HEADER] = 'true' return f(*args, **kwargs) return _wrapper def wraps(start_version, end_version=MAX_VERSION): """Annotation used to return the correct method based on requested version. Creates a VersionedMethod based on data from the method using the 'wraps' annotation. The VersionedMethod is stored in the _VERSIONED_METHOD_MAP. Also, adds a 'substitution' method that is used to look up the latest method matching the start_version. :param start_version: String obj representing first supported version. :param end_version: String obj representing last supported version. """ start_version = APIVersion(start_version) end_version = APIVersion(end_version) def decor(func): func.versioned = True name = utils.get_function_name(func) versioned_method = VersionedMethod(name, start_version, end_version, func) add_versioned_method(versioned_method) @functools.wraps(func) def substitution(obj, *args, **kwargs): methods = get_versioned_methods(name, obj.api_version) if not methods: raise exceptions.UnsupportedVersion( _("API version '%(version)s' is not supported on " "'%(method)s' method.") % { "version": obj.api_version.get_string(), "method": name, }) method = max(methods, key=lambda f: f.start_version) return method.func(obj, *args, **kwargs) if hasattr(func, 'arguments'): for cli_args, cli_kwargs in func.arguments: cliutils.add_arg(substitution, *cli_args, **cli_kwargs) return substitution return decor python-manilaclient-2.1.0/manilaclient/v2/0000775000175000017500000000000013644133466020514 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/v2/share_types.py0000664000175000017500000002421613644133413023411 0ustar zuulzuul00000000000000# Copyright (c) 2011 Rackspace US, Inc. # # 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. """ Share Type interface. """ from manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base from manilaclient import exceptions class ShareType(common_base.Resource): """A Share Type is the type of share to be created.""" def __init__(self, manager, info, loaded=False): super(ShareType, self).__init__(manager, info, loaded) self._required_extra_specs = info.get('required_extra_specs', {}) self._optional_extra_specs = info.get('extra_specs', {}).copy() for key in self._required_extra_specs.keys(): self._optional_extra_specs.pop(key, None) def __repr__(self): return "" % self.name @property def is_public(self): """Provide a user-friendly accessor to [os-]share-type-access.""" return self._info.get( "share_type_access:is_public", self._info.get("os-share-type-access:is_public", "N/A")) def get_keys(self, prefer_resource_data=True): """Get extra specs from a share type. :param prefer_resource_data: By default extra_specs are retrieved from resource data, but user can force this method to make API call. :return: dict with extra specs """ extra_specs = getattr(self, 'extra_specs', None) if prefer_resource_data and extra_specs: return extra_specs _resp, body = self.manager.api.client.get( "/types/%s/extra_specs" % common_base.getid(self)) self.extra_specs = body["extra_specs"] return body["extra_specs"] def get_required_keys(self): return self._required_extra_specs def get_optional_keys(self): return self._optional_extra_specs def set_keys(self, metadata): """Set extra specs on a share type. :param type : The :class:`ShareType` to set extra spec on :param metadata: A dict of key/value pairs to be set """ body = {'extra_specs': metadata} return self.manager._create( "/types/%s/extra_specs" % common_base.getid(self), body, "extra_specs", return_raw=True, ) def unset_keys(self, keys): """Unset extra specs on a share type. :param type_id: The :class:`ShareType` to unset extra spec on :param keys: A list of keys to be unset """ # NOTE(jdg): This wasn't actually doing all of the keys before # the return in the loop resulted in ony ONE key being unset. # since on success the return was NONE, we'll only interrupt the loop # and return if there's an error resp = None for k in keys: resp = self.manager._delete( "/types/%s/extra_specs/%s" % (common_base.getid(self), k)) if resp is not None: return resp def update(self, **kwargs): """Update this share type.""" return self.manager.update(self, **kwargs) class ShareTypeManager(base.ManagerWithFind): """Manage :class:`ShareType` resources.""" resource_class = ShareType def list(self, search_opts=None, show_all=True): """Get a list of all share types. :rtype: list of :class:`ShareType`. """ search_opts = search_opts or {} if show_all: search_opts['is_public'] = 'all' query_string = self._build_query_string(search_opts) return self._list("/types%s" % query_string, "share_types") def show(self, share_type): """Get a share. :param share: either share object or text with its ID. :rtype: :class:`Share` """ type_id = common_base.getid(share_type) return self._get("/types/%s" % type_id, "share_type") def get(self, share_type="default"): """Get a specific share type. :param share_type: The ID of the :class:`ShareType` to get. :rtype: :class:`ShareType` """ return self._get("/types/%s" % common_base.getid(share_type), "share_type") def delete(self, share_type): """Delete a specific share_type. :param share_type: The name or ID of the :class:`ShareType` to get. """ self._delete("/types/%s" % common_base.getid(share_type)) def _do_create(self, name, extra_specs, is_public, is_public_keyname="share_type_access:is_public", description=None): """Create a share type. :param name: Descriptive name of the share type :rtype: :class:`ShareType` """ body = { "share_type": { "name": name, is_public_keyname: is_public, "extra_specs": extra_specs, } } if description: body["share_type"]["description"] = description return self._create("/types", body, "share_type") def _do_update(self, share_type, name=None, is_public=None, is_public_keyname="share_type_access:is_public", description=None): """Update the name and/or description for a share type. :param share_type: the ID of the :class: `ShareType` to update. :param name: Descriptive name of the share type. :param description: Description of the share type. :rtype: :class:`ShareType` """ body = { "share_type": {} } if name: body["share_type"]["name"] = name if is_public is not None: body["share_type"][is_public_keyname] = is_public if description or description == "": body["share_type"]["description"] = description return self._update("/types/%s" % common_base.getid(share_type), body, "share_type") @api_versions.wraps("1.0", "2.6") def create(self, name, spec_driver_handles_share_servers, spec_snapshot_support=None, is_public=True, extra_specs=None): if extra_specs is None: extra_specs = {} self._handle_spec_driver_handles_share_servers( extra_specs, spec_driver_handles_share_servers) self._handle_spec_snapshot_support( extra_specs, spec_snapshot_support, set_default=True) return self._do_create( name, extra_specs, is_public, is_public_keyname="os-share-type-access:is_public") @api_versions.wraps("2.7", "2.23") # noqa def create(self, name, spec_driver_handles_share_servers, spec_snapshot_support=None, is_public=True, extra_specs=None): if extra_specs is None: extra_specs = {} self._handle_spec_driver_handles_share_servers( extra_specs, spec_driver_handles_share_servers) self._handle_spec_snapshot_support( extra_specs, spec_snapshot_support, set_default=True) return self._do_create(name, extra_specs, is_public) @api_versions.wraps("2.24", "2.40") # noqa def create(self, name, spec_driver_handles_share_servers, spec_snapshot_support=None, is_public=True, extra_specs=None): if extra_specs is None: extra_specs = {} self._handle_spec_driver_handles_share_servers( extra_specs, spec_driver_handles_share_servers) self._handle_spec_snapshot_support(extra_specs, spec_snapshot_support) return self._do_create(name, extra_specs, is_public) @api_versions.wraps("2.41") # noqa def create(self, name, spec_driver_handles_share_servers, spec_snapshot_support=None, is_public=True, extra_specs=None, description=None): if extra_specs is None: extra_specs = {} self._handle_spec_driver_handles_share_servers( extra_specs, spec_driver_handles_share_servers) self._handle_spec_snapshot_support(extra_specs, spec_snapshot_support) return self._do_create(name, extra_specs, is_public, description=description) @api_versions.wraps("2.50") def update(self, share_type, name=None, is_public=None, description=None): return self._do_update(share_type, name, is_public, description=description) def _handle_spec_driver_handles_share_servers( self, extra_specs, spec_driver_handles_share_servers): """Validation and default for DHSS extra spec.""" if spec_driver_handles_share_servers is not None: if 'driver_handles_share_servers' in extra_specs: msg = ("'driver_handles_share_servers' is already set via " "positional argument.") raise exceptions.CommandError(msg) else: extra_specs['driver_handles_share_servers'] = ( spec_driver_handles_share_servers) else: msg = ("'driver_handles_share_servers' is not set via " "positional argument.") raise exceptions.CommandError(msg) def _handle_spec_snapshot_support(self, extra_specs, spec_snapshot_support, set_default=False): """Validation and default for snapshot extra spec.""" if spec_snapshot_support is not None: if 'snapshot_support' in extra_specs: msg = "'snapshot_support' extra spec is provided twice." raise exceptions.CommandError(msg) else: extra_specs['snapshot_support'] = spec_snapshot_support elif 'snapshot_support' not in extra_specs and set_default: extra_specs['snapshot_support'] = True python-manilaclient-2.1.0/manilaclient/v2/shares.py0000664000175000017500000007134513644133413022355 0ustar zuulzuul00000000000000# Copyright 2012 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. """Interface for shares extension.""" import collections import ipaddress from oslo_utils import uuidutils import re import six import string from manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base from manilaclient.common import constants from manilaclient import exceptions from manilaclient.v2 import share_instances class Share(common_base.Resource): """A share is an extra block level storage to the OpenStack instances.""" def __repr__(self): return "" % self.id def update(self, **kwargs): """Update this share.""" self.manager.update(self, **kwargs) def unmanage(self, **kwargs): """Unmanage this share.""" self.manager.unmanage(self, **kwargs) def migration_start(self, host, force_host_assisted_migration, preserve_metadata, writable, nondisruptive, preserve_snapshots, new_share_network_id=None, new_share_type_id=None): """Migrate the share to a new host.""" self.manager.migration_start(self, host, force_host_assisted_migration, preserve_metadata, writable, nondisruptive, preserve_snapshots, new_share_network_id, new_share_type_id) def migration_complete(self): """Complete migration of a share.""" self.manager.migration_complete(self) def migration_cancel(self): """Attempts to cancel migration of a share.""" self.manager.migration_cancel(self) def migration_get_progress(self): """Obtain progress of migration of a share.""" return self.manager.migration_get_progress(self) def reset_task_state(self, task_state): """Reset the task state of a given share.""" self.manager.reset_task_state(self, task_state) def delete(self, share_group_id=None): """Delete this share.""" self.manager.delete(self, share_group_id=share_group_id) def force_delete(self): """Delete the specified share ignoring its current state.""" self.manager.force_delete(self) def allow(self, access_type, access, access_level, metadata=None): """Allow access to a share.""" return self.manager.allow( self, access_type, access, access_level, metadata) def deny(self, id): """Deny access from IP to a share.""" return self.manager.deny(self, id) def access_list(self): """Get access list from a share.""" return self.manager.access_list(self) def update_all_metadata(self, metadata): """Update all metadata of this share.""" return self.manager.update_all_metadata(self, metadata) def reset_state(self, state): """Update the share with the provided state.""" self.manager.reset_state(self, state) def extend(self, new_size): """Extend the size of the specified share.""" self.manager.extend(self, new_size) def shrink(self, new_size): """Shrink the size of the specified share.""" self.manager.shrink(self, new_size) def list_instances(self): """List instances of the specified share.""" self.manager.list_instances(self) def revert_to_snapshot(self, snapshot): """Reverts a share (in place) to a snapshot.""" self.manager.revert_to_snapshot(self, snapshot) class ShareManager(base.ManagerWithFind): """Manage :class:`Share` resources.""" resource_class = Share def create(self, share_proto, size, snapshot_id=None, name=None, description=None, metadata=None, share_network=None, share_type=None, is_public=False, availability_zone=None, share_group_id=None): """Create a share. :param share_proto: text - share protocol for new share available values are NFS, CIFS, CephFS, GlusterFS, HDFS and MAPRFS. :param size: int - size in GiB :param snapshot_id: text - ID of the snapshot :param name: text - name of new share :param description: text - description of a share :param metadata: dict - optional metadata to set on share creation :param share_network: either instance of ShareNetwork or text with ID :param share_type: either instance of ShareType or text with ID :param is_public: bool, whether to set share as public or not. :param share_group_id: text - ID of the share group to which the share should belong :rtype: :class:`Share` """ share_metadata = metadata if metadata is not None else dict() body = { 'size': size, 'snapshot_id': snapshot_id, 'name': name, 'description': description, 'metadata': share_metadata, 'share_proto': share_proto, 'share_network_id': common_base.getid(share_network), 'share_type': common_base.getid(share_type), 'is_public': is_public, 'availability_zone': availability_zone, } if share_group_id: body['share_group_id'] = share_group_id return self._create('/shares', {'share': body}, 'share') @api_versions.wraps("2.29") @api_versions.experimental_api def migration_start(self, share, host, force_host_assisted_migration, preserve_metadata, writable, nondisruptive, preserve_snapshots, new_share_network_id=None, new_share_type_id=None): return self._action( "migration_start", share, { "host": host, "force_host_assisted_migration": force_host_assisted_migration, "preserve_metadata": preserve_metadata, "preserve_snapshots": preserve_snapshots, "writable": writable, "nondisruptive": nondisruptive, "new_share_network_id": new_share_network_id, "new_share_type_id": new_share_type_id, }) @api_versions.wraps("2.22") @api_versions.experimental_api def reset_task_state(self, share, task_state): """Update the provided share with the provided task state. :param share: either share object or text with its ID. :param task_state: text with new task state to set for share. """ return self._action('reset_task_state', share, {"task_state": task_state}) @api_versions.wraps("2.22") @api_versions.experimental_api def migration_complete(self, share): """Completes migration for a given share. :param share: The :class:'share' to complete migration """ return self._action('migration_complete', share) @api_versions.wraps("2.22") @api_versions.experimental_api def migration_cancel(self, share): """Attempts to cancel migration for a given share. :param share: The :class:'share' to cancel migration """ return self._action('migration_cancel', share) @api_versions.wraps("2.22") @api_versions.experimental_api def migration_get_progress(self, share): """Obtains progress of share migration for a given share. :param share: The :class:'share' to obtain migration progress """ return self._action('migration_get_progress', share) def _do_manage(self, service_host, protocol, export_path, driver_options=None, share_type=None, name=None, description=None, is_public=None, share_server_id=None, resource_path="/shares/manage"): """Manage some existing share. :param service_host: text - host where manila share service is running :param protocol: text - share protocol that is used :param export_path: text - export path of share :param driver_options: dict - custom set of key-values :param share_type: text - share type that should be used for share :param name: text - name of new share :param description: - description for new share :param is_public: - visibility for new share :param share_server_id: text - id of share server associated with share """ driver_options = driver_options if driver_options else dict() body = { 'service_host': service_host, 'share_type': share_type, 'protocol': protocol, 'export_path': export_path, 'driver_options': driver_options, 'name': name, 'description': description, 'share_server_id': share_server_id, } if is_public is not None: body['is_public'] = is_public return self._create(resource_path, {'share': body}, 'share') @api_versions.wraps("1.0", "2.6") def manage(self, service_host, protocol, export_path, driver_options=None, share_type=None, name=None, description=None): return self._do_manage( service_host, protocol, export_path, driver_options=driver_options, share_type=share_type, name=name, description=description, resource_path="/os-share-manage") @api_versions.wraps("2.7", "2.7") # noqa def manage(self, service_host, protocol, export_path, driver_options=None, share_type=None, name=None, description=None): return self._do_manage( service_host, protocol, export_path, driver_options=driver_options, share_type=share_type, name=name, description=description, resource_path="/shares/manage") @api_versions.wraps("2.8", "2.48") # noqa def manage(self, service_host, protocol, export_path, driver_options=None, share_type=None, name=None, description=None, is_public=False): return self._do_manage( service_host, protocol, export_path, driver_options=driver_options, share_type=share_type, name=name, description=description, is_public=is_public, resource_path="/shares/manage") @api_versions.wraps("2.49") # noqa def manage(self, service_host, protocol, export_path, driver_options=None, share_type=None, name=None, description=None, is_public=False, share_server_id=None): return self._do_manage( service_host, protocol, export_path, driver_options=driver_options, share_type=share_type, name=name, description=description, is_public=is_public, share_server_id=share_server_id, resource_path="/shares/manage") @api_versions.wraps("1.0", "2.6") def unmanage(self, share): """Unmanage a share. :param share: either share object or text with its ID. """ return self.api.client.post( "/os-share-unmanage/%s/unmanage" % common_base.getid(share)) @api_versions.wraps("2.7") # noqa def unmanage(self, share): """Unmanage a share. :param share: either share object or text with its ID. """ return self._action("unmanage", share) @api_versions.wraps("2.27") def revert_to_snapshot(self, share, snapshot): """Reverts a share (in place) to a snapshot. The snapshot must be the most recent one known to manila. :param share: either share object or text with its ID. :param snapshot: either snapshot object or text with its ID. """ snapshot_id = common_base.getid(snapshot) info = {'snapshot_id': snapshot_id} return self._action('revert', share, info=info) def get(self, share): """Get a share. :param share: either share object or text with its ID. :rtype: :class:`Share` """ share_id = common_base.getid(share) return self._get("/shares/%s" % share_id, "share") def update(self, share, **kwargs): """Updates a share. :param share: either share object or text with its ID. :rtype: :class:`Share` """ if not kwargs: return body = {'share': kwargs, } share_id = common_base.getid(share) return self._update("/shares/%s" % share_id, body) @api_versions.wraps("1.0", "2.34") def list(self, detailed=True, search_opts=None, sort_key=None, sort_dir=None): """Get a list of all shares.""" search_opts = search_opts or {} search_opts.pop("export_location", None) return self.do_list(detailed=detailed, search_opts=search_opts, sort_key=sort_key, sort_dir=sort_dir) @api_versions.wraps("2.35") # noqa def list(self, detailed=True, search_opts=None, sort_key=None, sort_dir=None): """Get a list of all shares.""" return self.do_list(detailed=detailed, search_opts=search_opts, sort_key=sort_key, sort_dir=sort_dir) def do_list(self, detailed=True, search_opts=None, sort_key=None, sort_dir=None): """Get a list of all shares. :param detailed: Whether to return detailed share info or not. :param search_opts: dict with search options to filter out shares. available keys are below (('name1', 'name2', ...), 'type'): - ('all_tenants', int) - ('is_public', bool) - ('metadata', dict) - ('extra_specs', dict) - ('limit', int) - ('offset', int) - ('name', text) - ('status', text) - ('host', text) - ('share_server_id', text) - (('share_network_id', 'share_network'), text) - (('share_type_id', 'share_type'), text) - (('snapshot_id', 'snapshot'), text) Note, that member context will have restricted set of available search opts. For admin context filtering also available by each share attr from its Model. So, this list is not full for admin context. :param sort_key: Key to be sorted (i.e. 'created_at' or 'status'). :param sort_dir: Sort direction, should be 'desc' or 'asc'. :rtype: list of :class:`Share` """ if search_opts is None: search_opts = {} if sort_key is not None: if sort_key in constants.SHARE_SORT_KEY_VALUES: search_opts['sort_key'] = sort_key # NOTE(vponomaryov): Replace aliases with appropriate keys if sort_key == 'share_type': search_opts['sort_key'] = 'share_type_id' elif sort_key == 'snapshot': search_opts['sort_key'] = 'snapshot_id' elif sort_key == 'share_network': search_opts['sort_key'] = 'share_network_id' else: raise ValueError('sort_key must be one of the following: %s.' % ', '.join(constants.SHARE_SORT_KEY_VALUES)) if sort_dir is not None: if sort_dir in constants.SORT_DIR_VALUES: search_opts['sort_dir'] = sort_dir else: raise ValueError('sort_dir must be one of the following: %s.' % ', '.join(constants.SORT_DIR_VALUES)) if 'is_public' not in search_opts: search_opts['is_public'] = True export_location = search_opts.pop('export_location', None) if export_location: if uuidutils.is_uuid_like(export_location): search_opts['export_location_id'] = export_location else: search_opts['export_location_path'] = export_location query_string = self._build_query_string(search_opts) if detailed: path = "/shares/detail%s" % (query_string,) else: path = "/shares%s" % (query_string,) return self._list(path, 'shares') def delete(self, share, share_group_id=None): """Delete a share. :param share: either share object or text with its ID. :param share_group_id: text - ID of the share group to which the share belongs """ url = "/shares/%s" % common_base.getid(share) if share_group_id: url += "?share_group_id=%s" % share_group_id self._delete(url) def _do_force_delete(self, share, action_name): """Delete a share forcibly - share status will be avoided. :param share: either share object or text with its ID. """ return self._action(action_name, share) @api_versions.wraps("1.0", "2.6") def force_delete(self, share): return self._do_force_delete(share, "os-force_delete") @api_versions.wraps("2.7") # noqa def force_delete(self, share): return self._do_force_delete(share, "force_delete") @staticmethod def _validate_common_name(access): if len(access) == 0 or len(access) > 64: exc_str = ('Invalid CN (common name). Must be 1-64 chars long.') raise exceptions.CommandError(exc_str) ''' for the reference specification for AD usernames, reference below links: 1:https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/ windows-server-2008-R2-and-2008/cc733146(v=ws.11) 2:https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/ windows-server-2000/bb726984(v=technet.10) ''' @staticmethod def _validate_username(access): sole_periods_spaces_re = r'[\s|\.]+$' valid_username_re = r'.[^\"\/\\\[\]\:\;\|\=\,\+\*\?\<\>]{3,254}$' username = access if re.match(sole_periods_spaces_re, username): exc_str = ('Invalid user or group name,cannot consist solely ' 'of periods or spaces.') raise exceptions.CommandError(exc_str) if not re.match(valid_username_re, username): exc_str = ('Invalid user or group name. Must be 4-255 characters ' 'and consist of alphanumeric characters and ' 'exclude special characters "/\\[]:;|=,+*?<>') raise exceptions.CommandError(exc_str) @staticmethod def _validate_cephx_id(cephx_id): if not cephx_id: raise exceptions.CommandError( 'Ceph IDs may not be empty.') # This restriction may be lifted in Ceph in the future: # http://tracker.ceph.com/issues/14626 if not set(cephx_id) <= set(string.printable): raise exceptions.CommandError( 'Ceph IDs must consist of ASCII printable characters.') # Periods are technically permitted, but we restrict them here # to avoid confusion where users are unsure whether they should # include the "client." prefix: otherwise they could accidentally # create "client.client.foobar". if '.' in cephx_id: raise exceptions.CommandError( 'Ceph IDs may not contain periods.') def _validate_access(self, access_type, access, valid_access_types=None, enable_ipv6=False): if not valid_access_types: valid_access_types = ('ip', 'user', 'cert') if access_type in valid_access_types: if access_type == 'ip': try: if enable_ipv6: ipaddress.ip_network(six.text_type(access)) else: ipaddress.IPv4Network(six.text_type(access)) except ValueError as error: raise exceptions.CommandError(six.text_type(error)) elif access_type == 'user': self._validate_username(access) elif access_type == 'cert': # 'access' is used as the certificate's CN (common name) # to which access is allowed or denied by the backend. # The standard allows for just about any string in the # common name. The meaning of a string depends on its # interpretation and is limited to 64 characters. self._validate_common_name(access.strip()) elif access_type == 'cephx': self._validate_cephx_id(access.strip()) else: msg = ('Only following access types are supported: %s' % ', '.join(valid_access_types)) raise exceptions.CommandError(msg) def _do_allow(self, share, access_type, access, access_level, action_name, metadata=None): """Allow access to a share. :param share: either share object or text with its ID. :param access_type: string that represents access type ('ip','domain') :param access: string that represents access ('127.0.0.1') :param access_level: string that represents access level ('rw', 'ro') :param metadata: A dict of key/value pairs to be set """ access_params = { 'access_type': access_type, 'access_to': access, } if access_level: access_params['access_level'] = access_level if metadata: access_params['metadata'] = metadata access = self._action(action_name, share, access_params)[1]["access"] return access @api_versions.wraps("1.0", "2.6") def allow(self, share, access_type, access, access_level, metadata=None): self._validate_access(access_type, access) return self._do_allow( share, access_type, access, access_level, "os-allow_access") @api_versions.wraps("2.7", "2.12") # noqa def allow(self, share, access_type, access, access_level, metadata=None): self._validate_access(access_type, access) return self._do_allow( share, access_type, access, access_level, "allow_access") @api_versions.wraps("2.13", "2.37") # noqa def allow(self, share, access_type, access, access_level, metadata=None): valid_access_types = ('ip', 'user', 'cert', 'cephx') self._validate_access(access_type, access, valid_access_types) return self._do_allow( share, access_type, access, access_level, "allow_access") @api_versions.wraps("2.38", "2.44") # noqa def allow(self, share, access_type, access, access_level, metadata=None): valid_access_types = ('ip', 'user', 'cert', 'cephx') self._validate_access(access_type, access, valid_access_types, enable_ipv6=True) return self._do_allow( share, access_type, access, access_level, "allow_access") @api_versions.wraps("2.45") # noqa def allow(self, share, access_type, access, access_level, metadata=None): valid_access_types = ('ip', 'user', 'cert', 'cephx') self._validate_access(access_type, access, valid_access_types, enable_ipv6=True) return self._do_allow( share, access_type, access, access_level, "allow_access", metadata) def _do_deny(self, share, access_id, action_name): """Deny access to a share. :param share: either share object or text with its ID. :param access_id: ID of share access rule """ return self._action(action_name, share, {"access_id": access_id}) @api_versions.wraps("1.0", "2.6") def deny(self, share, access_id): return self._do_deny(share, access_id, "os-deny_access") @api_versions.wraps("2.7") # noqa def deny(self, share, access_id): return self._do_deny(share, access_id, "deny_access") def _do_access_list(self, share, action_name): """Get access list to a share. :param share: either share object or text with its ID. """ access_list = self._action(action_name, share)[1]["access_list"] if access_list: t = collections.namedtuple('Access', list(access_list[0])) return [t(*value.values()) for value in access_list] else: return [] @api_versions.wraps("1.0", "2.6") def access_list(self, share): return self._do_access_list(share, "os-access_list") @api_versions.wraps("2.7", "2.44") # noqa def access_list(self, share): return self._do_access_list(share, "access_list") def get_metadata(self, share): """Get metadata of a share. :param share: either share object or text with its ID. """ return self._get("/shares/%s/metadata" % common_base.getid(share), "metadata") def set_metadata(self, share, metadata): """Set or update metadata for share. :param share: either share object or text with its ID. :param metadata: A list of keys to be set. """ body = {'metadata': metadata} return self._create("/shares/%s/metadata" % common_base.getid(share), body, "metadata") def delete_metadata(self, share, keys): """Delete specified keys from shares metadata. :param share: either share object or text with its ID. :param keys: A list of keys to be removed. """ share_id = common_base.getid(share) for key in keys: self._delete("/shares/%(share_id)s/metadata/%(key)s" % { 'share_id': share_id, 'key': key}) def update_all_metadata(self, share, metadata): """Update all metadata of a share. :param share: either share object or text with its ID. :param metadata: A list of keys to be updated. """ body = {'metadata': metadata} return self._update("/shares/%s/metadata" % common_base.getid(share), body) def _action(self, action, share, info=None, **kwargs): """Perform a share 'action'. :param action: text with action name. :param share: either share object or text with its ID. :param info: dict with data for specified 'action'. :param kwargs: dict with data to be provided for action hooks. """ body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/shares/%s/action' % common_base.getid(share) return self.api.client.post(url, body=body) def _do_reset_state(self, share, state, action_name): """Update the provided share with the provided state. :param share: either share object or text with its ID. :param state: text with new state to set for share. """ return self._action(action_name, share, {"status": state}) @api_versions.wraps("1.0", "2.6") def reset_state(self, share, state): return self._do_reset_state(share, state, "os-reset_status") @api_versions.wraps("2.7") # noqa def reset_state(self, share, state): return self._do_reset_state(share, state, "reset_status") def _do_extend(self, share, new_size, action_name): """Extend the size of the specified share. :param share: either share object or text with its ID. :param new_size: The desired size to extend share to. """ return self._action(action_name, share, {"new_size": new_size}) @api_versions.wraps("1.0", "2.6") def extend(self, share, new_size): return self._do_extend(share, new_size, "os-extend") @api_versions.wraps("2.7") # noqa def extend(self, share, new_size): return self._do_extend(share, new_size, "extend") def _do_shrink(self, share, new_size, action_name): """Shrink the size of the specified share. :param share: either share object or text with its ID. :param new_size: The desired size to shrink share to. """ return self._action(action_name, share, {'new_size': new_size}) @api_versions.wraps("1.0", "2.6") def shrink(self, share, new_size): return self._do_shrink(share, new_size, "os-shrink") @api_versions.wraps("2.7") # noqa def shrink(self, share, new_size): return self._do_shrink(share, new_size, "shrink") def list_instances(self, share): """List instances of the specified share. :param share: either share object or text with its ID. """ return self._list( '/shares/%s/instances' % common_base.getid(share), 'share_instances', obj_class=share_instances.ShareInstance ) python-manilaclient-2.1.0/manilaclient/v2/share_replica_export_locations.py0000664000175000017500000000412513644133413027335 0ustar zuulzuul00000000000000# 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 api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base class ShareReplicaExportLocation(common_base.Resource): """Resource class for a share replica export location.""" def __repr__(self): return "" % self.id def __getitem__(self, key): return self._info[key] class ShareReplicaExportLocationManager(base.ManagerWithFind): """Manage :class:`ShareInstanceExportLocation` resources.""" resource_class = ShareReplicaExportLocation @api_versions.wraps("2.47") @api_versions.experimental_api def list(self, share_replica, search_opts=None): """List all share replica export locations.""" share_replica_id = common_base.getid(share_replica) return self._list( "/share-replicas/%s/export-locations" % share_replica_id, "export_locations") @api_versions.wraps("2.47") @api_versions.experimental_api def get(self, share_replica, export_location): """Get a share replica export location.""" share_replica_id = common_base.getid(share_replica) export_location_id = common_base.getid(export_location) return self._get( ("/share-replicas/%(share_replica_id)s/export-locations/" "%(export_location_id)s") % { "share_replica_id": share_replica_id, "export_location_id": export_location_id, }, "export_location") python-manilaclient-2.1.0/manilaclient/v2/share_access_rules.py0000664000175000017500000000602313644133413024714 0ustar zuulzuul00000000000000# Copyright 2018 Huawei Corporation. # All Rights Reserved. # # Copyright 2018 # # 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. """Interface for share access rules extension.""" from manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base RESOURCE_PATH = '/share-access-rules/%s' RESOURCE_NAME = 'access' RESOURCES_METADATA_PATH = '/share-access-rules/%s/metadata' RESOURCE_METADATA_PATH = '/share-access-rules/%s/metadata/%s' RESOURCE_LIST_PATH = '/share-access-rules' class ShareAccessRule(common_base.Resource): """A Share Access Rule.""" def __repr__(self): return "" % self.id def delete(self): """"Delete this share access rule.""" self.manager.delete(self) class ShareAccessRuleManager(base.ManagerWithFind): """Manage :class:`ShareAccessRule` resources.""" resource_class = ShareAccessRule @api_versions.wraps("2.45") def get(self, share_access_rule): """Get a specific share access rule. :param share_access_rule: either instance of ShareAccessRule, or text with UUID :rtype: :class:`ShareAccessRule` """ share_access_rule_id = common_base.getid(share_access_rule) url = RESOURCE_PATH % share_access_rule_id return self._get(url, RESOURCE_NAME) @api_versions.wraps("2.45") def set_metadata(self, access, metadata): """Set or update metadata for share access rule. :param share_access_rule: either share access rule object or text with its ID. :param metadata: A list of keys to be set. """ body = {'metadata': metadata} access_id = common_base.getid(access) url = RESOURCES_METADATA_PATH % access_id return self._update(url, body, "metadata") @api_versions.wraps("2.45") def unset_metadata(self, access, keys): """Unset metadata on a share access rule. :param keys: A list of keys on this object to be unset :return: None if successful, else API response on failure """ for k in keys: url = RESOURCE_METADATA_PATH % (common_base.getid(access), k) self._delete(url) @api_versions.wraps("2.45") def access_list(self, share, search_opts=None): search_opts = search_opts or {} search_opts['share_id'] = common_base.getid(share) query_string = self._build_query_string(search_opts) url = RESOURCE_LIST_PATH + query_string return self._list(url, 'access_list') python-manilaclient-2.1.0/manilaclient/v2/share_servers.py0000664000175000017500000001314113644133413023731 0ustar zuulzuul00000000000000# Copyright 2014 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 manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base RESOURCES_NAME = 'share_servers' RESOURCES_PATH = '/share-servers' RESOURCE_PATH = RESOURCES_PATH + '/%s' RESOURCE_NAME = 'share_server' ACTION_PATH = RESOURCE_PATH + '/action' class ShareServer(common_base.Resource): def __repr__(self): return "" % self.id def __getattr__(self, attr): if attr == 'share_network': attr = 'share_network_name' return super(ShareServer, self).__getattr__(attr) def delete(self): """Delete this share server.""" self.manager.delete(self) def unmanage(self, force=False): """Unmanage this share server.""" self.manager.unmanage(self, force) def reset_state(self, state): """Update the share server with the provided state.""" self.manager.reset_state(self, state) class ShareServerManager(base.ManagerWithFind): """Manage :class:`ShareServer` resources.""" resource_class = ShareServer def get(self, server): """Get a share server. :param server: ID of the :class:`ShareServer` to get. :rtype: :class:`ShareServer` """ server_id = common_base.getid(server) server = self._get("%s/%s" % (RESOURCES_PATH, server_id), RESOURCE_NAME) # Split big dict 'backend_details' to separated strings # as next: # +---------------------+------------------------------------+ # | Property | Value | # +---------------------+------------------------------------+ # | details:instance_id |35203a78-c733-4b1f-b82c-faded312e537| # +---------------------+------------------------------------+ for k, v in server._info["backend_details"].items(): server._info["details:%s" % k] = v return server def details(self, server): """Get a share server details. :param server: ID of the :class:`ShareServer` to get details from. :rtype: list of :class:`ShareServerBackendDetails """ server_id = common_base.getid(server) return self._get("%s/%s/details" % (RESOURCES_PATH, server_id), "details") def delete(self, server): """Delete share server. :param server: ID of the :class:`ShareServer` to delete. """ server_id = common_base.getid(server) self._delete(RESOURCE_PATH % server_id) def list(self, search_opts=None): """Get a list of share servers. :rtype: list of :class:`ShareServer` """ query_string = self._build_query_string(search_opts) return self._list(RESOURCES_PATH + query_string, RESOURCES_NAME) @api_versions.wraps("2.49", "2.50") def manage(self, host, share_network_id, identifier, driver_options=None): driver_options = driver_options or {} body = { 'host': host, 'share_network_id': share_network_id, 'identifier': identifier, 'driver_options': driver_options, } resource_path = RESOURCE_PATH % 'manage' return self._create(resource_path, {'share_server': body}, 'share_server') @api_versions.wraps("2.51") # noqa def manage(self, host, share_network_id, identifier, share_network_subnet_id=None, driver_options=None): driver_options = driver_options or {} body = { 'host': host, 'share_network_id': share_network_id, 'identifier': identifier, 'share_network_subnet_id': share_network_subnet_id, 'driver_options': driver_options, } resource_path = RESOURCE_PATH % 'manage' return self._create(resource_path, {'share_server': body}, 'share_server') @api_versions.wraps("2.49") def unmanage(self, share_server, force=False): return self._action("unmanage", share_server, {'force': force}) @api_versions.wraps("2.49") def reset_state(self, share_server, state): """Update the provided share server with the provided state. :param share_server: either share_server object or text with its ID. :param state: text with new state to set for share. """ return self._action("reset_status", share_server, {"status": state}) def _action(self, action, share_server, info=None): """Perform a share server 'action'. :param action: text with action name. :param share_server: either share_server object or text with its ID. :param info: dict with data for specified 'action'. :param kwargs: dict with data to be provided for action hooks. """ body = {action: info} self.run_hooks('modify_body_for_action', body) url = ACTION_PATH % common_base.getid(share_server) return self.api.client.post(url, body=body) python-manilaclient-2.1.0/manilaclient/v2/share_type_access.py0000664000175000017500000000462013644133413024544 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # 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. """Share type access interface.""" from manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base class ShareTypeAccess(common_base.Resource): def __repr__(self): return "" % self.id class ShareTypeAccessManager(base.ManagerWithFind): """Manage :class:`ShareTypeAccess` resources.""" resource_class = ShareTypeAccess def _do_list(self, share_type, action_name="share_type_access"): if share_type.is_public: return None return self._list( "/types/%(st_id)s/%(action_name)s" % { "st_id": common_base.getid(share_type), "action_name": action_name}, "share_type_access") @api_versions.wraps("1.0", "2.6") def list(self, share_type, search_opts=None): return self._do_list(share_type, "os-share-type-access") @api_versions.wraps("2.7") # noqa def list(self, share_type, search_opts=None): return self._do_list(share_type, "share_type_access") def add_project_access(self, share_type, project): """Add a project to the given share type access list.""" info = {'project': project} self._action('addProjectAccess', share_type, info) def remove_project_access(self, share_type, project): """Remove a project from the given share type access list.""" info = {'project': project} self._action('removeProjectAccess', share_type, info) def _action(self, action, share_type, info, **kwargs): """Perform a share type action.""" body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/types/%s/action' % common_base.getid(share_type) return self.api.client.post(url, body=body) python-manilaclient-2.1.0/manilaclient/v2/share_group_snapshots.py0000664000175000017500000002224413644133413025502 0ustar zuulzuul00000000000000# Copyright 2015 Chuck Fouts # Copyright 2016 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 manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base from manilaclient.common import constants RESOURCES_PATH = '/share-group-snapshots' RESOURCE_PATH = '/share-group-snapshots/%s' RESOURCE_PATH_ACTION = '/share-group-snapshots/%s/action' RESOURCES_NAME = 'share_group_snapshots' RESOURCE_NAME = 'share_group_snapshot' SG_GRADUATION_VERSION = "2.55" class ShareGroupSnapshot(common_base.Resource): """A snapshot of a share group.""" def __repr__(self): return "" % self.id def update(self, **kwargs): """Update this share group snapshot.""" self.manager.update(self, **kwargs) def delete(self): """Delete this share group snapshot.""" self.manager.delete(self) def reset_state(self, state): """Update this share group snapshot with the provided state.""" self.manager.reset_state(self, state) class ShareGroupSnapshotManager(base.ManagerWithFind): """Manage :class:`ShareGroupSnapshot` resources.""" resource_class = ShareGroupSnapshot def _create_share_group_snapshot(self, share_group, name=None, description=None): """Create a share group snapshot. :param share_group: either ShareGroup object or text with its UUID :param name: text - name of the new group snapshot :param description: text - description of the group snapshot :rtype: :class:`ShareGroupSnapshot` """ share_group_id = common_base.getid(share_group) body = {'share_group_id': share_group_id} if name: body['name'] = name if description: body['description'] = description return self._create( RESOURCES_PATH, {RESOURCE_NAME: body}, RESOURCE_NAME) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def create(self, share_group, name=None, description=None): return self._create_share_group_snapshot(share_group, name, description) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def create(self, share_group, name=None, description=None): return self._create_share_group_snapshot(share_group, name, description) def _get_share_group_snapshot(self, share_group_snapshot): """Get a share group snapshot. :param share_group_snapshot: either share group snapshot object or text with its UUID :rtype: :class:`ShareGroupSnapshot` """ share_group_snapshot_id = common_base.getid(share_group_snapshot) url = RESOURCE_PATH % share_group_snapshot_id return self._get(url, RESOURCE_NAME) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def get(self, share_group_snapshot): return self._get_share_group_snapshot(share_group_snapshot) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def get(self, share_group_snapshot): return self._get_share_group_snapshot(share_group_snapshot) def _list_share_group_snapshots(self, detailed=True, search_opts=None, sort_key=None, sort_dir=None): """Get a list of all share group snapshots. :param detailed: Whether to return detailed snapshot info or not. :param search_opts: dict with search options to filter out snapshots. available keys are below (('name1', 'name2', ...), 'type'): - ('all_tenants', int) - ('offset', int) - ('limit', int) - ('name', text) - ('status', text) - ('share_group_id', text) :param sort_key: Key to be sorted (i.e. 'created_at' or 'status'). :param sort_dir: Sort direction, should be 'desc' or 'asc'. :rtype: list of :class:`ShareGroupSnapshot` """ search_opts = search_opts or {} if sort_key is not None: if sort_key in constants.SHARE_GROUP_SNAPSHOT_SORT_KEY_VALUES: search_opts['sort_key'] = sort_key else: msg = 'sort_key must be one of the following: %s.' msg_args = ', '.join( constants.SHARE_GROUP_SNAPSHOT_SORT_KEY_VALUES) raise ValueError(msg % msg_args) if sort_dir is not None: if sort_dir in constants.SORT_DIR_VALUES: search_opts['sort_dir'] = sort_dir else: raise ValueError('sort_dir must be one of the following: %s.' % ', '.join(constants.SORT_DIR_VALUES)) query_string = self._build_query_string(search_opts) if detailed: url = RESOURCES_PATH + '/detail' + query_string else: url = RESOURCES_PATH + query_string return self._list(url, RESOURCES_NAME) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def list(self, detailed=True, search_opts=None, sort_key=None, sort_dir=None): return self._list_share_group_snapshots( detailed=detailed, search_opts=search_opts, sort_key=sort_key, sort_dir=sort_dir) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def list(self, detailed=True, search_opts=None, sort_key=None, sort_dir=None): return self._list_share_group_snapshots( detailed=detailed, search_opts=search_opts, sort_key=sort_key, sort_dir=sort_dir) def _update_share_group_snapshot(self, share_group_snapshot, **kwargs): """Updates a share group snapshot. :param share_group_snapshot: either ShareGroupSnapshot object or text with its UUID :rtype: :class:`ShareGroupSnapshot` """ share_group_snapshot_id = common_base.getid(share_group_snapshot) url = RESOURCE_PATH % share_group_snapshot_id if not kwargs: return self._get(url, RESOURCE_NAME) else: body = {RESOURCE_NAME: kwargs} return self._update(url, body, RESOURCE_NAME) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def update(self, share_group_snapshot, **kwargs): return self._update_share_group_snapshot(share_group_snapshot, **kwargs) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def update(self, share_group_snapshot, **kwargs): return self._update_share_group_snapshot(share_group_snapshot, **kwargs) def _delete_share_group_snapshot(self, share_group_snapshot, force=False): """Delete a share group snapshot. :param share_group_snapshot: either ShareGroupSnapshot object or text with its UUID :param force: True to force the deletion """ share_group_snapshot_id = common_base.getid(share_group_snapshot) if force: url = RESOURCE_PATH_ACTION % share_group_snapshot_id body = {'force_delete': None} self.api.client.post(url, body=body) else: url = RESOURCE_PATH % share_group_snapshot_id self._delete(url) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def delete(self, share_group_snapshot, force=False): self._delete_share_group_snapshot(share_group_snapshot, force=force) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def delete(self, share_group_snapshot, force=False): self._delete_share_group_snapshot(share_group_snapshot, force=force) def _share_group_snapshot_reset_state(self, share_group_snapshot, state): """Update the specified share group snapshot. :param share_group_snapshot: either ShareGroupSnapshot object or text with its UUID :param state: The new state for the share group snapshot """ share_group_snapshot_id = common_base.getid(share_group_snapshot) url = RESOURCE_PATH_ACTION % share_group_snapshot_id body = {'reset_status': {'status': state}} self.api.client.post(url, body=body) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def reset_state(self, share_group_snapshot, state): self._share_group_snapshot_reset_state(share_group_snapshot, state) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def reset_state(self, share_group_snapshot, state): self._share_group_snapshot_reset_state(share_group_snapshot, state) python-manilaclient-2.1.0/manilaclient/v2/quota_classes.py0000664000175000017500000001023013644133413023720 0ustar zuulzuul00000000000000# 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 manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base RESOURCE_PATH_LEGACY = '/os-quota-class-sets' RESOURCE_PATH = '/quota-class-sets' REPLICA_QUOTAS_MICROVERSION = "2.53" class QuotaClassSet(common_base.Resource): @property def id(self): """Needed by base.Resource to self-refresh and be indexed.""" return self.class_name def update(self, *args, **kwargs): self.manager.update(self.class_name, *args, **kwargs) class QuotaClassSetManager(base.ManagerWithFind): resource_class = QuotaClassSet @api_versions.wraps("1.0", "2.6") def get(self, class_name): return self._get( "%(resource_path)s/%(class_name)s" % { "resource_path": RESOURCE_PATH_LEGACY, "class_name": class_name}, "quota_class_set") @api_versions.wraps("2.7") # noqa def get(self, class_name): return self._get( "%(resource_path)s/%(class_name)s" % { "resource_path": RESOURCE_PATH, "class_name": class_name}, "quota_class_set") def _do_update(self, class_name, shares=None, gigabytes=None, snapshots=None, snapshot_gigabytes=None, share_networks=None, share_replicas=None, replica_gigabytes=None, resource_path=RESOURCE_PATH): body = { 'quota_class_set': { 'class_name': class_name, 'shares': shares, 'snapshots': snapshots, 'gigabytes': gigabytes, 'snapshot_gigabytes': snapshot_gigabytes, 'share_networks': share_networks, "share_replicas": share_replicas, "replica_gigabytes": replica_gigabytes } } for key in list(body['quota_class_set']): if body['quota_class_set'][key] is None: body['quota_class_set'].pop(key) self._update( "%(resource_path)s/%(class_name)s" % { "resource_path": resource_path, "class_name": class_name}, body) @api_versions.wraps("1.0", "2.6") def update(self, class_name, shares=None, gigabytes=None, snapshots=None, snapshot_gigabytes=None, share_networks=None): return self._do_update( class_name, shares=shares, gigabytes=gigabytes, snapshots=snapshots, snapshot_gigabytes=snapshot_gigabytes, share_networks=share_networks, resource_path=RESOURCE_PATH_LEGACY) @api_versions.wraps("2.7", "2.52") # noqa def update(self, class_name, shares=None, gigabytes=None, snapshots=None, snapshot_gigabytes=None, share_networks=None): return self._do_update( class_name, shares=shares, gigabytes=gigabytes, snapshots=snapshots, snapshot_gigabytes=snapshot_gigabytes, share_networks=share_networks, resource_path=RESOURCE_PATH) @api_versions.wraps(REPLICA_QUOTAS_MICROVERSION) # noqa def update(self, class_name, shares=None, gigabytes=None, snapshots=None, snapshot_gigabytes=None, share_networks=None, share_replicas=None, replica_gigabytes=None): return self._do_update( class_name, shares=shares, gigabytes=gigabytes, snapshots=snapshots, snapshot_gigabytes=snapshot_gigabytes, share_networks=share_networks, share_replicas=share_replicas, replica_gigabytes=replica_gigabytes, resource_path=RESOURCE_PATH) python-manilaclient-2.1.0/manilaclient/v2/share_groups.py0000664000175000017500000002603213644133413023562 0ustar zuulzuul00000000000000# Copyright 2015 Andrew Kerr # Copyright 2015 Chuck Fouts # Copyright 2016 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 manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base from manilaclient.common import constants RESOURCES_PATH = '/share-groups' RESOURCE_PATH = '/share-groups/%s' RESOURCE_PATH_ACTION = '/share-groups/%s/action' RESOURCES_NAME = 'share_groups' RESOURCE_NAME = 'share_group' SG_GRADUATION_VERSION = "2.55" class ShareGroup(common_base.Resource): """A share group is a logical grouping of shares on a single backend.""" def __repr__(self): return "" % self.id def update(self, **kwargs): """Update this share group.""" self.manager.update(self, **kwargs) def delete(self, force=False): """Delete this share group.""" self.manager.delete(self, force=force) def reset_state(self, state): """Update this share group with the provided state.""" self.manager.reset_state(self, state) class ShareGroupManager(base.ManagerWithFind): """Manage :class:`ShareGroup` resources.""" resource_class = ShareGroup def _create_share_group( self, share_group_type=None, share_types=None, share_network=None, name=None, description=None, source_share_group_snapshot=None, availability_zone=None): """Create a Share Group. :param share_group_type: either instance of ShareGroupType or text with UUID :param share_types: list of the share types allowed in the group. May not be supplied when 'source_group_snapshot_id' is provided. These may be ShareType objects or UUIDs. :param share_network: either the share network object or text of the UUID - represents the share network to use when creating a share group when driver_handles_share_servers = True. :param name: text - name of the new share group :param description: text - description of the share group :param source_share_group_snapshot: text - either instance of ShareGroupSnapshot or text with UUID from which this shar_group is to be created. May not be supplied when 'share_types' is provided. :param availability_zone: name of the availability zone where the group is to be created :rtype: :class:`ShareGroup` """ if share_types and source_share_group_snapshot: raise ValueError('Cannot specify a share group with both' 'share_types and source_share_group_snapshot.') body = {} if name: body['name'] = name if description: body['description'] = description if availability_zone: body['availability_zone'] = availability_zone if share_group_type: body['share_group_type_id'] = common_base.getid(share_group_type) if share_network: body['share_network_id'] = common_base.getid(share_network) if source_share_group_snapshot: body['source_share_group_snapshot_id'] = common_base.getid( source_share_group_snapshot) elif share_types: body['share_types'] = [common_base.getid(share_type) for share_type in share_types] return self._create( RESOURCES_PATH, {RESOURCE_NAME: body}, RESOURCE_NAME) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def create(self, share_group_type=None, share_types=None, share_network=None, name=None, description=None, source_share_group_snapshot=None, availability_zone=None): return self._create_share_group( share_group_type=share_group_type, share_types=share_types, share_network=share_network, name=name, description=description, source_share_group_snapshot=source_share_group_snapshot, availability_zone=availability_zone) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def create(self, share_group_type=None, share_types=None, share_network=None, name=None, description=None, source_share_group_snapshot=None, availability_zone=None): return self._create_share_group( share_group_type=share_group_type, share_types=share_types, share_network=share_network, name=name, description=description, source_share_group_snapshot=source_share_group_snapshot, availability_zone=availability_zone) def _get_share_group(self, share_group): """Get a share group. :param share_group: either ShareGroup object or text with its UUID :rtype: :class:`ShareGroup` """ share_group_id = common_base.getid(share_group) url = RESOURCE_PATH % share_group_id return self._get(url, RESOURCE_NAME) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def get(self, share_group): return self._get_share_group(share_group) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def get(self, share_group): return self._get_share_group(share_group) def _list_share_groups(self, detailed=True, search_opts=None, sort_key=None, sort_dir=None): """Get a list of all share groups. :param detailed: Whether to return detailed share group info or not. :param search_opts: dict with search options to filter out groups. available keys include (('name1', 'name2', ...), 'type'): - ('offset', int) - ('limit', int) - ('all_tenants', int) - ('name', text) - ('status', text) - ('share_server_id', text) - ('share_group_type_id', text) - ('source_share_group_snapshot_id', text) - ('host', text) - ('share_network_id', text) - ('project_id', text) :param sort_key: Key to be sorted (i.e. 'created_at' or 'status'). :param sort_dir: Sort direction, should be 'desc' or 'asc'. :rtype: list of :class:`ShareGroup` """ search_opts = search_opts or {} if sort_key is not None: if sort_key in constants.SHARE_GROUP_SORT_KEY_VALUES: search_opts['sort_key'] = sort_key # NOTE(cknight): Replace aliases with appropriate keys if sort_key == 'share_group_type': search_opts['sort_key'] = 'share_group_type_id' elif sort_key == 'share_network': search_opts['sort_key'] = 'share_network_id' else: msg = 'sort_key must be one of the following: %s.' msg_args = ', '.join(constants.SHARE_GROUP_SORT_KEY_VALUES) raise ValueError(msg % msg_args) if sort_dir is not None: if sort_dir in constants.SORT_DIR_VALUES: search_opts['sort_dir'] = sort_dir else: raise ValueError('sort_dir must be one of the following: %s.' % ', '.join(constants.SORT_DIR_VALUES)) query_string = self._build_query_string(search_opts) if detailed: url = RESOURCES_PATH + '/detail' + query_string else: url = RESOURCES_PATH + query_string return self._list(url, RESOURCES_NAME) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def list(self, detailed=True, search_opts=None, sort_key=None, sort_dir=None): return self._list_share_groups( detailed=detailed, search_opts=search_opts, sort_key=sort_key, sort_dir=sort_dir) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def list(self, detailed=True, search_opts=None, sort_key=None, sort_dir=None): return self._list_share_groups( detailed=detailed, search_opts=search_opts, sort_key=sort_key, sort_dir=sort_dir) def _update_share_group(self, share_group, **kwargs): """Updates a share group. :param share_group: either ShareGroup object or text with its UUID :rtype: :class:`ShareGroup` """ share_group_id = common_base.getid(share_group) url = RESOURCE_PATH % share_group_id if not kwargs: return self._get(url, RESOURCE_NAME) else: body = {RESOURCE_NAME: kwargs} return self._update(url, body, RESOURCE_NAME) @api_versions.wraps("2.31", "2.54") def update(self, share_group, **kwargs): return self._update_share_group(share_group, **kwargs) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def update(self, share_group, **kwargs): return self._update_share_group(share_group, **kwargs) def _delete_share_group(self, share_group, force=False): """Delete a share group. :param share_group: either ShareGroup object or text with its UUID :param force: True to force the deletion """ share_group_id = common_base.getid(share_group) if force: url = RESOURCE_PATH_ACTION % share_group_id body = {'force_delete': None} self.api.client.post(url, body=body) else: url = RESOURCE_PATH % share_group_id self._delete(url) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def delete(self, share_group, force=False): self._delete_share_group(share_group, force=force) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def delete(self, share_group, force=False): self._delete_share_group(share_group, force=force) def _share_group_reset_state(self, share_group, state): """Update the specified share group with the provided state. :param share_group: either ShareGroup object or text with its UUID :param state: The new state for the share group """ share_group_id = common_base.getid(share_group) url = RESOURCE_PATH_ACTION % share_group_id body = {'reset_status': {'status': state}} self.api.client.post(url, body=body) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def reset_state(self, share_group, state): self._share_group_reset_state(share_group, state) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def reset_state(self, share_group, state): self._share_group_reset_state(share_group, state) python-manilaclient-2.1.0/manilaclient/v2/share_instances.py0000664000175000017500000001030613644133413024227 0ustar zuulzuul00000000000000# 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 oslo_utils import uuidutils from manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base class ShareInstance(common_base.Resource): """A share is an extra block level storage to the OpenStack instances.""" def __repr__(self): return "" % self.id def force_delete(self): """Delete the specified share ignoring its current state.""" self.manager.force_delete(self) def reset_state(self, state): """Update the share with the provided state.""" self.manager.reset_state(self, state) class ShareInstanceManager(base.ManagerWithFind): """Manage :class:`ShareInstances` resources.""" resource_class = ShareInstance @api_versions.wraps("2.3") def get(self, instance): """Get a share instance. :param instance: either share object or text with its ID. :rtype: :class:`ShareInstance` """ share_id = common_base.getid(instance) return self._get("/share_instances/%s" % share_id, "share_instance") @api_versions.wraps("2.3", "2.34") def list(self, search_opts=None): """List all share instances.""" return self.do_list() @api_versions.wraps("2.35") # noqa def list(self, export_location=None, search_opts=None): """List all share instances.""" return self.do_list(export_location) def do_list(self, export_location=None): """List all share instances.""" path = '/share_instances' if export_location: if uuidutils.is_uuid_like(export_location): path += '?export_location_id=' + export_location else: path += '?export_location_path=' + export_location return self._list(path, 'share_instances') def _action(self, action, instance, info=None, **kwargs): """Perform a share instance 'action'. :param action: text with action name. :param instance: either share object or text with its ID. :param info: dict with data for specified 'action'. :param kwargs: dict with data to be provided for action hooks. """ body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/share_instances/%s/action' % common_base.getid(instance) return self.api.client.post(url, body=body) def _do_force_delete(self, instance, action_name="force_delete"): """Delete a share instance forcibly - share status will be avoided. :param instance: either share instance object or text with its ID. """ return self._action(action_name, common_base.getid(instance)) @api_versions.wraps("2.3", "2.6") def force_delete(self, instance): return self._do_force_delete(instance, "os-force_delete") @api_versions.wraps("2.7") # noqa def force_delete(self, instance): return self._do_force_delete(instance, "force_delete") def _do_reset_state(self, instance, state, action_name): """Update the provided share instance with the provided state. :param instance: either share object or text with its ID. :param state: text with new state to set for share. """ return self._action(action_name, instance, {"status": state}) @api_versions.wraps("2.3", "2.6") def reset_state(self, instance, state): return self._do_reset_state(instance, state, "os-reset_status") @api_versions.wraps("2.7") # noqa def reset_state(self, instance, state): return self._do_reset_state(instance, state, "reset_status") python-manilaclient-2.1.0/manilaclient/v2/share_snapshots.py0000664000175000017500000002100213644133413024255 0ustar zuulzuul00000000000000# Copyright 2012 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. """Interface for shares extension.""" from manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base from manilaclient.common import constants class ShareSnapshot(common_base.Resource): """Represent a snapshot of a share.""" def __repr__(self): return "" % self.id def update(self, **kwargs): """Update this snapshot.""" self.manager.update(self, **kwargs) def reset_state(self, state): """Update the snapshot with the provided state.""" self.manager.reset_state(self, state) def delete(self): """Delete this snapshot.""" self.manager.delete(self) def force_delete(self): """Delete the specified snapshot ignoring its current state.""" self.manager.force_delete(self) def unmanage_snapshot(self): """Unmanage this snapshot.""" self.manager.unmanage(self) def allow(self, access_type, access_to): """Allow access to a share snapshot.""" return self.manager.allow(self, access_type, access_to) def deny(self, id): """Denies access to a share snapshot.""" return self.manager.deny(self, id) def access_list(self): return self.manager.access_list(self) class ShareSnapshotManager(base.ManagerWithFind): """Manage :class:`ShareSnapshot` resources.""" resource_class = ShareSnapshot def create(self, share, force=False, name=None, description=None): """Create a snapshot of the given share. :param share_id: The ID of the share to snapshot. :param force: If force is True, create a snapshot even if the share is busy. Default is False. :param name: Name of the snapshot :param description: Description of the snapshot :rtype: :class:`ShareSnapshot` """ body = {'snapshot': {'share_id': common_base.getid(share), 'force': force, 'name': name, 'description': description}} return self._create('/snapshots', body, 'snapshot') @api_versions.wraps("2.12") def manage(self, share, provider_location, driver_options=None, name=None, description=None): """Manage an existing share snapshot. :param share: The share object. :param provider_location: The provider location of the snapshot on the backend. :param driver_options: dict - custom set of key-values. :param name: text - name of new snapshot :param description: - description for new snapshot """ driver_options = driver_options if driver_options else {} body = { 'share_id': common_base.getid(share), 'provider_location': provider_location, 'driver_options': driver_options, 'name': name, 'description': description, } return self._create('/snapshots/manage', {'snapshot': body}, 'snapshot') @api_versions.wraps("2.12") def unmanage(self, snapshot): """Unmanage a share snapshot. :param snapshot: either snapshot object or text with its ID. """ return self._action("unmanage", snapshot) def get(self, snapshot): """Get a snapshot. :param snapshot: The :class:`ShareSnapshot` instance or string with ID of snapshot to delete. :rtype: :class:`ShareSnapshot` """ snapshot_id = common_base.getid(snapshot) return self._get('/snapshots/%s' % snapshot_id, 'snapshot') def list(self, detailed=True, search_opts=None, sort_key=None, sort_dir=None): """Get a list of snapshots of shares. :param search_opts: Search options to filter out shares. :param sort_key: Key to be sorted. :param sort_dir: Sort direction, should be 'desc' or 'asc'. :rtype: list of :class:`ShareSnapshot` """ search_opts = search_opts or {} if sort_key is not None: if sort_key in constants.SNAPSHOT_SORT_KEY_VALUES: search_opts['sort_key'] = sort_key else: raise ValueError( 'sort_key must be one of the following: %s.' % ', '.join(constants.SNAPSHOT_SORT_KEY_VALUES)) if sort_dir is not None: if sort_dir in constants.SORT_DIR_VALUES: search_opts['sort_dir'] = sort_dir else: raise ValueError( 'sort_dir must be one of the following: %s.' % ', '.join(constants.SORT_DIR_VALUES)) query_string = self._build_query_string(search_opts) if detailed: path = "/snapshots/detail%s" % (query_string,) else: path = "/snapshots%s" % (query_string,) return self._list(path, 'snapshots') def delete(self, snapshot): """Delete a snapshot of a share. :param snapshot: The :class:`ShareSnapshot` to delete. """ self._delete("/snapshots/%s" % common_base.getid(snapshot)) def _do_force_delete(self, snapshot, action_name="force_delete"): """Delete the specified snapshot ignoring its current state.""" return self._action(action_name, common_base.getid(snapshot)) @api_versions.wraps("1.0", "2.6") def force_delete(self, snapshot): return self._do_force_delete(snapshot, "os-force_delete") @api_versions.wraps("2.7") # noqa def force_delete(self, snapshot): return self._do_force_delete(snapshot, "force_delete") def update(self, snapshot, **kwargs): """Update a snapshot. :param snapshot: The :class:`ShareSnapshot` instance or string with ID of snapshot to delete. :rtype: :class:`ShareSnapshot` """ if not kwargs: return body = {'snapshot': kwargs, } snapshot_id = common_base.getid(snapshot) return self._update("/snapshots/%s" % snapshot_id, body) def _do_reset_state(self, snapshot, state, action_name="reset_status"): """Update the specified share snapshot with the provided state.""" return self._action(action_name, snapshot, {"status": state}) @api_versions.wraps("1.0", "2.6") def reset_state(self, snapshot, state): return self._do_reset_state(snapshot, state, "os-reset_status") @api_versions.wraps("2.7") # noqa def reset_state(self, snapshot, state): return self._do_reset_state(snapshot, state, "reset_status") def _do_allow(self, snapshot, access_type, access_to): access_params = { 'access_type': access_type, 'access_to': access_to, } return self._action('allow_access', snapshot, access_params)[1]['snapshot_access'] @api_versions.wraps("2.32") def allow(self, snapshot, access_type, access_to): return self._do_allow(snapshot, access_type, access_to) def _do_deny(self, snapshot, id): return self._action('deny_access', snapshot, {'access_id': id}) @api_versions.wraps("2.32") def deny(self, snapshot, id): return self._do_deny(snapshot, id) def _do_access_list(self, snapshot): snapshot_id = common_base.getid(snapshot) access_list = self._list("/snapshots/%s/access-list" % snapshot_id, 'snapshot_access_list') return access_list @api_versions.wraps("2.32") def access_list(self, snapshot): return self._do_access_list(snapshot) def _action(self, action, snapshot, info=None, **kwargs): """Perform a snapshot 'action'.""" body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/snapshots/%s/action' % common_base.getid(snapshot) return self.api.client.post(url, body=body) python-manilaclient-2.1.0/manilaclient/v2/share_network_subnets.py0000664000175000017500000000657013644133413025504 0ustar zuulzuul00000000000000# 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. from manilaclient import base from manilaclient.common.apiclient import base as common_base RESOURCES_PATH = '/share-networks/%(share_network_id)s/subnets' RESOURCE_PATH = RESOURCES_PATH + '/%(share_network_subnet_id)s' RESOURCE_NAME = 'share_network_subnet' class ShareNetworkSubnet(common_base.Resource): """Network subnet info for Manila share networks.""" def __repr__(self): return "" % self.id def __getitem__(self, key): return self._info[key] def delete(self): """Delete this share network subnet.""" self.manager.delete(self) class ShareNetworkSubnetManager(base.ManagerWithFind): """Manage :class:`ShareNetworkSubnet` resources.""" resource_class = ShareNetworkSubnet def create(self, neutron_net_id=None, neutron_subnet_id=None, availability_zone=None, share_network_id=None): """Create share network subnet. :param neutron_net_id: ID of Neutron network :param neutron_subnet_id: ID of Neutron subnet :param availability_zone: Name of the target availability zone :rtype: :class:`ShareNetworkSubnet` """ values = {} if neutron_net_id: values['neutron_net_id'] = neutron_net_id if neutron_subnet_id: values['neutron_subnet_id'] = neutron_subnet_id if availability_zone: values['availability_zone'] = availability_zone body = {'share-network-subnet': values} url = '/share-networks/%(share_network_id)s/subnets' % { 'share_network_id': share_network_id } return self._create(url, body, RESOURCE_NAME) def get(self, share_network, share_network_subnet): """Get a share network subnet. :param policy: share network subnet to get. :rtype: :class:`NetworkSubnetInfo` """ share_network_id = common_base.getid(share_network) share_network_subnet_id = common_base.getid(share_network_subnet) url = ('/share-networks/%(share_network_id)s/subnets' '/%(share_network_subnet)s') % { 'share_network_id': share_network_id, 'share_network_subnet': share_network_subnet_id } return self._get(url, "share_network_subnet") def delete(self, share_network, share_network_subnet): """Delete a share network subnet. :param share_network: share network that owns the subnet. :param share_network_subnet: share network subnet to be deleted. """ url = ('/share-networks/%(share_network_id)s/subnets' '/%(share_network_subnet)s') % { 'share_network_id': common_base.getid(share_network), 'share_network_subnet': share_network_subnet } self._delete(url) python-manilaclient-2.1.0/manilaclient/v2/share_snapshot_instance_export_locations.py0000664000175000017500000000375613644133413031452 0ustar zuulzuul00000000000000# 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 manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base class ShareSnapshotInstanceExportLocation(common_base.Resource): """Represent an export location from a snapshot instance.""" def __repr__(self): return "" % self.id def __getitem__(self, key): return self._info[key] class ShareSnapshotInstanceExportLocationManager(base.ManagerWithFind): """Manage :class:`ShareSnapshotInstanceExportLocation` resources.""" resource_class = ShareSnapshotInstanceExportLocation @api_versions.wraps("2.32") def list(self, snapshot_instance=None, search_opts=None): return self._list("/snapshot-instances/%s/export-locations" % common_base.getid(snapshot_instance), 'share_snapshot_export_locations') @api_versions.wraps("2.32") def get(self, export_location, snapshot_instance=None): params = { "snapshot_instance_id": common_base.getid(snapshot_instance), "export_location_id": common_base.getid(export_location), } return self._get("/snapshot-instances/%(snapshot_instance_id)s/" "export-locations/%(export_location_id)s" % params, "share_snapshot_export_location") python-manilaclient-2.1.0/manilaclient/v2/contrib/0000775000175000017500000000000013644133466022154 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/v2/contrib/list_extensions.py0000664000175000017500000000275513644133413025761 0ustar zuulzuul00000000000000# 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 manilaclient import base from manilaclient.common.apiclient import base as common_base from manilaclient.common import cliutils class ListExtResource(common_base.Resource): @property def summary(self): descr = self.description.strip() if not descr: return '??' lines = descr.split("\n") if len(lines) == 1: return lines[0] else: return lines[0] + "..." class ListExtManager(base.Manager): resource_class = ListExtResource def show_all(self): return self._list("/extensions", 'extensions') @cliutils.service_type('share') def do_list_extensions(client, _args): """List all the os-api extensions that are available.""" extensions = client.list_extensions.show_all() fields = ["Name", "Summary", "Alias", "Updated"] cliutils.print_list(extensions, fields) python-manilaclient-2.1.0/manilaclient/v2/contrib/__init__.py0000664000175000017500000000000013644133413024243 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/v2/limits.py0000664000175000017500000000553113644133413022363 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2015 Chuck Fouts # 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 base from manilaclient.common.apiclient import base as common_base class Limits(common_base.Resource): """A collection of RateLimit and AbsoluteLimit objects.""" def __repr__(self): return "" @property def absolute(self): for (name, value) in list(self._info['absolute'].items()): yield AbsoluteLimit(name, value) @property def rate(self): for group in self._info['rate']: uri = group['uri'] regex = group['regex'] for rate in group['limit']: yield RateLimit(rate['verb'], uri, regex, rate['value'], rate['remaining'], rate['unit'], rate['next-available']) class RateLimit(object): """Data model that represents a flattened view of a single rate limit.""" def __init__(self, verb, uri, regex, value, remain, unit, next_available): self.verb = verb self.uri = uri self.regex = regex self.value = value self.remain = remain self.unit = unit self.next_available = next_available def __eq__(self, other): return (self.uri == other.uri and self.regex == other.regex and self.value == other.value and self.verb == other.verb and self.remain == other.remain and self.unit == other.unit and self.next_available == other.next_available) def __repr__(self): return "" % (self.verb, self.uri) class AbsoluteLimit(object): """Data model that represents a single absolute limit.""" def __init__(self, name, value): self.name = name self.value = value def __eq__(self, other): return self.value == other.value and self.name == other.name def __repr__(self): return "" % (self.name) class LimitsManager(base.Manager): """Manager object used to interact with limits resource.""" resource_class = Limits def get(self): """Get a specific extension. :rtype: :class:`Limits` """ return self._get("/limits", "limits") python-manilaclient-2.1.0/manilaclient/v2/share_export_locations.py0000664000175000017500000000354313644133413025641 0ustar zuulzuul00000000000000# 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 manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base class ShareExportLocation(common_base.Resource): """Resource class for a share export location.""" def __repr__(self): return "" % self.id def __getitem__(self, key): return self._info[key] class ShareExportLocationManager(base.ManagerWithFind): """Manage :class:`ShareExportLocation` resources.""" resource_class = ShareExportLocation @api_versions.wraps("2.9") def list(self, share, search_opts=None): """List all share export locations.""" share_id = common_base.getid(share) return self._list("/shares/%s/export_locations" % share_id, "export_locations") @api_versions.wraps("2.9") def get(self, share, export_location): """Get a share export location.""" share_id = common_base.getid(share) export_location_id = common_base.getid(export_location) return self._get( "/shares/%(share_id)s/export_locations/%(export_location_id)s" % { "share_id": share_id, "export_location_id": export_location_id}, "export_location") python-manilaclient-2.1.0/manilaclient/v2/share_snapshot_instances.py0000664000175000017500000000534613644133413026156 0ustar zuulzuul00000000000000# 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. from manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base class ShareSnapshotInstance(common_base.Resource): """A snapshot instance is an instance of a snapshot.""" def __repr__(self): return "" % self.id def reset_state(self, state): """Update snapshot instance's 'status' attr.""" self.manager.reset_state(self, state) class ShareSnapshotInstanceManager(base.ManagerWithFind): """Manage :class:`SnapshotInstances` resources.""" resource_class = ShareSnapshotInstance @api_versions.wraps("2.19") def get(self, instance): """Get a snapshot instance. :param instance: either snapshot instance object or text with its ID. :rtype: :class:`ShareSnapshotInstance` """ snapshot_instance_id = common_base.getid(instance) return self._get("/snapshot-instances/%s" % snapshot_instance_id, "snapshot_instance") @api_versions.wraps("2.19") def list(self, detailed=False, snapshot=None, search_opts=None): """List all snapshot instances.""" if detailed: url = '/snapshot-instances/detail' else: url = '/snapshot-instances' if snapshot: url += '?snapshot_id=%s' % common_base.getid(snapshot) return self._list(url, 'snapshot_instances') @api_versions.wraps("2.19") def reset_state(self, instance, state): """Reset the 'status' attr of the snapshot instance. :param instance: either snapshot instance object or its UUID. :param state: state to set the snapshot instance's 'status' attr to. """ return self._action("reset_status", instance, {"status": state}) def _action(self, action, instance, info=None, **kwargs): """Perform a snapshot instance 'action'.""" body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = ('/snapshot-instances/%s/action' % common_base.getid(instance)) return self.api.client.post(url, body=body) python-manilaclient-2.1.0/manilaclient/v2/quotas.py0000664000175000017500000002335613644133413022403 0ustar zuulzuul00000000000000# 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 manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base RESOURCE_PATH_LEGACY = '/os-quota-sets' RESOURCE_PATH = '/quota-sets' REPLICA_QUOTAS_MICROVERSION = "2.53" class QuotaSet(common_base.Resource): @property def id(self): """Needed by Resource to self-refresh and be indexed.""" return self.tenant_id def update(self, *args, **kwargs): self.manager.update(self.tenant_id, *args, **kwargs) class QuotaSetManager(base.ManagerWithFind): resource_class = QuotaSet def _check_user_id_and_share_type_args(self, user_id, share_type): if user_id and share_type: raise ValueError( "'user_id' and 'share_type' values are mutually exclusive. " "one or both should be unset.") def _do_get(self, tenant_id, user_id=None, share_type=None, detail=False, resource_path=RESOURCE_PATH): self._check_user_id_and_share_type_args(user_id, share_type) if hasattr(tenant_id, 'tenant_id'): tenant_id = tenant_id.tenant_id if detail: query = '/detail' else: query = '' if user_id and share_type: query = '%s?user_id=%s&share_type=%s' % ( query, user_id, share_type) elif user_id: query = '%s?user_id=%s' % (query, user_id) elif share_type: query = '%s?share_type=%s' % (query, share_type) data = { "resource_path": resource_path, "tenant_id": tenant_id, } url = ("%(resource_path)s/%(tenant_id)s" + query) % data return self._get(url, "quota_set") @api_versions.wraps("1.0", "2.6") def get(self, tenant_id, user_id=None, detail=False): return self._do_get(tenant_id, user_id, resource_path=RESOURCE_PATH_LEGACY) @api_versions.wraps("2.7", "2.24") # noqa def get(self, tenant_id, user_id=None, detail=False): return self._do_get(tenant_id, user_id, resource_path=RESOURCE_PATH) @api_versions.wraps("2.25", "2.38") # noqa def get(self, tenant_id, user_id=None, detail=False): return self._do_get(tenant_id, user_id, detail=detail, resource_path=RESOURCE_PATH) @api_versions.wraps("2.39") # noqa def get(self, tenant_id, user_id=None, share_type=None, detail=False): return self._do_get( tenant_id, user_id, share_type=share_type, detail=detail, resource_path=RESOURCE_PATH) def _do_update(self, tenant_id, shares=None, snapshots=None, gigabytes=None, snapshot_gigabytes=None, share_networks=None, force=None, user_id=None, share_type=None, share_groups=None, share_group_snapshots=None, share_replicas=None, replica_gigabytes=None, resource_path=RESOURCE_PATH): self._check_user_id_and_share_type_args(user_id, share_type) body = { 'quota_set': { 'tenant_id': tenant_id, 'shares': shares, 'snapshots': snapshots, 'gigabytes': gigabytes, 'snapshot_gigabytes': snapshot_gigabytes, 'share_networks': share_networks, 'share_groups': share_groups, 'share_group_snapshots': share_group_snapshots, 'force': force, 'share_replicas': share_replicas, 'replica_gigabytes': replica_gigabytes, }, } for key in list(body['quota_set']): if body['quota_set'][key] is None: body['quota_set'].pop(key) data = { "resource_path": resource_path, "tenant_id": tenant_id, "user_id": user_id, "st": share_type, } if user_id: url = '%(resource_path)s/%(tenant_id)s?user_id=%(user_id)s' % data elif share_type: url = '%(resource_path)s/%(tenant_id)s?share_type=%(st)s' % data else: url = "%(resource_path)s/%(tenant_id)s" % data return self._update(url, body, 'quota_set') @api_versions.wraps("1.0", "2.6") def update(self, tenant_id, shares=None, snapshots=None, gigabytes=None, snapshot_gigabytes=None, share_networks=None, force=None, user_id=None): return self._do_update( tenant_id, shares, snapshots, gigabytes, snapshot_gigabytes, share_networks, force, user_id, resource_path=RESOURCE_PATH_LEGACY, ) @api_versions.wraps("2.7", "2.38") # noqa def update(self, tenant_id, shares=None, snapshots=None, gigabytes=None, snapshot_gigabytes=None, share_networks=None, force=None, user_id=None): return self._do_update( tenant_id, shares, snapshots, gigabytes, snapshot_gigabytes, share_networks, force, user_id, resource_path=RESOURCE_PATH, ) def _validate_st_and_sn_in_same_request(self, share_type, share_networks): if share_type and share_networks: raise ValueError( "'share_networks' quota can be set only for project or user, " "not share type.") @api_versions.wraps("2.39", "2.39") # noqa def update(self, tenant_id, user_id=None, share_type=None, shares=None, snapshots=None, gigabytes=None, snapshot_gigabytes=None, share_networks=None, force=None): self._validate_st_and_sn_in_same_request(share_type, share_networks) return self._do_update( tenant_id, shares, snapshots, gigabytes, snapshot_gigabytes, share_networks, force, user_id, share_type=share_type, resource_path=RESOURCE_PATH, ) @api_versions.wraps("2.40", "2.52") # noqa def update(self, tenant_id, user_id=None, share_type=None, shares=None, snapshots=None, gigabytes=None, snapshot_gigabytes=None, share_networks=None, share_groups=None, share_group_snapshots=None, force=None): self._validate_st_and_sn_in_same_request(share_type, share_networks) return self._do_update( tenant_id, shares, snapshots, gigabytes, snapshot_gigabytes, share_networks, force, user_id, share_type=share_type, share_groups=share_groups, share_group_snapshots=share_group_snapshots, resource_path=RESOURCE_PATH, ) @api_versions.wraps(REPLICA_QUOTAS_MICROVERSION) # noqa def update(self, tenant_id, user_id=None, share_type=None, shares=None, snapshots=None, gigabytes=None, snapshot_gigabytes=None, share_networks=None, share_groups=None, share_group_snapshots=None, share_replicas=None, replica_gigabytes=None, force=None): self._validate_st_and_sn_in_same_request(share_type, share_networks) return self._do_update( tenant_id, shares, snapshots, gigabytes, snapshot_gigabytes, share_networks, force, user_id, share_type=share_type, share_groups=share_groups, share_group_snapshots=share_group_snapshots, share_replicas=share_replicas, replica_gigabytes=replica_gigabytes, resource_path=RESOURCE_PATH ) @api_versions.wraps("1.0", "2.6") def defaults(self, tenant_id): return self._get( "%(resource_path)s/%(tenant_id)s/defaults" % { "resource_path": RESOURCE_PATH_LEGACY, "tenant_id": tenant_id}, "quota_set") @api_versions.wraps("2.7") # noqa def defaults(self, tenant_id): return self._get( "%(resource_path)s/%(tenant_id)s/defaults" % { "resource_path": RESOURCE_PATH, "tenant_id": tenant_id}, "quota_set") def _do_delete(self, tenant_id, user_id=None, share_type=None, resource_path=RESOURCE_PATH): self._check_user_id_and_share_type_args(user_id, share_type) data = { "resource_path": resource_path, "tenant_id": tenant_id, "user_id": user_id, "st": share_type, } if user_id: url = '%(resource_path)s/%(tenant_id)s?user_id=%(user_id)s' % data elif share_type: url = '%(resource_path)s/%(tenant_id)s?share_type=%(st)s' % data else: url = '%(resource_path)s/%(tenant_id)s' % data self._delete(url) @api_versions.wraps("1.0", "2.6") def delete(self, tenant_id, user_id=None): return self._do_delete( tenant_id, user_id, resource_path=RESOURCE_PATH_LEGACY) @api_versions.wraps("2.7", "2.38") # noqa def delete(self, tenant_id, user_id=None): return self._do_delete(tenant_id, user_id, resource_path=RESOURCE_PATH) @api_versions.wraps("2.39") # noqa def delete(self, tenant_id, user_id=None, share_type=None): return self._do_delete( tenant_id, user_id, share_type, resource_path=RESOURCE_PATH) python-manilaclient-2.1.0/manilaclient/v2/services.py0000664000175000017500000000651213644133413022705 0ustar zuulzuul00000000000000# Copyright 2014 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 manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base RESOURCE_PATH_LEGACY = '/os-services' RESOURCE_PATH = '/services' RESOURCE_NAME = 'services' class Service(common_base.Resource): def __repr__(self): return "" % self.id def server_api_version(self, **kwargs): """Get api version.""" return self.manager.api_version(self, kwargs) class ServiceManager(base.Manager): """Manage :class:`Service` resources.""" resource_class = Service def _do_list(self, search_opts=None, resource_path=RESOURCE_PATH): """Get a list of all services. :rtype: list of :class:`Service` """ query_string = self._build_query_string(search_opts) return self._list(resource_path + query_string, RESOURCE_NAME) @api_versions.wraps("1.0", "2.6") def list(self, search_opts=None): return self._do_list( search_opts=search_opts, resource_path=RESOURCE_PATH_LEGACY) @api_versions.wraps("2.7") # noqa def list(self, search_opts=None): return self._do_list( search_opts=search_opts, resource_path=RESOURCE_PATH) def _do_enable(self, host, binary, resource_path=RESOURCE_PATH): """Enable the service specified by hostname and binary.""" body = {"host": host, "binary": binary} return self._update("%s/enable" % resource_path, body) @api_versions.wraps("1.0", "2.6") def enable(self, host, binary): return self._do_enable(host, binary, RESOURCE_PATH_LEGACY) @api_versions.wraps("2.7") # noqa def enable(self, host, binary): return self._do_enable(host, binary, RESOURCE_PATH) def _do_disable(self, host, binary, resource_path=RESOURCE_PATH): """Disable the service specified by hostname and binary.""" body = {"host": host, "binary": binary} return self._update("%s/disable" % resource_path, body) @api_versions.wraps("1.0", "2.6") def disable(self, host, binary): return self._do_disable(host, binary, RESOURCE_PATH_LEGACY) @api_versions.wraps("2.7") # noqa def disable(self, host, binary): return self._do_disable(host, binary, RESOURCE_PATH) def server_api_version(self, url_append=""): """Returns the API Version supported by the server. :param url_append: String to append to url to obtain specific version :return: Returns response obj for a server that supports microversions. Returns an empty list for Kilo and prior Manila servers. """ try: return self._get_with_base_url(url_append, 'versions') except LookupError: return [] python-manilaclient-2.1.0/manilaclient/v2/share_group_types.py0000664000175000017500000001625313644133413024627 0ustar zuulzuul00000000000000# Copyright 2016 Clinton Knight # # 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. """Interface for share group types extension.""" from manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base RESOURCES_PATH = '/share-group-types' RESOURCE_PATH = '/share-group-types/%s' RESOURCE_PATH_ACTION = '/share-group-types/%s/action' RESOURCES_NAME = 'share_group_types' RESOURCE_NAME = 'share_group_type' GROUP_SPECS_RESOURCES_PATH = '/share-group-types/%s/group-specs' GROUP_SPECS_RESOURCE_PATH = '/share-group-types/%s/group-specs/%s' GROUP_SPECS_RESOURCES_NAME = 'group_specs' SG_GRADUATION_VERSION = "2.55" class ShareGroupType(common_base.Resource): """A Share Group Type is the type of share group to be created.""" def __init__(self, manager, info, loaded=False): super(ShareGroupType, self).__init__(manager, info, loaded) self._group_specs = info.get(GROUP_SPECS_RESOURCES_NAME, {}) def __repr__(self): return "" % self.name @property def is_public(self): """Provide a user-friendly accessor to share-type-access.""" return self._info.get('is_public', 'N/A') def get_keys(self, prefer_resource_data=True): """Get group specs from a share group type. :param prefer_resource_data: By default group_specs are retrieved from resource data, but user can force this method to make an API call and update the group specs in this object. :return: dict with group specs """ if prefer_resource_data: return self._group_specs else: share_group_type_id = common_base.getid(self) url = GROUP_SPECS_RESOURCES_PATH % share_group_type_id _resp, body = self.manager.api.client.get(url) self._group_specs = body.get(GROUP_SPECS_RESOURCES_NAME, {}) return self._group_specs def set_keys(self, group_specs): """Set group specs on a share group type. :param extra_specs: A dict of key/value pairs to be set on this object :return: dict with group specs """ share_group_type_id = common_base.getid(self) url = GROUP_SPECS_RESOURCES_PATH % share_group_type_id body = {GROUP_SPECS_RESOURCES_NAME: group_specs} return self.manager._create( url, body, GROUP_SPECS_RESOURCES_NAME, return_raw=True) def unset_keys(self, keys): """Unset group specs on a share group type. :param keys: A list of keys on this object to be unset :return: None if successful, else API response on failure """ share_group_type_id = common_base.getid(self) for k in keys: url = GROUP_SPECS_RESOURCE_PATH % (share_group_type_id, k) resp = self.manager._delete(url) if resp is not None: return resp class ShareGroupTypeManager(base.ManagerWithFind): """Manage :class:`ShareGroupType` resources.""" resource_class = ShareGroupType def _create_share_group_type(self, name, share_types, is_public=False, group_specs=None): """Create a share group type. :param name: Descriptive name of the share group type :param share_types: list of either instances of ShareType or text with share type UUIDs :param is_public: True to create a public share group type :param group_specs: dict containing group spec key-value pairs :rtype: :class:`ShareGroupType` """ if not share_types: raise ValueError('At least one share type must be specified when ' 'creating a share group type.') body = { 'name': name, 'is_public': is_public, 'group_specs': group_specs or {}, 'share_types': [common_base.getid(share_type) for share_type in share_types], } return self._create( RESOURCES_PATH, {RESOURCE_NAME: body}, RESOURCE_NAME) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def create(self, name, share_types, is_public=False, group_specs=None): return self._create_share_group_type(name, share_types, is_public, group_specs) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def create(self, name, share_types, is_public=False, group_specs=None): return self._create_share_group_type(name, share_types, is_public, group_specs) def _get_share_group_type(self, share_group_type="default"): """Get a specific share group type. :param share_group_type: either instance of ShareGroupType, or text with UUID, or 'default' :rtype: :class:`ShareGroupType` """ share_group_type_id = common_base.getid(share_group_type) url = RESOURCE_PATH % share_group_type_id return self._get(url, RESOURCE_NAME) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def get(self, share_group_type="default"): return self._get_share_group_type(share_group_type) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def get(self, share_group_type="default"): return self._get_share_group_type(share_group_type) def _list_share_group_types(self, show_all=True, search_opts=None): """Get a list of all share group types. :rtype: list of :class:`ShareGroupType`. """ query_string = '?is_public=all' if show_all else '' url = RESOURCES_PATH + query_string return self._list(url, RESOURCES_NAME) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def list(self, show_all=True, search_opts=None): return self._list_share_group_types(show_all, search_opts) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def list(self, show_all=True, search_opts=None): return self._list_share_group_types(show_all, search_opts) def _delete_share_group_type(self, share_group_type): """Delete a specific share group type. :param share_group_type: either instance of ShareGroupType, or text with UUID """ share_group_type_id = common_base.getid(share_group_type) url = RESOURCE_PATH % share_group_type_id self._delete(url) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def delete(self, share_group_type): self._delete_share_group_type(share_group_type) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def delete(self, share_group_type): self._delete_share_group_type(share_group_type) python-manilaclient-2.1.0/manilaclient/v2/availability_zones.py0000664000175000017500000000253613644133413024754 0ustar zuulzuul00000000000000# 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. from manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base RESOURCE_PATH_LEGACY = '/os-availability-zone' RESOURCE_PATH = '/availability-zones' RESOURCE_NAME = 'availability_zones' class AvailabilityZone(common_base.Resource): def __repr__(self): return "" % self.id class AvailabilityZoneManager(base.Manager): """Manage :class:`Service` resources.""" resource_class = AvailabilityZone @api_versions.wraps("1.0", "2.6") def list(self): return self._list(RESOURCE_PATH_LEGACY, RESOURCE_NAME) @api_versions.wraps("2.7") # noqa def list(self): return self._list(RESOURCE_PATH, RESOURCE_NAME) python-manilaclient-2.1.0/manilaclient/v2/client.py0000664000175000017500000003321713644133413022342 0ustar zuulzuul00000000000000# 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 debtcollector import removals from keystoneauth1 import adapter from keystoneauth1 import session from keystoneclient import client as ks_client import manilaclient from manilaclient.common import constants from manilaclient.common import httpclient from manilaclient import exceptions from manilaclient.v2 import availability_zones from manilaclient.v2 import limits from manilaclient.v2 import messages from manilaclient.v2 import quota_classes from manilaclient.v2 import quotas from manilaclient.v2 import scheduler_stats from manilaclient.v2 import security_services from manilaclient.v2 import services from manilaclient.v2 import share_access_rules from manilaclient.v2 import share_export_locations from manilaclient.v2 import share_group_snapshots from manilaclient.v2 import share_group_type_access from manilaclient.v2 import share_group_types from manilaclient.v2 import share_groups from manilaclient.v2 import share_instance_export_locations from manilaclient.v2 import share_instances from manilaclient.v2 import share_network_subnets from manilaclient.v2 import share_networks from manilaclient.v2 import share_replica_export_locations from manilaclient.v2 import share_replicas from manilaclient.v2 import share_servers from manilaclient.v2 import share_snapshot_export_locations from manilaclient.v2 import share_snapshot_instance_export_locations from manilaclient.v2 import share_snapshot_instances from manilaclient.v2 import share_snapshots from manilaclient.v2 import share_type_access from manilaclient.v2 import share_types from manilaclient.v2 import shares class Client(object): """Top-level object to access the OpenStack Manila API. Create an instance with your creds:: >>> client = Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) Or, alternatively, you can create a client instance using the keystoneauth1.session API:: >>> from keystoneclient.auth.identity import v2 >>> from keystoneauth1 import session >>> from manilaclient import client >>> auth = v2.Password(auth_url=AUTH_URL, username=USERNAME, password=PASSWORD, tenant_name=PROJECT_ID) >>> sess = session.Session(auth=auth) >>> manila = client.Client(VERSION, session=sess) Then call methods on its managers:: >>> client.shares.list() ... """ @removals.removed_kwarg( 'share_service_name', message="Please use 'service_name' instead", removal_version='2.0.0') @removals.removed_kwarg( 'proxy_tenant_id', message="This is not used anywhere", removal_version='2.0.0') @removals.removed_kwarg( 'proxy_token', message="This is not used anywhere", removal_version='2.0.0') @removals.removed_kwarg( 'os_cache', message="Please use 'use_keyring' instead", removal_version='2.0.0') @removals.removed_kwarg( 'api_key', message="Please use 'password' instead", removal_version='2.0.0') def __init__(self, username=None, api_key=None, project_id=None, auth_url=None, insecure=False, timeout=None, tenant_id=None, project_name=None, region_name=None, endpoint_type='publicURL', extensions=None, service_type=constants.V2_SERVICE_TYPE, service_name=None, retries=None, http_log_debug=False, input_auth_token=None, session=None, auth=None, cacert=None, service_catalog_url=None, user_agent='python-manilaclient', use_keyring=False, force_new_token=False, cached_token_lifetime=300, api_version=manilaclient.API_MIN_VERSION, user_id=None, user_domain_id=None, user_domain_name=None, project_domain_id=None, project_domain_name=None, cert=None, password=None, **kwargs): self.username = username self.password = password or api_key self.tenant_id = tenant_id or project_id self.tenant_name = project_name self.user_id = user_id self.project_id = project_id or tenant_id self.project_name = project_name self.user_domain_id = user_domain_id self.user_domain_name = user_domain_name self.project_domain_id = project_domain_id self.project_domain_name = project_domain_name self.endpoint_type = endpoint_type self.auth_url = auth_url self.region_name = region_name self.cacert = cacert self.cert = cert self.insecure = insecure self.use_keyring = use_keyring self.force_new_token = force_new_token self.cached_token_lifetime = cached_token_lifetime service_name = kwargs.get("share_service_name", service_name) if input_auth_token and not service_catalog_url: msg = ("For token-based authentication you should " "provide 'input_auth_token' and 'service_catalog_url'.") raise exceptions.ClientException(msg) self.project_id = tenant_id if tenant_id is not None else project_id self.keystone_client = None self.session = session # NOTE(u_glide): token authorization has highest priority. # That's why session and/or password will be ignored # if token is provided. if not input_auth_token: if session: self.keystone_client = adapter.LegacyJsonAdapter( session=session, auth=auth, interface=endpoint_type, service_type=service_type, service_name=service_name, region_name=region_name) input_auth_token = self.keystone_client.session.get_token(auth) else: self.keystone_client = self._get_keystone_client() input_auth_token = self.keystone_client.auth_token if not input_auth_token: raise RuntimeError("Not Authorized") if session and not service_catalog_url: service_catalog_url = self.keystone_client.session.get_endpoint( auth, interface=endpoint_type, service_type=service_type) elif not service_catalog_url: catalog = self.keystone_client.service_catalog.get_endpoints( service_type) for catalog_entry in catalog.get(service_type, []): if (catalog_entry.get("interface") == ( endpoint_type.lower().split("url")[0]) or catalog_entry.get(endpoint_type)): if (region_name and not region_name == ( catalog_entry.get( "region", catalog_entry.get("region_id")))): continue service_catalog_url = catalog_entry.get( "url", catalog_entry.get(endpoint_type)) break if not service_catalog_url: raise RuntimeError("Could not find Manila endpoint in catalog") self.api_version = api_version self.client = httpclient.HTTPClient(service_catalog_url, input_auth_token, user_agent, insecure=insecure, cacert=cacert, timeout=timeout, retries=retries, http_log_debug=http_log_debug, api_version=self.api_version) self.availability_zones = availability_zones.AvailabilityZoneManager( self) self.limits = limits.LimitsManager(self) self.messages = messages.MessageManager(self) self.services = services.ServiceManager(self) self.security_services = security_services.SecurityServiceManager(self) self.share_networks = share_networks.ShareNetworkManager(self) self.share_network_subnets = ( share_network_subnets.ShareNetworkSubnetManager(self)) self.quota_classes = quota_classes.QuotaClassSetManager(self) self.quotas = quotas.QuotaSetManager(self) self.shares = shares.ShareManager(self) self.share_export_locations = ( share_export_locations.ShareExportLocationManager(self)) self.share_groups = share_groups.ShareGroupManager(self) self.share_group_snapshots = ( share_group_snapshots.ShareGroupSnapshotManager(self)) self.share_group_type_access = ( share_group_type_access.ShareGroupTypeAccessManager(self)) self.share_group_types = share_group_types.ShareGroupTypeManager(self) self.share_instances = share_instances.ShareInstanceManager(self) self.share_instance_export_locations = ( share_instance_export_locations.ShareInstanceExportLocationManager( self)) self.share_snapshots = share_snapshots.ShareSnapshotManager(self) self.share_snapshot_instances = ( share_snapshot_instances.ShareSnapshotInstanceManager(self)) self.share_snapshot_export_locations = ( share_snapshot_export_locations.ShareSnapshotExportLocationManager( self)) self.share_snapshot_instance_export_locations = ( share_snapshot_instance_export_locations. ShareSnapshotInstanceExportLocationManager(self)) self.share_types = share_types.ShareTypeManager(self) self.share_type_access = share_type_access.ShareTypeAccessManager(self) self.share_servers = share_servers.ShareServerManager(self) self.share_replicas = share_replicas.ShareReplicaManager(self) self.share_replica_export_locations = ( share_replica_export_locations.ShareReplicaExportLocationManager( self)) self.pools = scheduler_stats.PoolManager(self) self.share_access_rules = ( share_access_rules.ShareAccessRuleManager(self)) self._load_extensions(extensions) def _load_extensions(self, extensions): if not extensions: return for extension in extensions: if extension.manager_class: setattr(self, extension.name, extension.manager_class(self)) @removals.remove( message="authenticate() method is deprecated. Client automatically " "makes authentication call in the constructor.", removal_version='2.0.0') def authenticate(self): """Authenticate against the server. Normally this is called automatically when you first access the API, but you can call this method to force authentication right now. Returns on success; raises :exc:`exceptions.Unauthorized` if the credentials are wrong. """ pass def _get_keystone_client(self): # First create a Keystone session if self.insecure: verify = False else: verify = self.cacert or True ks_session = session.Session(verify=verify, cert=self.cert) # Discover the supported keystone versions using the given url ks_discover = session.discover.Discover(ks_session, self.auth_url) # Inspect the auth_url to see the supported version. If both v3 and v2 # are supported, then use the highest version if possible. v2_auth_url = ks_discover.url_for('v2.0') v3_auth_url = ks_discover.url_for('v3.0') if v3_auth_url: keystone_client = ks_client.Client( session=ks_session, version=(3, 0), auth_url=v3_auth_url, username=self.username, password=self.password, user_id=self.user_id, user_domain_name=self.user_domain_name, user_domain_id=self.user_domain_id, project_id=self.project_id or self.tenant_id, project_name=self.project_name, project_domain_name=self.project_domain_name, project_domain_id=self.project_domain_id, region_name=self.region_name) elif v2_auth_url: keystone_client = ks_client.Client( session=ks_session, version=(2, 0), auth_url=v2_auth_url, username=self.username, password=self.password, tenant_id=self.tenant_id, tenant_name=self.tenant_name, region_name=self.region_name, cert=self.cert, use_keyring=self.use_keyring, force_new_token=self.force_new_token, stale_duration=self.cached_token_lifetime) else: raise exceptions.CommandError( 'Unable to determine the Keystone version to authenticate ' 'with using the given auth_url.') keystone_client.authenticate() return keystone_client python-manilaclient-2.1.0/manilaclient/v2/share_replicas.py0000664000175000017500000001507313644133413024050 0ustar zuulzuul00000000000000# Copyright 2015 Chuck Fouts. # 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 api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base RESOURCES_PATH = '/share-replicas' RESOURCE_PATH = '/share-replicas/%s' RESOURCE_PATH_ACTION = '/share-replicas/%s/action' RESOURCES_NAME = 'share_replicas' RESOURCE_NAME = 'share_replica' class ShareReplica(common_base.Resource): """A replica is 'mirror' instance of a share at some point in time.""" def __repr__(self): return "" % self.id def resync(self): """Re-sync this replica.""" self.manager.resync(self) def promote(self): """Promote this replica to be the 'active' replica.""" self.manager.promote(self) def reset_state(self, state): """Update replica's 'status' attr with the provided state.""" self.manager.reset_state(self, state) def reset_replica_state(self, replica_state): """Update replica's 'replica_state' attr with the provided state.""" self.manager.reset_replica_state(self, replica_state) class ShareReplicaManager(base.ManagerWithFind): """Manage :class:`ShareReplica` resources.""" resource_class = ShareReplica @api_versions.wraps("2.11") @api_versions.experimental_api def get(self, replica): """Get a share replica. :param replica: either replica object or its UUID. :rtype: :class:`ShareReplica` """ replica_id = common_base.getid(replica) return self._get(RESOURCE_PATH % replica_id, RESOURCE_NAME) @api_versions.wraps("2.11") @api_versions.experimental_api def list(self, share=None, search_opts=None): """List all share replicas or list replicas belonging to a share. :param share: either share object or its UUID. :param search_opts: default None :rtype: list of :class:`ShareReplica` """ if share: share_id = '?share_id=' + common_base.getid(share) url = RESOURCES_PATH + '/detail' + share_id return self._list(url, RESOURCES_NAME) else: return self._list(RESOURCES_PATH + '/detail', RESOURCES_NAME) @api_versions.wraps("2.11") @api_versions.experimental_api def promote(self, replica): """Promote the provided replica. :param replica: either replica object or its UUID. """ return self._action('promote', replica) @api_versions.wraps("2.11") @api_versions.experimental_api def create(self, share, availability_zone=None): """Create a replica for a share. :param share: The share to create the replica of. Can be the share object or its UUID. :param availability_zone: The 'availability_zone' object or its UUID. """ share_id = common_base.getid(share) body = {'share_id': share_id} if availability_zone: body['availability_zone'] = common_base.getid(availability_zone) return self._create(RESOURCES_PATH, {RESOURCE_NAME: body}, RESOURCE_NAME) @api_versions.wraps("2.11") @api_versions.experimental_api def delete(self, replica, force=False): """Delete a replica. :param replica: either replica object or its UUID. :param force: optional 'force' flag. """ self._do_delete(replica, force=force) @api_versions.wraps("2.11") @api_versions.experimental_api def reset_state(self, replica, state): """Reset the 'status' attr of the replica. :param replica: either replica object or its UUID. :param state: state to set the replica's 'status' attr to. """ return self._do_reset_state(replica, state, "reset_status") @api_versions.wraps("2.11") @api_versions.experimental_api def reset_replica_state(self, replica, state): """Reset the 'replica_state' attr of the replica. :param replica: either replica object or its UUID. :param state: state to set the replica's 'replica_state' attr to. """ return self._do_reset_state(replica, state, "reset_replica_state") @api_versions.wraps("2.11") @api_versions.experimental_api def resync(self, replica): """Re-sync the provided replica. :param replica: either replica object or its UUID. """ return self._action('resync', replica) def _action(self, action, replica, info=None, **kwargs): """Perform a share replica 'action'. :param action: text with action name. :param replica: either replica object or its UUID. :param info: dict with data for specified 'action'. :param kwargs: dict with data to be provided for action hooks. """ body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) replica_id = common_base.getid(replica) url = RESOURCE_PATH_ACTION % replica_id return self.api.client.post(url, body=body) def _do_delete(self, replica, force=False): """Delete a share replica. :param replica: either share replica object or its UUID. """ replica_id = common_base.getid(replica) url = RESOURCE_PATH % replica_id if force: self._do_force_delete(replica_id) else: self._delete(url) def _do_force_delete(self, replica, action_name="force_delete"): """Delete a share replica forcibly - share status will be avoided. :param replica: either share replica object or its UUID. """ return self._action(action_name, common_base.getid(replica)) def _do_reset_state(self, replica, state, action_name): """Update the provided share replica with the provided state. :param replica: either share replica object or its UUID. :param state: text with new state to set for share. """ attr = action_name.split("reset_")[1] return self._action(action_name, replica, {attr: state}) python-manilaclient-2.1.0/manilaclient/v2/share_snapshot_export_locations.py0000664000175000017500000000357413644133413027564 0ustar zuulzuul00000000000000# 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 manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base class ShareSnapshotExportLocation(common_base.Resource): """Represent an export location snapshot of a snapshot.""" def __repr__(self): return "" % self.id def __getitem__(self, key): return self._info[key] class ShareSnapshotExportLocationManager(base.ManagerWithFind): """Manage :class:`ShareSnapshotExportLocation` resources.""" resource_class = ShareSnapshotExportLocation @api_versions.wraps("2.32") def list(self, snapshot=None, search_opts=None): return self._list("/snapshots/%s/export-locations" % common_base.getid(snapshot), 'share_snapshot_export_locations') @api_versions.wraps("2.32") def get(self, export_location, snapshot=None): params = { "snapshot_id": common_base.getid(snapshot), "export_location_id": common_base.getid(export_location), } return self._get("/snapshots/%(snapshot_id)s/export-locations/" "%(export_location_id)s" % params, "share_snapshot_export_location") python-manilaclient-2.1.0/manilaclient/v2/share_networks.py0000664000175000017500000002047313644133413024122 0ustar zuulzuul00000000000000# 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 manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base from manilaclient import exceptions RESOURCES_PATH = '/share-networks' RESOURCE_PATH = "/share-networks/%s" RESOURCE_NAME = 'share_network' RESOURCES_NAME = 'share_networks' class ShareNetwork(common_base.Resource): """Network info for Manila shares.""" def __repr__(self): return "" % self.id def update(self, **kwargs): """Update this share network.""" return self.manager.update(self, **kwargs) def delete(self): """Delete this share network.""" self.manager.delete(self) class ShareNetworkManager(base.ManagerWithFind): """Manage :class:`ShareNetwork` resources.""" resource_class = ShareNetwork @api_versions.wraps("1.0", "2.25") def create(self, neutron_net_id=None, neutron_subnet_id=None, nova_net_id=None, name=None, description=None): """Create share network. :param neutron_net_id: ID of Neutron network :param neutron_subnet_id: ID of Neutron subnet :param nova_net_id: ID of Nova network :param name: share network name :param description: share network description :rtype: :class:`ShareNetwork` """ values = {} if neutron_net_id: values['neutron_net_id'] = neutron_net_id if neutron_subnet_id: values['neutron_subnet_id'] = neutron_subnet_id if nova_net_id: values['nova_net_id'] = nova_net_id if name: values['name'] = name if description: values['description'] = description body = {RESOURCE_NAME: values} return self._create(RESOURCES_PATH, body, RESOURCE_NAME) @api_versions.wraps("2.26", "2.50") # noqa def create(self, neutron_net_id=None, neutron_subnet_id=None, name=None, description=None): """Create share network. :param neutron_net_id: ID of Neutron network :param neutron_subnet_id: ID of Neutron subnet :param name: share network name :param description: share network description :rtype: :class:`ShareNetwork` """ values = {} if neutron_net_id: values['neutron_net_id'] = neutron_net_id if neutron_subnet_id: values['neutron_subnet_id'] = neutron_subnet_id if name: values['name'] = name if description: values['description'] = description body = {RESOURCE_NAME: values} return self._create(RESOURCES_PATH, body, RESOURCE_NAME) @api_versions.wraps("2.51") # noqa def create(self, neutron_net_id=None, neutron_subnet_id=None, name=None, description=None, availability_zone=None): values = {} if neutron_net_id: values['neutron_net_id'] = neutron_net_id if neutron_subnet_id: values['neutron_subnet_id'] = neutron_subnet_id if name: values['name'] = name if description: values['description'] = description if availability_zone: values['availability_zone'] = availability_zone body = {RESOURCE_NAME: values} return self._create(RESOURCES_PATH, body, RESOURCE_NAME) def add_security_service(self, share_network, security_service): """Associate given security service with a share network. :param share_network: share network name, id or ShareNetwork instance :param security_service: name, id or SecurityService instance :rtype: :class:`ShareNetwork` """ body = { 'add_security_service': { 'security_service_id': common_base.getid(security_service), }, } return self._create( RESOURCE_PATH % common_base.getid(share_network) + '/action', body, RESOURCE_NAME, ) def remove_security_service(self, share_network, security_service): """Dissociate security service from a share network. :param share_network: share network name, id or ShareNetwork instance :param security_service: name, id or SecurityService instance :rtype: :class:`ShareNetwork` """ body = { 'remove_security_service': { 'security_service_id': common_base.getid(security_service), }, } return self._create( RESOURCE_PATH % common_base.getid(share_network) + '/action', body, RESOURCE_NAME, ) def get(self, share_network): """Get a share network. :param policy: share network to get. :rtype: :class:`NetworkInfo` """ return self._get(RESOURCE_PATH % common_base.getid(share_network), RESOURCE_NAME) @api_versions.wraps("1.0", "2.25") def update(self, share_network, neutron_net_id=None, neutron_subnet_id=None, nova_net_id=None, name=None, description=None): """Updates a share network. :param share_network: share network to update. :rtype: :class:`ShareNetwork` """ values = {} if neutron_net_id is not None: values['neutron_net_id'] = neutron_net_id if neutron_subnet_id is not None: values['neutron_subnet_id'] = neutron_subnet_id if nova_net_id is not None: values['nova_net_id'] = nova_net_id if name is not None: values['name'] = name if description is not None: values['description'] = description for k, v in values.items(): if v == '': values[k] = None if not values: msg = "Must specify fields to be updated" raise exceptions.CommandError(msg) body = {RESOURCE_NAME: values} return self._update(RESOURCE_PATH % common_base.getid(share_network), body, RESOURCE_NAME) @api_versions.wraps("2.26") # noqa def update(self, share_network, neutron_net_id=None, neutron_subnet_id=None, name=None, description=None): """Updates a share network. :param share_network: share network to update. :rtype: :class:`ShareNetwork` """ values = {} if neutron_net_id is not None: values['neutron_net_id'] = neutron_net_id if neutron_subnet_id is not None: values['neutron_subnet_id'] = neutron_subnet_id if name is not None: values['name'] = name if description is not None: values['description'] = description for k, v in values.items(): if v == '': values[k] = None if not values: msg = "Must specify fields to be updated" raise exceptions.CommandError(msg) body = {RESOURCE_NAME: values} return self._update(RESOURCE_PATH % common_base.getid(share_network), body, RESOURCE_NAME) def delete(self, share_network): """Delete a share network. :param share_network: share network to be deleted. """ self._delete(RESOURCE_PATH % common_base.getid(share_network)) def list(self, detailed=True, search_opts=None): """Get a list of all share network. :rtype: list of :class:`NetworkInfo` """ query_string = self._build_query_string(search_opts) if detailed: path = RESOURCES_PATH + "/detail" + query_string else: path = RESOURCES_PATH + query_string return self._list(path, RESOURCES_NAME) python-manilaclient-2.1.0/manilaclient/v2/__init__.py0000664000175000017500000000125113644133413022614 0ustar zuulzuul00000000000000# Copyright 2015 Chuck Fouts # # 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.v2.client import Client # noqa python-manilaclient-2.1.0/manilaclient/v2/security_services.py0000664000175000017500000001271013644133413024631 0ustar zuulzuul00000000000000# 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 manilaclient import base from manilaclient.common.apiclient import base as common_base from manilaclient import exceptions RESOURCES_PATH = '/security-services' RESOURCE_PATH = "/security-services/%s" RESOURCE_NAME = 'security_service' RESOURCES_NAME = 'security_services' class SecurityService(common_base.Resource): """Security service for Manila shares.""" def __repr__(self): return "" % self.id def update(self, **kwargs): """Update this security service.""" return self.manager.update(self, **kwargs) def delete(self): """"Delete this security service.""" self.manager.delete(self) class SecurityServiceManager(base.ManagerWithFind): """Manage :class:`SecurityService` resources.""" resource_class = SecurityService def create(self, type, dns_ip=None, ou=None, server=None, domain=None, user=None, password=None, name=None, description=None): """Create security service for NAS. :param type: security service type - 'ldap', 'kerberos' or 'active_directory' :param dns_ip: dns ip address used inside tenant's network :param ou: security service organizational unit :param server: security service server ip address or hostname :param domain: security service domain :param user: security identifier used by tenant :param password: password used by user :param name: security service name :param description: security service description :rtype: :class:`SecurityService` """ values = {'type': type} if dns_ip: values['dns_ip'] = dns_ip if ou: values['ou'] = ou if server: values['server'] = server if domain: values['domain'] = domain if user: values['user'] = user if password: values['password'] = password if name: values['name'] = name if description: values['description'] = description body = {RESOURCE_NAME: values} return self._create(RESOURCES_PATH, body, RESOURCE_NAME) def get(self, security_service): """Get a security service info. :param security_service: security service to get. :rtype: :class:`SecurityService` """ return self._get( RESOURCE_PATH % common_base.getid(security_service), RESOURCE_NAME, ) def update(self, security_service, dns_ip=None, ou=None, server=None, domain=None, password=None, user=None, name=None, description=None): """Updates a security service. :param security_service: security service to update. :param dns_ip: dns ip address used inside tenant's network :param ou: security service organizational unit :param server: security service server ip address or hostname :param domain: security service domain :param user: security identifier used by tenant :param password: password used by user :param name: security service name :param description: security service description :rtype: :class:`SecurityService` """ values = {} if dns_ip is not None: values['dns_ip'] = dns_ip if ou is not None: values['ou'] = ou if server is not None: values['server'] = server if domain is not None: values['domain'] = domain if user is not None: values['user'] = user if password is not None: values['password'] = password if name is not None: values['name'] = name if description is not None: values['description'] = description for k, v in values.items(): if v == '': values[k] = None if not values: msg = "Must specify fields to be updated" raise exceptions.CommandError(msg) body = {RESOURCE_NAME: values} return self._update( RESOURCE_PATH % common_base.getid(security_service), body, RESOURCE_NAME, ) def delete(self, security_service): """Delete a security service. :param security_service: security service to be deleted. """ self._delete(RESOURCE_PATH % common_base.getid(security_service)) def list(self, detailed=True, search_opts=None): """Get a list of all security services. :rtype: list of :class:`SecurityService` """ query_string = self._build_query_string(search_opts) if detailed: path = RESOURCES_PATH + "/detail" + query_string else: path = RESOURCES_PATH + query_string return self._list(path, RESOURCES_NAME) python-manilaclient-2.1.0/manilaclient/v2/scheduler_stats.py0000664000175000017500000000307513644133413024257 0ustar zuulzuul00000000000000# 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 manilaclient import base from manilaclient.common.apiclient import base as common_base RESOURCES_PATH = '/scheduler-stats/pools' RESOURCES_NAME = 'pools' class Pool(common_base.Resource): def __repr__(self): return "" % self.name class PoolManager(base.Manager): """Manage :class:`Pool` resources.""" resource_class = Pool def list(self, detailed=True, search_opts=None): """Get a list of pools. :rtype: list of :class:`Pool` """ query_string = self._build_query_string(search_opts) if detailed: path = '%(resources_path)s/detail%(query)s' % { 'resources_path': RESOURCES_PATH, 'query': query_string } else: path = '%(resources_path)s%(query)s' % { 'resources_path': RESOURCES_PATH, 'query': query_string } return self._list(path, RESOURCES_NAME) python-manilaclient-2.1.0/manilaclient/v2/share_instance_export_locations.py0000664000175000017500000000404413644133413027522 0ustar zuulzuul00000000000000# 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 manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base class ShareInstanceExportLocation(common_base.Resource): """Resource class for a share export location.""" def __repr__(self): return "" % self.id def __getitem__(self, key): return self._info[key] class ShareInstanceExportLocationManager(base.ManagerWithFind): """Manage :class:`ShareInstanceExportLocation` resources.""" resource_class = ShareInstanceExportLocation @api_versions.wraps("2.9") def list(self, share_instance, search_opts=None): """List all share export locations.""" share_instance_id = common_base.getid(share_instance) return self._list( "/share_instances/%s/export_locations" % share_instance_id, "export_locations") @api_versions.wraps("2.9") def get(self, share_instance, export_location): """Get a share export location.""" share_instance_id = common_base.getid(share_instance) export_location_id = common_base.getid(export_location) return self._get( ("/share_instances/%(share_instance_id)s/export_locations/" "%(export_location_id)s") % { "share_instance_id": share_instance_id, "export_location_id": export_location_id, }, "export_location") python-manilaclient-2.1.0/manilaclient/v2/shell.py0000664000175000017500000054312113644133413022173 0ustar zuulzuul00000000000000# 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 __future__ import print_function from operator import xor import os import sys import time from oslo_utils import strutils import six from manilaclient import api_versions from manilaclient.common.apiclient import utils as apiclient_utils from manilaclient.common import cliutils from manilaclient.common import constants from manilaclient import exceptions def _poll_for_status(poll_fn, obj_id, action, final_ok_states, poll_period=5, show_progress=True): """Block while action is performed, periodically printing progress.""" def print_progress(progress): if show_progress: msg = ('\rInstance %(action)s... %(progress)s%% complete' % dict(action=action, progress=progress)) else: msg = '\rInstance %(action)s...' % dict(action=action) sys.stdout.write(msg) sys.stdout.flush() print() while True: obj = poll_fn(obj_id) status = obj.status.lower() progress = getattr(obj, 'progress', None) or 0 if status in final_ok_states: print_progress(100) print("\nFinished") break elif status == "error": print("\nError %(action)s instance" % {'action': action}) break else: print_progress(progress) time.sleep(poll_period) def _find_share(cs, share): """Get a share by ID.""" return apiclient_utils.find_resource(cs.shares, share) @api_versions.wraps("1.0", "2.8") def _print_share(cs, share): info = share._info.copy() info.pop('links', None) # NOTE(vponomaryov): remove deprecated single field 'export_location' and # leave only list field 'export_locations'. Also, transform the latter to # text with new line separators to make it pretty in CLI. # It will look like following: # +-------------------+--------------------------------------------+ # | Property | Value | # +-------------------+--------------------------------------------+ # | status | available | # | export_locations | 1.2.3.4:/f/o/o | # | | 5.6.7.8:/b/a/r | # | | 9.10.11.12:/q/u/u/z | # | id | d778d2ee-b6bb-4c5f-9f5d-6f3057d549b1 | # | size | 1 | # | share_proto | NFS | # +-------------------+--------------------------------------------+ if info.get('export_locations'): info.pop('export_location', None) info['export_locations'] = "\n".join(info['export_locations']) # No need to print both volume_type and share_type to CLI if 'volume_type' in info and 'share_type' in info: info.pop('volume_type', None) cliutils.print_dict(info) @api_versions.wraps("2.9") # noqa def _print_share(cs, share): info = share._info.copy() info.pop('links', None) # NOTE(vponomaryov): remove deprecated single field 'export_location' and # leave only list field 'export_locations'. Also, transform the latter to # text with new line separators to make it pretty in CLI. # It will look like following: # +-------------------+--------------------------------------------+ # | Property | Value | # +-------------------+--------------------------------------------+ # | status | available | # | export_locations | | # | | uuid = FOO-UUID | # | | path = 5.6.7.8:/foo/export/location/path | # | | | # | | uuid = BAR-UUID | # | | path = 5.6.7.8:/bar/export/location/path | # | | | # | id | d778d2ee-b6bb-4c5f-9f5d-6f3057d549b1 | # | size | 1 | # | share_proto | NFS | # +-------------------+--------------------------------------------+ if info.get('export_locations'): info['export_locations'] = ( cliutils.transform_export_locations_to_string_view( info['export_locations'])) # No need to print both volume_type and share_type to CLI if 'volume_type' in info and 'share_type' in info: info.pop('volume_type', None) cliutils.print_dict(info) def _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): info = instance._info.copy() info.pop('links', None) if info.get('export_locations'): info['export_locations'] = ( cliutils.transform_export_locations_to_string_view( info['export_locations'])) cliutils.print_dict(info) def _find_share_replica(cs, replica): """Get a replica by ID.""" return apiclient_utils.find_resource(cs.share_replicas, replica) @api_versions.wraps("2.11", "2.46") def _print_share_replica(cs, replica): info = replica._info.copy() info.pop('links', None) cliutils.print_dict(info) @api_versions.wraps("2.47") # noqa def _print_share_replica(cs, replica): info = replica._info.copy() info.pop('links', None) if info.get('export_locations'): info['export_locations'] = ( cliutils.transform_export_locations_to_string_view( info['export_locations'])) cliutils.print_dict(info) @api_versions.wraps("2.31") def _find_share_group(cs, share_group): """Get a share group ID.""" return apiclient_utils.find_resource(cs.share_groups, share_group) def _print_share_group(cs, share_group): info = share_group._info.copy() info.pop('links', None) if info.get('share_types'): info['share_types'] = "\n".join(info['share_types']) cliutils.print_dict(info) @api_versions.wraps("2.31") def _find_share_group_snapshot(cs, share_group_snapshot): """Get a share group snapshot by name or ID.""" return apiclient_utils.find_resource( cs.share_group_snapshots, share_group_snapshot) def _print_share_group_snapshot(cs, share_group_snapshot): info = share_group_snapshot._info.copy() info.pop('links', None) info.pop('members', None) cliutils.print_dict(info) def _print_share_group_snapshot_members(cs, share_group_snapshot): info = share_group_snapshot._info.copy() cliutils.print_dict(info.get('members', {})) def _find_share_snapshot(cs, snapshot): """Get a snapshot by ID.""" return apiclient_utils.find_resource(cs.share_snapshots, snapshot) def _print_share_snapshot(cs, snapshot): info = snapshot._info.copy() info.pop('links', None) if info.get('export_locations'): info['export_locations'] = ( cliutils.transform_export_locations_to_string_view( info['export_locations'])) cliutils.print_dict(info) def _quota_set_pretty_show(quotas): """Convert quotas object to dict and display.""" new_quotas = {} for quota_k, quota_v in sorted(quotas.to_dict().items()): if isinstance(quota_v, dict): quota_v = '\n'.join( ['%s = %s' % (k, v) for k, v in sorted(quota_v.items())]) new_quotas[quota_k] = quota_v cliutils.print_dict(new_quotas) def _find_share_snapshot_instance(cs, snapshot_instance): """Get a share snapshot instance by ID.""" return apiclient_utils.find_resource( cs.share_snapshot_instances, snapshot_instance) def _find_share_network(cs, share_network): """Get a share network by ID or name.""" return apiclient_utils.find_resource(cs.share_networks, share_network) def _find_security_service(cs, security_service): """Get a security service by ID or name.""" return apiclient_utils.find_resource(cs.security_services, security_service) def _find_share_server(cs, share_server): """Get a share server by ID.""" return apiclient_utils.find_resource(cs.share_servers, share_server) def _find_message(cs, message): """Get a message by ID.""" return apiclient_utils.find_resource(cs.messages, message) def _translate_keys(collection, convert): for item in collection: keys = item.__dict__ for from_key, to_key in convert: if from_key in keys and to_key not in keys: setattr(item, to_key, item._info[from_key]) def _extract_metadata(args): return _extract_key_value_options(args, 'metadata') def _extract_extra_specs(args): return _extract_key_value_options(args, 'extra_specs') def _extract_group_specs(args): return _extract_key_value_options(args, 'group_specs') def _extract_key_value_options(args, option_name): result_dict = {} duplicate_options = [] options = getattr(args, option_name, None) if options: for option in options: # unset doesn't require a val, so we have the if/else if '=' in option: (key, value) = option.split('=', 1) else: key = option value = None if key not in result_dict: result_dict[key] = value else: duplicate_options.append(key) if len(duplicate_options) > 0: duplicate_str = ', '.join(duplicate_options) msg = "Following options were duplicated: %s" % duplicate_str raise exceptions.CommandError(msg) return result_dict def _split_columns(columns, title=True): if title: list_of_keys = list(map(lambda x: x.strip().title(), columns.split(","))) else: list_of_keys = list(map(lambda x: x.strip().lower(), columns.split(","))) return list_of_keys @api_versions.wraps("2.0") def do_api_version(cs, args): """Display the API version information.""" columns = ['ID', 'Status', 'Version', 'Min_version'] column_labels = ['ID', 'Status', 'Version', 'Minimum Version'] response = cs.services.server_api_version() cliutils.print_list(response, columns, field_labels=column_labels) def do_endpoints(cs, args): """Discover endpoints that get returned from the authenticate services.""" catalog = cs.keystone_client.service_catalog.catalog for e in catalog.get('serviceCatalog', catalog.get('catalog')): cliutils.print_dict(e['endpoints'][0], e['name']) def do_credentials(cs, args): """Show user credentials returned from auth.""" catalog = cs.keystone_client.service_catalog.catalog cliutils.print_dict(catalog['user'], "User Credentials") if not catalog['version'] == 'v3': data = catalog['token'] else: data = { 'issued_at': catalog['issued_at'], 'expires': catalog['expires_at'], 'id': catalog['auth_token'], 'audit_ids': catalog['audit_ids'], 'tenant': catalog['project'], } cliutils.print_dict(data, "Token") _quota_resources = [ 'shares', 'snapshots', 'gigabytes', 'snapshot_gigabytes', 'share_networks', 'share_replicas', 'replica_gigabytes' ] def _quota_class_update(manager, identifier, args): updates = {} for resource in _quota_resources: val = getattr(args, resource, None) if val is not None: updates[resource] = val if updates: manager.update(identifier, **updates) @cliutils.arg( '--tenant-id', metavar='', default=None, help='ID of tenant 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 tenant/user.""" project = args.tenant_id or cs.keystone_client.project_id kwargs = { "tenant_id": project, "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', metavar='', default=None, help='ID of tenant to list the default quotas for.') def do_quota_defaults(cs, args): """List the default quotas for a tenant.""" project = args.tenant_id or cs.keystone_client.project_id _quota_set_pretty_show(cs.quotas.defaults(project)) @cliutils.arg( 'tenant_id', metavar='', help='UUID of tenant 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( '--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.tenant_id, "user_id": args.user_id, "shares": args.shares, "gigabytes": args.gigabytes, "snapshots": args.snapshots, "snapshot_gigabytes": args.snapshot_gigabytes, "share_networks": args.share_networks, "force": args.force, } if args.share_type is not None: if cs.api_version < api_versions.APIVersion("2.39"): raise exceptions.CommandError( "'share type' quotas are available only starting with " "'2.39' API microversion.") kwargs["share_type"] = args.share_type if args.share_groups is not None or args.share_group_snapshots is not None: if cs.api_version < api_versions.APIVersion("2.40"): raise exceptions.CommandError( "'share group' quotas are available only starting with " "'2.40' API microversion.") elif args.share_type is not None: raise exceptions.CommandError( "Share type quotas cannot be used to constrain share groups.") kwargs["share_groups"] = args.share_groups kwargs["share_group_snapshots"] = args.share_group_snapshots if args.share_replicas is not None or args.replica_gigabytes is not None: if cs.api_version < api_versions.APIVersion("2.53"): raise exceptions.CommandError( "'share replica' quotas are available only starting with " "'2.53' API microversion.") kwargs["share_replicas"] = args.share_replicas kwargs["replica_gigabytes"] = args.replica_gigabytes cs.quotas.update(**kwargs) @cliutils.arg( '--tenant-id', metavar='', help='ID of tenant 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 tenant/user or tenant/share-type. The quota will revert back to default (Admin only). """ project_id = args.tenant_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-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') def do_quota_class_update(cs, args): """Update the quotas for a quota class (Admin only).""" if args.share_replicas is not None or args.replica_gigabytes is not None: if cs.api_version < api_versions.APIVersion("2.53"): raise exceptions.CommandError( "'share replica' quotas are available only starting with " "'2.53' API microversion.") _quota_class_update(cs.quota_classes, args.class_name, args) def do_absolute_limits(cs, args): """Print a list of absolute limits for a user.""" limits = cs.limits.get().absolute columns = ['Name', 'Value'] cliutils.print_list(limits, columns) @cliutils.arg( '--columns', metavar='', 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', metavar='', action='single_alias', help='Optional snapshot ID 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 tenants 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.service_type('sharev2') def do_create(cs, args): """Creates a new share (NFS, CIFS, CephFS, GlusterFS, HDFS or MAPRFS).""" share_metadata = None if args.metadata is not None: share_metadata = _extract_metadata(args) share_group = None if args.share_group: share_group = _find_share_group(cs, args.share_group).id share_network = None if args.share_network: share_network = _find_share_network(cs, args.share_network) share = cs.shares.create(args.share_protocol, args.size, args.snapshot_id, args.name, args.description, metadata=share_metadata, share_network=share_network, share_type=args.share_type, is_public=args.public, availability_zone=args.availability_zone, share_group_id=share_group) _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', 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 (key is only necessary on unset).') def do_metadata(cs, args): """Set or delete metadata on a share.""" share = _find_share(cs, args.share) metadata = _extract_metadata(args) if args.action == 'set': cs.shares.set_metadata(share, metadata) elif args.action == 'unset': cs.shares.delete_metadata(share, sorted(list(metadata), reverse=True)) @cliutils.arg( 'share', metavar='', help='Name or ID of the share.') def do_metadata_show(cs, args): """Show metadata of given share.""" share = _find_share(cs, args.share) metadata = cs.shares.get_metadata(share)._info cliutils.print_dict(metadata, 'Property') @cliutils.arg( 'share', metavar='', 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 tenants 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)") def do_manage(cs, args): """Manage share not handled by Manila (Admin only).""" driver_options = _extract_key_value_options(args, 'driver_options') if cs.api_version.matches(api_versions.APIVersion("2.49"), api_versions.APIVersion()): share = cs.shares.manage( args.service_host, args.protocol, args.export_path, driver_options=driver_options, share_type=args.share_type, name=args.name, description=args.description, is_public=args.public, share_server_id=args.share_server_id) else: if args.share_server_id: raise exceptions.CommandError("Invalid parameter " "--share_server_id specified. This" " parameter is only supported on" " microversion 2.49 or newer.") share = cs.shares.manage( args.service_host, args.protocol, args.export_path, driver_options=driver_options, share_type=args.share_type, name=args.name, description=args.description, is_public=args.public) _print_share(cs, share) @api_versions.wraps("2.49") @cliutils.arg( 'host', metavar='', 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) def do_share_server_manage(cs, args): """Manage share server not handled by Manila (Admin only).""" driver_options = _extract_key_value_options(args, 'driver_options') manage_kwargs = { 'driver_options': driver_options, } if cs.api_version < api_versions.APIVersion("2.51"): if getattr(args, 'share_network_subnet'): raise exceptions.CommandError( "Share network subnet option is only available with manila " "API version >= 2.51") else: manage_kwargs['share_network_subnet_id'] = args.share_network_subnet share_server = cs.share_servers.manage( args.host, args.share_network, args.identifier, **manage_kwargs) cliutils.print_dict(share_server._info) @cliutils.arg( 'share_server_id', metavar='', 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) def do_snapshot_manage(cs, args): """Manage share snapshot not handled by Manila (Admin only).""" share_ref = _find_share(cs, args.share) driver_options = _extract_key_value_options(args, 'driver_options') share_snapshot = cs.share_snapshots.manage( share_ref, args.provider_location, driver_options=driver_options, name=args.name, description=args.description ) _print_share_snapshot(cs, share_snapshot) @cliutils.arg( 'share', metavar='', help='Name or ID of the share(s).') def do_unmanage(cs, args): """Unmanage share (Admin only).""" share_ref = _find_share(cs, args.share) share_ref.unmanage() @api_versions.wraps("2.49") @cliutils.arg( 'share_server', metavar='', nargs='+', help='ID of the share server(s).') @cliutils.arg( '--force', dest='force', action="store_true", required=False, default=False, help="Enforces the unmanage share server operation, even if the back-end " "driver does not support it.") def do_share_server_unmanage(cs, args): """Unmanage share server (Admin only).""" failure_count = 0 for server in args.share_server: try: cs.share_servers.unmanage(server, args.force) except Exception as e: failure_count += 1 print("Unmanage for share server %s failed: %s" % (server, e), file=sys.stderr) if failure_count == len(args.share_server): raise exceptions.CommandError("Unable to unmanage any of the " "specified share servers.") @api_versions.wraps("2.12") @cliutils.arg( 'snapshot', metavar='', nargs='+', help='Name or ID of the snapshot(s).') def do_snapshot_unmanage(cs, args): """Unmanage one or more share snapshots (Admin only).""" failure_count = 0 for snapshot in args.snapshot: try: snapshot_ref = _find_share_snapshot(cs, snapshot) snapshot_ref.unmanage_snapshot() except Exception as e: failure_count += 1 print("Unmanage for share snapshot %s failed: %s" % (snapshot, e), file=sys.stderr) if failure_count == len(args.snapshot): raise exceptions.CommandError("Unable to unmanage any of the " "specified snapshots.") @api_versions.wraps("2.27") @cliutils.arg( 'snapshot', metavar='', help='Name or ID of the snapshot to restore. The snapshot must be the ' 'most recent one known to manila.') def do_revert_to_snapshot(cs, args): """Revert a share to the specified snapshot.""" snapshot = _find_share_snapshot(cs, args.snapshot) share = _find_share(cs, snapshot.share_id) share.revert_to_snapshot(snapshot) @cliutils.arg( 'share', metavar='', 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.service_type('sharev2') def do_delete(cs, args): """Remove one or more shares.""" failure_count = 0 for share in args.share: try: share_ref = _find_share(cs, share) if args.share_group: share_group_id = _find_share_group(cs, args.share_group).id share_ref.delete(share_group_id=share_group_id) else: share_ref.delete() except Exception as e: failure_count += 1 print("Delete for share %s failed: %s" % (share, e), file=sys.stderr) if failure_count == len(args.share): raise exceptions.CommandError("Unable to delete any of the specified " "shares.") @cliutils.arg( 'share', metavar='', nargs='+', help='Name or ID of the share(s) to force delete.') def do_force_delete(cs, args): """Attempt force-delete of share, regardless of state (Admin only).""" failure_count = 0 for share in args.share: try: _find_share(cs, share).force_delete() except Exception as e: failure_count += 1 print("Delete for share %s failed: %s" % (share, e), file=sys.stderr) if failure_count == len(args.share): raise exceptions.CommandError("Unable to force delete any of " "specified shares.") @api_versions.wraps("1.0", "2.8") @cliutils.arg( 'share', metavar='', 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): """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) def do_access_allow(cs, args): """Allow access to a given share.""" access_metadata = None if cs.api_version.matches(api_versions.APIVersion("2.45"), api_versions.APIVersion()): access_metadata = _extract_metadata(args) elif getattr(args, 'metadata'): raise exceptions.CommandError( "Adding metadata to access rules is supported only beyond " "API version 2.45") share = _find_share(cs, args.share) access = share.allow(args.access_type, args.access_to, args.access_level, access_metadata) cliutils.print_dict(access) @api_versions.wraps("2.45") @cliutils.arg( 'access_id', metavar='', 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("Failed to remove rule %(access)s: %(reason)s." % {'access': access_id, 'reason': e}, file=sys.stderr) if failure_count == len(args.id): raise exceptions.CommandError("Unable to delete any of the specified " "snapshot rules.") @api_versions.wraps("1.0", "2.20") @cliutils.arg( 'share', metavar='', 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): """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): """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', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all tenants (Admin only).') @cliutils.arg( '--name', metavar='', type=six.text_type, default=None, help='Filter results by name.') @cliutils.arg( '--description', metavar='', type=six.text_type, default=None, help='Filter results by description. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--name~', metavar='', type=six.text_type, default=None, help='Filter results matching a share name pattern. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--description~', metavar='', type=six.text_type, 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='Key to be sorted, available keys are %(keys)s. ' 'OPTIONAL: Default=None.' % {'keys': constants.SHARE_SORT_KEY_VALUES}) @cliutils.arg( '--sort-dir', '--sort_dir', # alias metavar='', type=str, default=None, action='single_alias', help='Sort direction, available values are %(values)s. ' 'OPTIONAL: Default=None.' % {'values': constants.SORT_DIR_VALUES}) @cliutils.arg( '--snapshot', metavar='', 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-tenants'.") @cliutils.arg( '--public', dest='public', action='store_true', default=False, help="Add public shares from all tenants 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.service_type('sharev2') def do_list(cs, args): """List NAS shares with filters.""" columns = args.columns all_tenants = int(os.environ.get("ALL_TENANTS", args.all_tenants)) 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_tenants 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_tenants, 'name': args.name, 'status': args.status, 'host': args.host, 'share_network_id': share_network.id, 'snapshot_id': snapshot.id, 'share_type_id': share_type.id, 'metadata': _extract_metadata(args), 'extra_specs': _extract_extra_specs(args), 'share_server_id': args.share_server_id, 'project_id': args.project_id, 'is_public': args.public, } if cs.api_version.matches(api_versions.APIVersion("2.36"), api_versions.APIVersion()): search_opts['name~'] = getattr(args, 'name~') search_opts['description~'] = getattr(args, 'description~') search_opts['description'] = getattr(args, 'description') elif (getattr(args, 'name~') or getattr(args, 'description~') or getattr(args, 'description')): raise exceptions.CommandError( "Pattern based filtering (name~, description~ and description)" " is only available with manila API version >= 2.36") if cs.api_version.matches(api_versions.APIVersion("2.35"), api_versions.APIVersion()): search_opts['export_location'] = args.export_location elif args.export_location: raise exceptions.CommandError( "Filtering by export location is only " "available with manila API version >= 2.35") if (args.count and cs.api_version.matches( api_versions.APIVersion(), api_versions.APIVersion("2.41"))): raise exceptions.CommandError( "Display total number of shares is only " "available with manila API version >= 2.42") if share_group: search_opts['share_group_id'] = share_group.id total_count = 0 if strutils.bool_from_string(args.count, strict=True): search_opts['with_count'] = args.count shares, total_count = cs.shares.list( search_opts=search_opts, sort_key=args.sort_key, sort_dir=args.sort_dir, ) else: shares = cs.shares.list( search_opts=search_opts, sort_key=args.sort_key, sort_dir=args.sort_dir, ) # NOTE(vponomaryov): usage of 'export_location' and # 'export_locations' columns may cause scaling issue using API 2.9+ and # when lots of shares are returned. if (shares and columns is not None and 'export_location' in columns and not hasattr(shares[0], 'export_location')): # NOTE(vponomaryov): we will get here only using API 2.9+ for share in shares: els_objs = cs.share_export_locations.list(share) els = [el.to_dict()['path'] for el in els_objs] setattr(share, 'export_locations', els) setattr(share, 'export_location', els[0] if els else None) cliutils.print_list(shares, list_of_keys, sortby_index=None) if args.count: print("Shares in total: %s" % total_count) @cliutils.arg( '--share-id', '--share_id', # alias metavar='', 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): """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") def do_share_instance_force_delete(cs, args): """Force-delete the share instance, regardless of state (Admin only).""" failure_count = 0 for instance in args.instance: try: _find_share_instance(cs, instance).force_delete() except Exception as e: failure_count += 1 print("Delete for share instance %s failed: %s" % (instance, e), file=sys.stderr) if failure_count == len(args.instance): raise exceptions.CommandError("Unable to force delete any of " "specified share instances.") @cliutils.arg( 'instance', metavar='', 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', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all tenants (Admin only).') @cliutils.arg( '--name', metavar='', type=six.text_type, default=None, help='Filter results by name.') @cliutils.arg( '--description', metavar='', type=six.text_type, 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='Key to be sorted, available keys are %(keys)s. ' 'Default=None.' % {'keys': constants.SNAPSHOT_SORT_KEY_VALUES}) @cliutils.arg( '--sort-dir', '--sort_dir', # alias metavar='', type=str, default=None, action='single_alias', help='Sort direction, available values are %(values)s. ' 'OPTIONAL: Default=None.' % {'values': constants.SORT_DIR_VALUES}) @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,name".') @cliutils.arg( '--name~', metavar='', type=six.text_type, default=None, help='Filter results matching a share snapshot name pattern. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--description~', metavar='', type=six.text_type, default=None, help='Filter results matching a share snapshot description pattern. ' 'Available only for microversion >= 2.36.') def do_snapshot_list(cs, args): """List all the snapshots.""" all_tenants = int(os.environ.get("ALL_TENANTS", args.all_tenants)) 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_tenants: 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_tenants, 'name': args.name, 'status': args.status, 'share_id': share.id, 'usage': args.usage, } if cs.api_version.matches(api_versions.APIVersion("2.36"), api_versions.APIVersion()): search_opts['name~'] = getattr(args, 'name~') search_opts['description~'] = getattr(args, 'description~') search_opts['description'] = getattr(args, 'description') elif (getattr(args, 'name~') or getattr(args, 'description~') or getattr(args, 'description')): raise exceptions.CommandError( "Pattern based filtering (name~, description~ and description)" " is only available with manila API version >= 2.36") 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) @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)') 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) _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 tenants.') 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.') 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) except Exception as e: failure_count += 1 print("Delete for snapshot %s failed: %s" % ( snapshot, 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.') def do_snapshot_force_delete(cs, args): """Attempt force-deletion of one or more snapshots. Regardless of the state (Admin only). """ failure_count = 0 for snapshot in args.snapshot: try: snapshot_ref = _find_share_snapshot( cs, snapshot) cs.share_snapshots.force_delete(snapshot_ref) except Exception as e: failure_count += 1 print("Delete for snapshot %s failed: %s" % ( snapshot, e), file=sys.stderr) if failure_count == len(args.snapshot): raise exceptions.CommandError("Unable to force delete any of the " "specified snapshots.") @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( '--nova-net-id', '--nova-net_id', '--nova_net_id', '--nova_net-id', # aliases metavar='', default=None, action='single_alias', help="Nova net ID. Used to set up network for share servers. This " "option is deprecated and will be rejected in newer releases " "of OpenStack Manila.") @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 description for network used by the tenant.""" values = { 'neutron_net_id': args.neutron_net_id, 'neutron_subnet_id': args.neutron_subnet_id, 'nova_net_id': args.nova_net_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): """Create description for network used by the tenant.""" 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( '--nova-net-id', '--nova-net_id', '--nova_net_id', '--nova_net-id', # aliases metavar='', default=None, action='single_alias', help="Nova net ID. Used to set up network for share servers. This " "option is deprecated and will be rejected in newer releases " "of OpenStack Manila.") @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, 'nova_net_id': args.nova_net_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): """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): """Get a description for network used by the tenant.""" 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', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all tenants (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( '--nova-net-id', '--nova_net_id', '--nova_net-id', '--nova-net_id', # aliases metavar='', action='single_alias', default=None, help='Filter results by Nova net ID. This option is deprecated and will ' 'be rejected in newer releases of OpenStack Manila.') @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_tenants = int(os.environ.get("ALL_TENANTS", args.all_tenants)) search_opts = { 'all_tenants': all_tenants, 'project_id': args.project_id, 'name': args.name, 'created_since': args.created_since, 'created_before': args.created_before, 'nova_net_id': args.nova_net_id, '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', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all tenants (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=six.text_type, default=None, help='Filter results by name.') @cliutils.arg( '--description', metavar='', type=six.text_type, 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=six.text_type, default=None, help='Filter results matching a share network name pattern. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--description~', metavar='', type=six.text_type, default=None, help='Filter results matching a share network description pattern. ' 'Available only for microversion >= 2.36.') def do_share_network_list(cs, args): """Get a list of network info.""" all_tenants = int(os.environ.get("ALL_TENANTS", args.all_tenants)) search_opts = { 'all_tenants': all_tenants, '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 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( '--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( '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("Deletion of share network subnet %s failed: %s" % ( subnet, 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("Delete for share network %s failed: %s" % ( share_network, 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 tenant'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 tenant.") @cliutils.arg( '--password', metavar='', default=None, help="Password used by user.") @cliutils.arg( '--name', metavar='', default=None, help="Security service name.") @cliutils.arg( '--description', metavar='', default=None, help="Security service description.") def do_security_service_create(cs, args): """Create security service used by tenant.""" 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") 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 tenant'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 tenant.") @cliutils.arg( '--password', metavar='', default=None, help="Password used by user.") @cliutils.arg( '--name', metavar='', default=None, help="Security service name.") @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") 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', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all tenants (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 tenant.') @cliutils.arg( '--dns-ip', '--dns_ip', # alias metavar='', action='single_alias', default=None, help="Filter results by DNS IP address used inside tenant'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( '--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_tenants = int(os.environ.get("ALL_TENANTS", args.all_tenants)) search_opts = { 'all_tenants': all_tenants, '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 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("Delete for security service %s failed: %s" % ( security_service, 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") else: search_opts.update({'share_network_subnet': args.share_network_subnet}) fields.append("Share Network Subnet Id") 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.') def do_share_server_delete(cs, args): """Delete one or more share servers (Admin only).""" failure_count = 0 for server_id in args.id: try: id_ref = _find_share_server(cs, server_id) cs.share_servers.delete(id_ref) except Exception as e: failure_count += 1 print("Delete for share server %s failed: %s" % ( server_id, e), file=sys.stderr) if failure_count == len(args.id): raise exceptions.CommandError("Unable to delete any of the specified " "share servers.") @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("%s : %s" % (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.') def do_extend(cs, args): """Increases the size of an existing share.""" share = _find_share(cs, args.share) cs.shares.extend(share, args.new_size) @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.') 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) ############################################################################## # # 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 " "argument is not valid: %s" % six.text_type(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 = ("Argument '%s' value specified twice." % key) 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 = ("Argument '%s' is of boolean " "type and has invalid value: %s" % (key, six.text_type(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 tenants 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", six.text_type(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("Delete for share type %s failed: %s" % ( name_or_id, 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 (key is only necessary on 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 (key is only necessary on 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.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) _print_share_group(cs, share_group) @cliutils.arg( '--all-tenants', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all tenants (Admin only).') @cliutils.arg( '--name', metavar='', type=six.text_type, default=None, help='Filter results by name.') @cliutils.arg( '--description', metavar='', type=six.text_type, 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-tenants'.") @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='Key to be sorted, available keys are %(keys)s. Default=None.' % { 'keys': constants.SHARE_GROUP_SORT_KEY_VALUES}) @cliutils.arg( '--sort-dir', '--sort_dir', metavar='', type=str, default=None, action='single_alias', help='Sort direction, available values are %(values)s. ' 'OPTIONAL: Default=None.' % {'values': constants.SORT_DIR_VALUES}) @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,name".') @cliutils.arg( '--name~', metavar='', type=six.text_type, default=None, help='Filter results matching a share group name pattern. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--description~', metavar='', type=six.text_type, 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_tenants = int(os.environ.get("ALL_TENANTS", args.all_tenants)) 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_tenants, '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.service_type('sharev2') def do_share_group_delete(cs, args): """Remove one or more share groups.""" failure_count = 0 kwargs = {} if args.force is not None: kwargs['force'] = args.force for share_group in args.share_group: try: share_group_ref = _find_share_group(cs, share_group) cs.share_groups.delete(share_group_ref, **kwargs) except Exception as e: failure_count += 1 print("Delete for share group %s failed: %s" % (share_group, e), file=sys.stderr) if failure_count == len(args.share_group): raise exceptions.CommandError("Unable to delete any of the specified " "share groups.") @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.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) _print_share_group_snapshot(cs, sg_snapshot) @cliutils.arg( '--all-tenants', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all tenants (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='Key to be sorted, available keys are %(keys)s. Default=None.' % { 'keys': constants.SHARE_GROUP_SNAPSHOT_SORT_KEY_VALUES}) @cliutils.arg( '--sort-dir', '--sort_dir', metavar='', type=str, default=None, action='single_alias', help='Sort direction, available values are %(values)s. ' 'OPTIONAL: Default=None.' % {'values': constants.SORT_DIR_VALUES}) @cliutils.arg( '--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_tenants = int(os.environ.get("ALL_TENANTS", args.all_tenants)) search_opts = { 'offset': args.offset, 'limit': args.limit, 'all_tenants': all_tenants, '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.service_type('sharev2') def do_share_group_snapshot_delete(cs, args): """Remove one or more share group snapshots.""" failure_count = 0 kwargs = {} if args.force is not None: 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) except Exception as e: failure_count += 1 print("Delete for share group snapshot %s failed: %s" % ( sg_snapshot, 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 (Experimental).""" 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) @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.') @api_versions.wraps("2.11") def do_share_replica_create(cs, args): """Create a share replica (Experimental).""" share = _find_share(cs, args.share) replica = cs.share_replicas.create(share, args.availability_zone) _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 (Experimental).""" 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): """Show details about a replica (Experimental).""" 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.') @api_versions.wraps("2.11") def do_share_replica_delete(cs, args): """Remove one or more share replicas (Experimental).""" 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) except Exception as e: failure_count += 1 print("Delete for share replica %s failed: %s" % (replica, 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") def do_share_replica_promote(cs, args): """Promote specified replica to 'active' replica_state (Experimental).""" replica = _find_share_replica(cs, args.replica) cs.share_replicas.promote(replica) @api_versions.wraps("2.47") @api_versions.experimental_api @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 (Experimental).""" 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") @api_versions.experimental_api @cliutils.arg( 'replica', 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_replica_export_location_show(cs, args): """Show details of a share replica's export location (Experimental).""" 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 (Experimental).""" 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 (Experimental). """ 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 (Experimental). """ replica = _find_share_replica(cs, args.replica) cs.share_replicas.resync(replica) ############################################################################## # # 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='Key to be sorted, available keys are %(keys)s. Default=desc.' % { 'keys': constants.MESSAGE_SORT_KEY_VALUES}) @cliutils.arg( '--sort-dir', '--sort_dir', metavar='', type=str, default=None, action='single_alias', help='Sort direction, available values are %(values)s. ' 'OPTIONAL: Default=None.' % {'values': constants.SORT_DIR_VALUES}) @cliutils.arg( '--columns', metavar='', 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("Delete for message %s failed: %s" % (message, 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) python-manilaclient-2.1.0/manilaclient/v2/share_group_type_access.py0000664000175000017500000000723113644133413025761 0ustar zuulzuul00000000000000# Copyright 2016 Clinton Knight # # 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. """Share group type access interface.""" from manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base RESOURCES_PATH = '/share-group-types' RESOURCE_PATH = '/share-group-types/%s/access' RESOURCE_PATH_ACTION = '/share-group-types/%s/action' RESOURCE_NAME = 'share_group_type_access' SG_GRADUATION_VERSION = "2.55" class ShareGroupTypeAccess(common_base.Resource): def __repr__(self): return "" % self.id class ShareGroupTypeAccessManager(base.ManagerWithFind): """Manage :class:`ShareGroupTypeAccess` resources.""" resource_class = ShareGroupTypeAccess def _list_share_group_type_access(self, share_group_type, search_opts=None): if share_group_type.is_public: return None share_group_type_id = common_base.getid(share_group_type) url = RESOURCE_PATH % share_group_type_id return self._list(url, RESOURCE_NAME) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def list(self, share_group_type, search_opts=None): return self._list_share_group_type_access(share_group_type, search_opts) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def list(self, share_group_type, search_opts=None): return self._list_share_group_type_access(share_group_type, search_opts) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def add_project_access(self, share_group_type, project): """Add a project to the given share group type access list.""" info = {'project': project} self._action('addProjectAccess', share_group_type, info) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def add_project_access(self, share_group_type, project): """Add a project to the given share group type access list.""" info = {'project': project} self._action('addProjectAccess', share_group_type, info) @api_versions.wraps("2.31", "2.54") @api_versions.experimental_api def remove_project_access(self, share_group_type, project): """Remove a project from the given share group type access list.""" info = {'project': project} self._action('removeProjectAccess', share_group_type, info) @api_versions.wraps(SG_GRADUATION_VERSION) # noqa def remove_project_access(self, share_group_type, project): """Remove a project from the given share group type access list.""" info = {'project': project} self._action('removeProjectAccess', share_group_type, info) def _action(self, action, share_group_type, info, **kwargs): """Perform a share group type action.""" body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) share_group_type_id = common_base.getid(share_group_type) url = RESOURCE_PATH_ACTION % share_group_type_id return self.api.client.post(url, body=body) python-manilaclient-2.1.0/manilaclient/v2/messages.py0000664000175000017500000000534213644133413022671 0ustar zuulzuul00000000000000# 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. """Asynchronous User Message interface.""" from manilaclient import api_versions from manilaclient import base from manilaclient.common.apiclient import base as common_base from manilaclient.common import constants RESOURCES_PATH = '/messages' RESOURCE_PATH = '/messages/%s' RESOURCES_NAME = 'messages' RESOURCE_NAME = 'message' class Message(common_base.Resource): NAME_ATTR = 'id' def __repr__(self): return "" % self.id def delete(self): """Delete this message.""" return self.manager.delete(self) class MessageManager(base.ManagerWithFind): """Manage :class:`Message` resources.""" resource_class = Message @api_versions.wraps('2.37') def get(self, message_id): """Get a message. :param message_id: The ID of the message to get. :rtype: :class:`Message` """ return self._get(RESOURCE_PATH % message_id, RESOURCE_NAME) @api_versions.wraps('2.37') def list(self, search_opts=None, sort_key=None, sort_dir=None): """Lists all messages. :param search_opts: Search options to filter out messages. :rtype: list of :class:`Message` """ search_opts = search_opts or {} if sort_key is not None: if sort_key in constants.MESSAGE_SORT_KEY_VALUES: search_opts['sort_key'] = sort_key else: raise ValueError( 'sort_key must be one of the following: %s.' % ', '.join(constants.MESSAGE_SORT_KEY_VALUES)) if sort_dir is not None: if sort_dir in constants.SORT_DIR_VALUES: search_opts['sort_dir'] = sort_dir else: raise ValueError('sort_dir must be one of the following: %s.' % ', '.join(constants.SORT_DIR_VALUES)) query_string = self._build_query_string(search_opts) path = RESOURCES_PATH + query_string return self._list(path, RESOURCES_NAME) @api_versions.wraps('2.37') def delete(self, message): """Delete a message.""" loc = RESOURCE_PATH % common_base.getid(message) return self._delete(loc) python-manilaclient-2.1.0/manilaclient/base.py0000664000175000017500000001750113644133413021445 0ustar zuulzuul00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 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. """ Base utilities to build API operation managers and objects on top of. """ import contextlib import hashlib import os from manilaclient.common import cliutils from manilaclient import exceptions from manilaclient import utils # Python 2.4 compat try: all except NameError: def all(iterable): return True not in (not x for x in iterable) class Manager(utils.HookableMixin): """Manager for CRUD operations. Managers interact with a particular type of API (shares, snapshots, etc.) and provide CRUD operations for them. """ resource_class = None def __init__(self, api): self.api = api self.client = api.client @property def api_version(self): return self.api.api_version def _list(self, url, response_key, obj_class=None, body=None): resp = None if body: resp, body = self.api.client.post(url, body=body) else: resp, body = self.api.client.get(url) if obj_class is None: obj_class = self.resource_class data = body[response_key] # NOTE(ja): keystone returns values as list as {'values': [ ... ]} # unlike other services which just return the list... if isinstance(data, dict): try: data = data['values'] except KeyError: pass with self.completion_cache('human_id', obj_class, mode="w"): with self.completion_cache('uuid', obj_class, mode="w"): resource = [obj_class(self, res, loaded=True) for res in data if res] if 'count' in body: return resource, body['count'] else: return resource @contextlib.contextmanager def completion_cache(self, cache_type, obj_class, mode): """Bash autocompletion items storage. The completion cache store items that can be used for bash autocompletion, like UUIDs or human-friendly IDs. A resource listing will clear and repopulate the cache. A resource create will append to the cache. Delete is not handled because listings are assumed to be performed often enough to keep the cache reasonably up-to-date. """ base_dir = cliutils.env('manilaclient_UUID_CACHE_DIR', 'MANILACLIENT_UUID_CACHE_DIR', default="~/.manilaclient") # NOTE(sirp): Keep separate UUID caches for each username + endpoint # pair username = cliutils.env('OS_USERNAME', 'MANILA_USERNAME') url = cliutils.env('OS_URL', 'MANILA_URL') uniqifier = hashlib.md5(username.encode('utf-8') + url.encode('utf-8')).hexdigest() cache_dir = os.path.expanduser(os.path.join(base_dir, uniqifier)) try: os.makedirs(cache_dir, 0o755) except OSError: # NOTE(kiall): This is typically either permission denied while # attempting to create the directory, or the directory # already exists. Either way, don't fail. pass resource = obj_class.__name__.lower() filename = "%s-%s-cache" % (resource, cache_type.replace('_', '-')) path = os.path.join(cache_dir, filename) cache_attr = "_%s_cache" % cache_type try: setattr(self, cache_attr, open(path, mode)) except IOError: # NOTE(kiall): This is typically a permission denied while # attempting to write the cache file. pass try: yield finally: cache = getattr(self, cache_attr, None) if cache: cache.close() delattr(self, cache_attr) def write_to_completion_cache(self, cache_type, val): cache = getattr(self, "_%s_cache" % cache_type, None) if cache: cache.write("%s\n" % val) def _get(self, url, response_key=None): resp, body = self.api.client.get(url) if response_key: return self.resource_class(self, body[response_key], loaded=True) else: return self.resource_class(self, body, loaded=True) def _get_with_base_url(self, url, response_key=None): resp, body = self.api.client.get_with_base_url(url) if response_key: return [self.resource_class(self, res, loaded=True) for res in body[response_key] if res] else: return self.resource_class(self, body, loaded=True) def _create(self, url, body, response_key, return_raw=False, **kwargs): self.run_hooks('modify_body_for_create', body, **kwargs) resp, body = self.api.client.post(url, body=body) if return_raw: return body[response_key] with self.completion_cache('human_id', self.resource_class, mode="a"): with self.completion_cache('uuid', self.resource_class, mode="a"): return self.resource_class(self, body[response_key]) def _delete(self, url): resp, body = self.api.client.delete(url) def _update(self, url, body, response_key=None, **kwargs): self.run_hooks('modify_body_for_update', body, **kwargs) resp, body = self.api.client.put(url, body=body) if body: if response_key: return self.resource_class(self, body[response_key]) else: return self.resource_class(self, body) def _build_query_string(self, search_opts): search_opts = search_opts or {} search_opts = utils.unicode_key_value_to_string(search_opts) params = sorted( [(k, v) for (k, v) in search_opts.items() if v]) query_string = "?%s" % utils.safe_urlencode(params) if params else '' return query_string class ManagerWithFind(Manager): """Like a `Manager`, but with additional `find()`/`findall()` methods.""" def find(self, **kwargs): """Find a single item with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ matches = self.findall(**kwargs) num_matches = len(matches) if num_matches == 0: msg = "No %s matching %s." % (self.resource_class.__name__, kwargs) raise exceptions.NotFound(404, msg) elif num_matches > 1: raise exceptions.NoUniqueMatch else: return matches[0] def findall(self, **kwargs): """Find all items with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ found = [] searches = list(kwargs.items()) search_opts = {'all_tenants': 1} for obj in self.list(search_opts=search_opts): try: if all(getattr(obj, attr) == value for (attr, value) in searches): found.append(obj) except AttributeError: continue return found def list(self, search_opts=None): raise NotImplementedError python-manilaclient-2.1.0/manilaclient/utils.py0000664000175000017500000000546713644133413021703 0ustar zuulzuul00000000000000# 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 six from six.moves.urllib import parse class HookableMixin(object): """Mixin so classes can register and run hooks.""" _hooks_map = {} @classmethod def add_hook(cls, hook_type, hook_func): if hook_type not in cls._hooks_map: cls._hooks_map[hook_type] = [] cls._hooks_map[hook_type].append(hook_func) @classmethod def run_hooks(cls, hook_type, *args, **kwargs): hook_funcs = cls._hooks_map.get(hook_type) or [] for hook_func in hook_funcs: hook_func(*args, **kwargs) def safe_issubclass(*args): """Like issubclass, but will just return False if not a class.""" try: if issubclass(*args): return True except TypeError: pass return False def get_function_name(func): if six.PY2: if hasattr(func, "im_class"): return "%s.%s" % (func.im_class, func.__name__) else: return "%s.%s" % (func.__module__, func.__name__) else: return "%s.%s" % (func.__module__, func.__qualname__) def _encode(src): """remove extra 'u' in PY2.""" if six.PY2 and isinstance(src, six.text_type): return src.encode('utf-8') return src def unicode_key_value_to_string(src): """Recursively converts dictionary keys to strings.""" if isinstance(src, dict): return dict((_encode(k), _encode(unicode_key_value_to_string(v))) for k, v in src.items()) if isinstance(src, list): return [unicode_key_value_to_string(l) for l in src] return _encode(src) def safe_urlencode(params_dict): """Workaround incompatible change to urllib.parse urllib's parse library used to adhere to RFC 2396 until python 3.7. The library moved from RFC 2396 to RFC 3986 for quoting URL strings in python 3.7 and '~' is now included in the set of reserved characters. [1] This utility ensures "~" is never encoded. See LP 1785283 [2] for more details. [1] https://docs.python.org/3/library/urllib.parse.html#url-quoting [2] https://bugs.launchpad.net/python-manilaclient/+bug/1785283 :param params_dict can be a list of (k,v) tuples, or a dictionary """ parsed_params = parse.urlencode(params_dict) return parsed_params.replace("%7E", "~") python-manilaclient-2.1.0/manilaclient/common/0000775000175000017500000000000013644133466021455 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/common/httpclient.py0000664000175000017500000001553413644133413024205 0ustar zuulzuul00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2011 Piston Cloud Computing, Inc. # Copyright 2015 Mirantis Inc. # # 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 copy import logging from oslo_serialization import jsonutils from oslo_utils import strutils import re import requests import six from six.moves.urllib import parse from manilaclient import exceptions try: from eventlet import sleep except ImportError: from time import sleep # noqa class HTTPClient(object): """HTTP Client class used by multiple clients. The imported Requests module caches and reuses objects with the same destination. To avoid the problem of sending duplicate requests it is necessary that the Requests module is only imported once during client execution. This class is shared by multiple client versions so that the client can be changed to another version during execution. """ API_VERSION_HEADER = "X-Openstack-Manila-Api-Version" def __init__(self, endpoint_url, token, user_agent, api_version, insecure=False, cacert=None, timeout=None, retries=None, http_log_debug=False): self.endpoint_url = endpoint_url self.base_url = self._get_base_url(self.endpoint_url) self.retries = int(retries or 0) self.http_log_debug = http_log_debug self.request_options = self._set_request_options( insecure, cacert, timeout) self.default_headers = { 'X-Auth-Token': token, self.API_VERSION_HEADER: api_version.get_string(), 'User-Agent': user_agent, 'Accept': 'application/json', } self._add_log_handlers(http_log_debug) def _add_log_handlers(self, http_log_debug): self._logger = logging.getLogger(__name__) # check that handler hasn't already been added if http_log_debug and not self._logger.handlers: ch = logging.StreamHandler() ch._name = 'http_client_handler' self._logger.setLevel(logging.DEBUG) self._logger.addHandler(ch) if hasattr(requests, 'logging'): rql = requests.logging.getLogger(requests.__name__) rql.addHandler(ch) def _get_base_url(self, url): """Truncates url and returns base endpoint""" service_endpoint = parse.urlparse(url) service_endpoint_base_path = re.search( r'(.+?)/v([0-9]+|[0-9]+\.[0-9]+)(/.*|$)', service_endpoint.path) base_path = (service_endpoint_base_path.group(1) if service_endpoint_base_path else '') base_url = service_endpoint._replace(path=base_path) return parse.urlunparse(base_url) + '/' def _set_request_options(self, insecure, cacert, timeout=None): options = {'verify': True} if insecure: options['verify'] = False elif cacert: options['verify'] = cacert if timeout: options['timeout'] = timeout return options def request(self, url, method, **kwargs): headers = copy.deepcopy(self.default_headers) headers.update(kwargs.get('headers', {})) options = copy.deepcopy(self.request_options) if 'body' in kwargs: headers['Content-Type'] = 'application/json' options['data'] = jsonutils.dumps(kwargs['body']) self.log_request(method, url, headers, options.get('data', None)) resp = requests.request(method, url, headers=headers, **options) self.log_response(resp) body = None if resp.text: try: body = jsonutils.loads(resp.text) except ValueError: pass if resp.status_code >= 400: raise exceptions.from_response(resp, method, url) return resp, body def _cs_request(self, url, method, **kwargs): return self._cs_request_with_retries( self.endpoint_url + url, method, **kwargs) def _cs_request_base_url(self, url, method, **kwargs): return self._cs_request_with_retries( self.base_url + url, method, **kwargs) def _cs_request_with_retries(self, url, method, **kwargs): attempts = 0 timeout = 1 while True: attempts += 1 try: resp, body = self.request(url, method, **kwargs) return resp, body except (exceptions.BadRequest, requests.exceptions.RequestException, exceptions.ClientException) as e: if attempts > self.retries: raise self._logger.debug("Request error: %s", six.text_type(e)) self._logger.debug( "Failed attempt(%(current)s of %(total)s), " " retrying in %(sec)s seconds", { 'current': attempts, 'total': self.retries, 'sec': timeout }) sleep(timeout) timeout *= 2 def get_with_base_url(self, url, **kwargs): return self._cs_request_base_url(url, 'GET', **kwargs) def get(self, url, **kwargs): return self._cs_request(url, 'GET', **kwargs) def post(self, url, **kwargs): return self._cs_request(url, 'POST', **kwargs) def put(self, url, **kwargs): return self._cs_request(url, 'PUT', **kwargs) def delete(self, url, **kwargs): return self._cs_request(url, 'DELETE', **kwargs) def log_request(self, method, url, headers, data=None): if not self.http_log_debug: return string_parts = ['curl -i', ' -X %s' % method, ' %s' % url] for element in headers: header = ' -H "%s: %s"' % (element, headers[element]) string_parts.append(header) if data: if "password" in data: data = strutils.mask_password(data) string_parts.append(" -d '%s'" % data) self._logger.debug("\nREQ: %s\n", "".join(string_parts)) def log_response(self, resp): if not self.http_log_debug: return self._logger.debug( "RESP: [%(code)s] %(headers)s\nRESP BODY: %(body)s\n", { 'code': resp.status_code, 'headers': resp.headers, 'body': resp.text }) python-manilaclient-2.1.0/manilaclient/common/cliutils.py0000664000175000017500000002076513644133413023661 0ustar zuulzuul00000000000000# Copyright 2012 Red Hat, Inc. # # 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. # W0603: Using the global statement # W0621: Redefining name %s from outer scope # pylint: disable=W0603,W0621 from __future__ import print_function import getpass import inspect import os import sys import textwrap from oslo_utils import encodeutils from oslo_utils import strutils import prettytable import six from six import moves from manilaclient.common._i18n import _ class MissingArgs(Exception): """Supplied arguments are not sufficient for calling a function.""" def __init__(self, missing): self.missing = missing msg = _("Missing arguments: %s") % ", ".join(missing) super(MissingArgs, self).__init__(msg) def validate_args(fn, *args, **kwargs): """Check that the supplied args are sufficient for calling a function. >>> validate_args(lambda a: None) Traceback (most recent call last): ... MissingArgs: Missing argument(s): a >>> validate_args(lambda a, b, c, d: None, 0, c=1) Traceback (most recent call last): ... MissingArgs: Missing argument(s): b, d :param fn: the function to check :param arg: the positional arguments supplied :param kwargs: the keyword arguments supplied """ argspec = inspect.getargspec(fn) num_defaults = len(argspec.defaults or []) required_args = argspec.args[:len(argspec.args) - num_defaults] def isbound(method): return getattr(method, '__self__', None) is not None if isbound(fn): required_args.pop(0) missing = [arg for arg in required_args if arg not in kwargs] missing = missing[len(args):] if missing: raise MissingArgs(missing) def arg(*args, **kwargs): """Decorator for CLI args. Example: >>> @arg("name", help="Name of the new entity") ... def entity_create(args): ... pass """ def _decorator(func): add_arg(func, *args, **kwargs) return func return _decorator def env(*args, **kwargs): """Returns the first environment variable set. If all are empty, defaults to '' or keyword arg `default`. """ for arg in args: value = os.environ.get(arg) if value: return value return kwargs.get('default', '') def add_arg(func, *args, **kwargs): """Bind CLI arguments to a shell.py `do_foo` function.""" if not hasattr(func, 'arguments'): func.arguments = [] # NOTE(sirp): avoid dups that can occur when the module is shared across # tests. if (args, kwargs) not in func.arguments: # Because of the semantics of decorator composition if we just append # to the options list positional options will appear to be backwards. func.arguments.insert(0, (args, kwargs)) def unauthenticated(func): """Adds 'unauthenticated' attribute to decorated function. Usage: >>> @unauthenticated ... def mymethod(f): ... pass """ func.unauthenticated = True return func def isunauthenticated(func): """Checks if the function does not require authentication. Mark such functions with the `@unauthenticated` decorator. :returns: bool """ return getattr(func, 'unauthenticated', False) def print_list(objs, fields, formatters=None, sortby_index=0, mixed_case_fields=None, field_labels=None): """Print a list or objects as a table, one row per object. :param objs: iterable of :class:`Resource` :param fields: attributes that correspond to columns, in order :param formatters: `dict` of callables for field formatting :param sortby_index: index of the field for sorting table rows :param mixed_case_fields: fields corresponding to object attributes that have mixed case names (e.g., 'serverId') :param field_labels: Labels to use in the heading of the table, default to fields. """ formatters = formatters or {} mixed_case_fields = mixed_case_fields or [] field_labels = field_labels or fields if len(field_labels) != len(fields): raise ValueError(_("Field labels list %(labels)s has different number " "of elements than fields list %(fields)s"), {'labels': field_labels, 'fields': fields}) if sortby_index is None: kwargs = {} else: kwargs = {'sortby': field_labels[sortby_index]} pt = prettytable.PrettyTable(field_labels) pt.align = 'l' for o in objs: row = [] for field in fields: if field in formatters: row.append(formatters[field](o)) else: if field in mixed_case_fields: field_name = field.replace(' ', '_') else: field_name = field.lower().replace(' ', '_') data = getattr(o, field_name, '') row.append(data) pt.add_row(row) if six.PY3: print(encodeutils.safe_encode(pt.get_string(**kwargs)).decode()) else: print(encodeutils.safe_encode(pt.get_string(**kwargs))) def print_dict(dct, dict_property="Property", wrap=0): """Print a `dict` as a table of two columns. :param dct: `dict` to print :param dict_property: name of the first column :param wrap: wrapping for the second column """ pt = prettytable.PrettyTable([dict_property, 'Value']) pt.align = 'l' for k, v in dct.items(): # convert dict to str to check length if isinstance(v, dict): v = six.text_type(v) if wrap > 0: v = textwrap.fill(six.text_type(v), wrap) # if value has a newline, add in multiple rows # e.g. fault with stacktrace if v and isinstance(v, six.string_types) and r'\n' in v: lines = v.strip().split(r'\n') col1 = k for line in lines: pt.add_row([col1, line]) col1 = '' else: pt.add_row([k, v]) if six.PY3: print(encodeutils.safe_encode(pt.get_string()).decode()) else: print(encodeutils.safe_encode(pt.get_string())) def get_password(max_password_prompts=3): """Read password from TTY.""" verify = strutils.bool_from_string(env("OS_VERIFY_PASSWORD")) pw = None if hasattr(sys.stdin, "isatty") and sys.stdin.isatty(): # Check for Ctrl-D try: for __ in moves.range(max_password_prompts): pw1 = getpass.getpass("OS Password: ") if verify: pw2 = getpass.getpass("Please verify: ") else: pw2 = pw1 if pw1 == pw2 and pw1: pw = pw1 break except EOFError: pass return pw def service_type(stype): """Adds 'service_type' attribute to decorated function. Usage: .. code-block:: python @service_type('volume') def mymethod(f): ... """ def inner(f): f.service_type = stype return f return inner def get_service_type(f): """Retrieves service type from function.""" return getattr(f, 'service_type', None) def pretty_choice_list(l): return ', '.join("'%s'" % i for i in l) def exit(msg=''): if msg: print(msg, file=sys.stderr) sys.exit(1) def transform_export_locations_to_string_view(export_locations): export_locations_string_view = '' replica_export_location_ignored_keys = ( 'replica_state', 'availability_zone', 'share_replica_id') for el in export_locations: if hasattr(el, '_info'): export_locations_dict = el._info else: export_locations_dict = el for k, v in export_locations_dict.items(): # NOTE(gouthamr): We don't want to show replica related info # twice in the output, so ignore those. if k not in replica_export_location_ignored_keys: export_locations_string_view += '\n%(k)s = %(v)s' % { 'k': k, 'v': v} return export_locations_string_view python-manilaclient-2.1.0/manilaclient/common/_i18n.py0000664000175000017500000000215313644133413022736 0ustar zuulzuul00000000000000# 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. """oslo.i18n integration module. See https://docs.openstack.org/oslo.i18n/latest/user/usage.html """ import oslo_i18n # NOTE(dhellmann): This reference to o-s-l-o will be replaced by the # application name when this module is synced into the separate # repository. It is OK to have more than one translation function # using the same domain, since there will still only be one message # catalog. _translators = oslo_i18n.TranslatorFactory(domain='manilaclient') # The primary translation function using the well-known name "_" _ = _translators.primary python-manilaclient-2.1.0/manilaclient/common/__init__.py0000664000175000017500000000000013644133413023544 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/common/constants.py0000664000175000017500000000622313644133413024036 0ustar zuulzuul00000000000000# 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. """Common constants that can be used all over the manilaclient.""" # These are used for providing desired sorting params with list requests SORT_DIR_VALUES = ('asc', 'desc') SHARE_SORT_KEY_VALUES = ( 'id', 'status', 'size', 'host', 'share_proto', 'availability_zone', 'user_id', 'project_id', 'created_at', 'updated_at', 'display_name', 'name', 'share_type_id', 'share_type', 'share_network_id', 'share_network', 'snapshot_id', 'snapshot', ) SNAPSHOT_SORT_KEY_VALUES = ( 'id', 'status', 'size', 'share_id', 'user_id', 'project_id', 'progress', 'name', 'display_name', ) SHARE_GROUP_SORT_KEY_VALUES = ( 'id', 'name', 'status', 'host', 'user_id', 'project_id', 'created_at', 'availability_zone', 'share_network', 'share_network_id', 'share_group_type', 'share_group_type_id', 'source_share_group_snapshot_id', ) SHARE_GROUP_SNAPSHOT_SORT_KEY_VALUES = ( 'id', 'name', 'status', 'host', 'user_id', 'project_id', 'created_at', 'share_group_id', ) TASK_STATE_MIGRATION_SUCCESS = 'migration_success' TASK_STATE_MIGRATION_ERROR = 'migration_error' TASK_STATE_MIGRATION_CANCELLED = 'migration_cancelled' TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE = 'migration_driver_phase1_done' TASK_STATE_DATA_COPYING_COMPLETED = 'data_copying_completed' EXPERIMENTAL_HTTP_HEADER = 'X-OpenStack-Manila-API-Experimental' V1_SERVICE_TYPE = 'share' V2_SERVICE_TYPE = 'sharev2' # Service type authority recommends using 'shared-file-system' as # the service type. See: https://opendev.org/openstack/service-types-authority SFS_SERVICE_TYPE = 'shared-file-system' SERVICE_TYPES = {'1': V1_SERVICE_TYPE, '2': V2_SERVICE_TYPE} EXTENSION_PLUGIN_NAMESPACE = 'manilaclient.common.apiclient.auth' MESSAGE_SORT_KEY_VALUES = ( 'id', 'project_id', 'request_id', 'resource_type', 'action_id', 'detail_id', 'resource_id', 'message_level', 'expires_at', 'request_id', 'created_at' ) STATUS_AVAILABLE = 'available' STATUS_ERROR = 'error' STATUS_ACTIVE = 'active' STATUS_MANAGE_ERROR = 'manage_error' STATUS_UNMANAGE_ERROR = 'unmanage_error' STATUS_DELETING = 'deleting' STATUS_CREATING = 'creating' SNAPSHOT_SUPPORT = 'snapshot_support' CREATE_SHARE_FROM_SNAPSHOT_SUPPORT = 'create_share_from_snapshot_support' REVERT_TO_SNAPSHOT_SUPPORT = 'revert_to_snapshot_support' MOUNT_SNAPSHOT_SUPPORT = 'mount_snapshot_support' BOOL_SPECS = ( SNAPSHOT_SUPPORT, CREATE_SHARE_FROM_SNAPSHOT_SUPPORT, REVERT_TO_SNAPSHOT_SUPPORT, MOUNT_SNAPSHOT_SUPPORT ) python-manilaclient-2.1.0/manilaclient/common/apiclient/0000775000175000017500000000000013644133466023425 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/common/apiclient/exceptions.py0000664000175000017500000003123013644133413026147 0ustar zuulzuul00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 Nebula, Inc. # Copyright 2013 Alessio Ababilov # 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. """ Exception definitions. """ ######################################################################## # # THIS MODULE IS DEPRECATED # # Please refer to # https://etherpad.openstack.org/p/kilo-oslo-library-proposals for # the discussion leading to this deprecation. # # We recommend checking out the python-openstacksdk project # (https://launchpad.net/python-openstacksdk) instead. # ######################################################################## import inspect import sys import six from manilaclient.common._i18n import _ class ClientException(Exception): """The base exception class for all exceptions this library raises.""" pass class ValidationError(ClientException): """Error in validation on API client side.""" pass class UnsupportedVersion(ClientException): """User is trying to use an unsupported version of the API.""" pass class CommandError(ClientException): """Error in CLI tool.""" pass class AuthorizationFailure(ClientException): """Cannot authorize API client.""" pass class ConnectionError(ClientException): """Cannot connect to API service.""" pass class ConnectionRefused(ConnectionError): """Connection refused while trying to connect to API service.""" pass class AuthPluginOptionsMissing(AuthorizationFailure): """Auth plugin misses some options.""" def __init__(self, opt_names): super(AuthPluginOptionsMissing, self).__init__( _("Authentication failed. Missing options: %s") % ", ".join(opt_names)) self.opt_names = opt_names class AuthSystemNotFound(AuthorizationFailure): """User has specified an AuthSystem that is not installed.""" def __init__(self, auth_system): super(AuthSystemNotFound, self).__init__( _("AuthSystemNotFound: %r") % auth_system) self.auth_system = auth_system class NoUniqueMatch(ClientException): """Multiple entities found instead of one.""" pass class EndpointException(ClientException): """Something is rotten in Service Catalog.""" pass class EndpointNotFound(EndpointException): """Could not find requested endpoint in Service Catalog.""" pass class AmbiguousEndpoints(EndpointException): """Found more than one matching endpoint in Service Catalog.""" def __init__(self, endpoints=None): super(AmbiguousEndpoints, self).__init__( _("AmbiguousEndpoints: %r") % endpoints) self.endpoints = endpoints class HttpError(ClientException): """The base exception class for all HTTP exceptions.""" http_status = 0 message = _("HTTP Error") def __init__(self, message=None, details=None, response=None, request_id=None, url=None, method=None, http_status=None): self.http_status = http_status or self.http_status self.message = message or self.message self.details = details self.request_id = request_id self.response = response self.url = url self.method = method formatted_string = "%s (HTTP %s)" % (self.message, self.http_status) if request_id: formatted_string += " (Request-ID: %s)" % request_id super(HttpError, self).__init__(formatted_string) class HTTPRedirection(HttpError): """HTTP Redirection.""" message = _("HTTP Redirection") class HTTPClientError(HttpError): """Client-side HTTP error. Exception for cases in which the client seems to have erred. """ message = _("HTTP Client Error") class HttpServerError(HttpError): """Server-side HTTP error. Exception for cases in which the server is aware that it has erred or is incapable of performing the request. """ message = _("HTTP Server Error") class MultipleChoices(HTTPRedirection): """HTTP 300 - Multiple Choices. Indicates multiple options for the resource that the client may follow. """ http_status = 300 message = _("Multiple Choices") class BadRequest(HTTPClientError): """HTTP 400 - Bad Request. The request cannot be fulfilled due to bad syntax. """ http_status = 400 message = _("Bad Request") class Unauthorized(HTTPClientError): """HTTP 401 - Unauthorized. Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. """ http_status = 401 message = _("Unauthorized") class PaymentRequired(HTTPClientError): """HTTP 402 - Payment Required. Reserved for future use. """ http_status = 402 message = _("Payment Required") class Forbidden(HTTPClientError): """HTTP 403 - Forbidden. The request was a valid request, but the server is refusing to respond to it. """ http_status = 403 message = _("Forbidden") class NotFound(HTTPClientError): """HTTP 404 - Not Found. The requested resource could not be found but may be available again in the future. """ http_status = 404 message = _("Not Found") class MethodNotAllowed(HTTPClientError): """HTTP 405 - Method Not Allowed. A request was made of a resource using a request method not supported by that resource. """ http_status = 405 message = _("Method Not Allowed") class NotAcceptable(HTTPClientError): """HTTP 406 - Not Acceptable. The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request. """ http_status = 406 message = _("Not Acceptable") class ProxyAuthenticationRequired(HTTPClientError): """HTTP 407 - Proxy Authentication Required. The client must first authenticate itself with the proxy. """ http_status = 407 message = _("Proxy Authentication Required") class RequestTimeout(HTTPClientError): """HTTP 408 - Request Timeout. The server timed out waiting for the request. """ http_status = 408 message = _("Request Timeout") class Conflict(HTTPClientError): """HTTP 409 - Conflict. Indicates that the request could not be processed because of conflict in the request, such as an edit conflict. """ http_status = 409 message = _("Conflict") class Gone(HTTPClientError): """HTTP 410 - Gone. Indicates that the resource requested is no longer available and will not be available again. """ http_status = 410 message = _("Gone") class LengthRequired(HTTPClientError): """HTTP 411 - Length Required. The request did not specify the length of its content, which is required by the requested resource. """ http_status = 411 message = _("Length Required") class PreconditionFailed(HTTPClientError): """HTTP 412 - Precondition Failed. The server does not meet one of the preconditions that the requester put on the request. """ http_status = 412 message = _("Precondition Failed") class RequestEntityTooLarge(HTTPClientError): """HTTP 413 - Request Entity Too Large. The request is larger than the server is willing or able to process. """ http_status = 413 message = _("Request Entity Too Large") def __init__(self, *args, **kwargs): try: self.retry_after = int(kwargs.pop('retry_after')) except (KeyError, ValueError): self.retry_after = 0 super(RequestEntityTooLarge, self).__init__(*args, **kwargs) class RequestUriTooLong(HTTPClientError): """HTTP 414 - Request-URI Too Long. The URI provided was too long for the server to process. """ http_status = 414 message = _("Request-URI Too Long") class UnsupportedMediaType(HTTPClientError): """HTTP 415 - Unsupported Media Type. The request entity has a media type which the server or resource does not support. """ http_status = 415 message = _("Unsupported Media Type") class RequestedRangeNotSatisfiable(HTTPClientError): """HTTP 416 - Requested Range Not Satisfiable. The client has asked for a portion of the file, but the server cannot supply that portion. """ http_status = 416 message = _("Requested Range Not Satisfiable") class ExpectationFailed(HTTPClientError): """HTTP 417 - Expectation Failed. The server cannot meet the requirements of the Expect request-header field. """ http_status = 417 message = _("Expectation Failed") class UnprocessableEntity(HTTPClientError): """HTTP 422 - Unprocessable Entity. The request was well-formed but was unable to be followed due to semantic errors. """ http_status = 422 message = _("Unprocessable Entity") class InternalServerError(HttpServerError): """HTTP 500 - Internal Server Error. A generic error message, given when no more specific message is suitable. """ http_status = 500 message = _("Internal Server Error") # NotImplemented is a python keyword. class HttpNotImplemented(HttpServerError): """HTTP 501 - Not Implemented. The server either does not recognize the request method, or it lacks the ability to fulfill the request. """ http_status = 501 message = _("Not Implemented") class BadGateway(HttpServerError): """HTTP 502 - Bad Gateway. The server was acting as a gateway or proxy and received an invalid response from the upstream server. """ http_status = 502 message = _("Bad Gateway") class ServiceUnavailable(HttpServerError): """HTTP 503 - Service Unavailable. The server is currently unavailable. """ http_status = 503 message = _("Service Unavailable") class GatewayTimeout(HttpServerError): """HTTP 504 - Gateway Timeout. The server was acting as a gateway or proxy and did not receive a timely response from the upstream server. """ http_status = 504 message = _("Gateway Timeout") class HttpVersionNotSupported(HttpServerError): """HTTP 505 - HttpVersion Not Supported. The server does not support the HTTP protocol version used in the request. """ http_status = 505 message = _("HTTP Version Not Supported") # _code_map contains all the classes that have http_status attribute. _code_map = dict( (getattr(obj, 'http_status', None), obj) for name, obj in vars(sys.modules[__name__]).items() if inspect.isclass(obj) and getattr(obj, 'http_status', False) ) def from_response(response, method, url): """Returns an instance of :class:`HttpError` or subclass based on response. :param response: instance of `requests.Response` class :param method: HTTP method used for request :param url: URL used for request """ req_id = response.headers.get("x-openstack-request-id") # NOTE(hdd) true for older versions of nova and cinder if not req_id: req_id = response.headers.get("x-compute-request-id") kwargs = { "http_status": response.status_code, "response": response, "method": method, "url": url, "request_id": req_id, } if "retry-after" in response.headers: kwargs["retry_after"] = response.headers["retry-after"] content_type = response.headers.get("Content-Type", "") if content_type.startswith("application/json"): try: body = response.json() except ValueError: pass else: if isinstance(body, dict): error = body.get(list(body)[0]) if isinstance(error, dict): kwargs["message"] = (error.get("message") or error.get("faultstring")) kwargs["details"] = (error.get("details") or six.text_type(body)) elif content_type.startswith("text/"): kwargs["details"] = response.text try: cls = _code_map[response.status_code] except KeyError: if 500 <= response.status_code < 600: cls = HttpServerError elif 400 <= response.status_code < 500: cls = HTTPClientError else: cls = HttpError return cls(**kwargs) python-manilaclient-2.1.0/manilaclient/common/apiclient/base.py0000664000175000017500000004106413644133413024706 0ustar zuulzuul00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2012 Grid Dynamics # 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. """ Base utilities to build API operation managers and objects on top of. """ # E1102: %s is not callable # pylint: disable=E1102 import abc import copy from oslo_utils import strutils import six from manilaclient.common._i18n import _ from manilaclient.common.apiclient import exceptions from manilaclient import utils def getid(obj): """Return id if argument is a Resource. Abstracts the common pattern of allowing both an object or an object's ID (UUID) as a parameter when dealing with relationships. """ try: if obj.uuid: return obj.uuid except AttributeError: pass try: return obj.id except AttributeError: return obj # TODO(aababilov): call run_hooks() in HookableMixin's child classes class HookableMixin(object): """Mixin so classes can register and run hooks.""" _hooks_map = {} @classmethod def add_hook(cls, hook_type, hook_func): """Add a new hook of specified type. :param cls: class that registers hooks :param hook_type: hook type, e.g., '__pre_parse_args__' :param hook_func: hook function """ if hook_type not in cls._hooks_map: cls._hooks_map[hook_type] = [] cls._hooks_map[hook_type].append(hook_func) @classmethod def run_hooks(cls, hook_type, *args, **kwargs): """Run all hooks of specified type. :param cls: class that registers hooks :param hook_type: hook type, e.g., '__pre_parse_args__' :param args: args to be passed to every hook function :param kwargs: kwargs to be passed to every hook function """ hook_funcs = cls._hooks_map.get(hook_type) or [] for hook_func in hook_funcs: hook_func(*args, **kwargs) class BaseManager(HookableMixin): """Basic manager type providing common operations. Managers interact with a particular type of API (servers, flavors, images, etc.) and provide CRUD operations for them. """ resource_class = None def __init__(self, client): """Initializes BaseManager with `client`. :param client: instance of BaseClient descendant for HTTP requests """ super(BaseManager, self).__init__() self.client = client def _list(self, url, response_key=None, obj_class=None, json=None): """List the collection. :param url: a partial URL, e.g., '/servers' :param response_key: the key to be looked up in response dictionary, e.g., 'servers'. If response_key is None - all response body will be used. :param obj_class: class for constructing the returned objects (self.resource_class will be used by default) :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) """ if json: body = self.client.post(url, json=json).json() else: body = self.client.get(url).json() if obj_class is None: obj_class = self.resource_class data = body[response_key] if response_key is not None else body # NOTE(ja): keystone returns values as list as {'values': [ ... ]} # unlike other services which just return the list... try: data = data['values'] except (KeyError, TypeError): pass return [obj_class(self, res, loaded=True) for res in data if res] def _get(self, url, response_key=None): """Get an object from collection. :param url: a partial URL, e.g., '/servers' :param response_key: the key to be looked up in response dictionary, e.g., 'server'. If response_key is None - all response body will be used. """ body = self.client.get(url).json() data = body[response_key] if response_key is not None else body return self.resource_class(self, data, loaded=True) def _head(self, url): """Retrieve request headers for an object. :param url: a partial URL, e.g., '/servers' """ resp = self.client.head(url) return resp.status_code == 204 def _post(self, url, json, response_key=None, return_raw=False): """Create an object. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'server'. If response_key is None - all response body will be used. :param return_raw: flag to force returning raw JSON instead of Python object of self.resource_class """ body = self.client.post(url, json=json).json() data = body[response_key] if response_key is not None else body if return_raw: return data return self.resource_class(self, data) def _put(self, url, json=None, response_key=None): """Update an object with PUT method. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers'. If response_key is None - all response body will be used. """ resp = self.client.put(url, json=json) # PUT requests may not return a body if resp.content: body = resp.json() if response_key is not None: return self.resource_class(self, body[response_key]) else: return self.resource_class(self, body) def _patch(self, url, json=None, response_key=None): """Update an object with PATCH method. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers'. If response_key is None - all response body will be used. """ body = self.client.patch(url, json=json).json() if response_key is not None: return self.resource_class(self, body[response_key]) else: return self.resource_class(self, body) def _delete(self, url): """Delete an object. :param url: a partial URL, e.g., '/servers/my-server' """ return self.client.delete(url) @six.add_metaclass(abc.ABCMeta) class ManagerWithFind(BaseManager): """Manager with additional `find()`/`findall()` methods.""" @abc.abstractmethod def list(self): pass def find(self, **kwargs): """Find a single item with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ matches = self.findall(**kwargs) num_matches = len(matches) if num_matches == 0: msg = _("No %(name)s matching %(args)s.") % { 'name': self.resource_class.__name__, 'args': kwargs } raise exceptions.NotFound(msg) elif num_matches > 1: raise exceptions.NoUniqueMatch() else: return matches[0] def findall(self, **kwargs): """Find all items with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ found = [] searches = kwargs.items() for obj in self.list(): try: if all(getattr(obj, attr) == value for (attr, value) in searches): found.append(obj) except AttributeError: continue return found class CrudManager(BaseManager): """Base manager class for manipulating entities. Children of this class are expected to define a `collection_key` and `key`. - `collection_key`: Usually a plural noun by convention (e.g. `entities`); used to refer collections in both URL's (e.g. `/v3/entities`) and JSON objects containing a list of member resources (e.g. `{'entities': [{}, {}, {}]}`). - `key`: Usually a singular noun by convention (e.g. `entity`); used to refer to an individual member of the collection. """ collection_key = None key = None def build_url(self, base_url=None, **kwargs): """Builds a resource URL for the given kwargs. Given an example collection where `collection_key = 'entities'` and `key = 'entity'`, the following URL's could be generated. By default, the URL will represent a collection of entities, e.g.:: /entities If kwargs contains an `entity_id`, then the URL will represent a specific member, e.g.:: /entities/{entity_id} :param base_url: if provided, the generated URL will be appended to it """ url = base_url if base_url is not None else '' url += '/%s' % self.collection_key # do we have a specific entity? entity_id = kwargs.get('%s_id' % self.key) if entity_id is not None: url += '/%s' % entity_id return url def _filter_kwargs(self, kwargs): """Drop null values and handle ids.""" for key, ref in kwargs.copy().items(): if ref is None: kwargs.pop(key) else: if isinstance(ref, Resource): kwargs.pop(key) kwargs['%s_id' % key] = getid(ref) return kwargs def create(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._post( self.build_url(**kwargs), {self.key: kwargs}, self.key) def get(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._get( self.build_url(**kwargs), self.key) def head(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._head(self.build_url(**kwargs)) def list(self, base_url=None, **kwargs): """List the collection. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) return self._list( '%(base_url)s%(query)s' % { 'base_url': self.build_url(base_url=base_url, **kwargs), 'query': '?%s' % utils.safe_urlencode(kwargs) if kwargs else '' }, self.collection_key) def put(self, base_url=None, **kwargs): """Update an element. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) return self._put(self.build_url(base_url=base_url, **kwargs)) def update(self, **kwargs): kwargs = self._filter_kwargs(kwargs) params = kwargs.copy() params.pop('%s_id' % self.key) return self._patch( self.build_url(**kwargs), {self.key: params}, self.key) def delete(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._delete( self.build_url(**kwargs)) def find(self, base_url=None, **kwargs): """Find a single item with attributes matching ``**kwargs``. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) rl = self._list( '%(base_url)s%(query)s' % { 'base_url': self.build_url(base_url=base_url, **kwargs), 'query': '?%s' % utils.safe_urlencode(kwargs) if kwargs else '' }, self.collection_key) num = len(rl) if num == 0: msg = _("No %(name)s matching %(args)s.") % { 'name': self.resource_class.__name__, 'args': kwargs } raise exceptions.NotFound(404, msg) elif num > 1: raise exceptions.NoUniqueMatch else: return rl[0] class Extension(HookableMixin): """Extension descriptor.""" SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__') manager_class = None def __init__(self, name, module): super(Extension, self).__init__() self.name = name self.module = module self._parse_extension_module() def _parse_extension_module(self): self.manager_class = None for attr_name, attr_value in self.module.__dict__.items(): if attr_name in self.SUPPORTED_HOOKS: self.add_hook(attr_name, attr_value) else: try: if issubclass(attr_value, BaseManager): self.manager_class = attr_value except TypeError: pass def __repr__(self): return "" % self.name class Resource(object): """Base class for OpenStack resources (tenant, user, etc.). This is pretty much just a bag for attributes. """ HUMAN_ID = False NAME_ATTR = 'name' def __init__(self, manager, info, loaded=False): """Populate and bind to a manager. :param manager: BaseManager object :param info: dictionary representing resource attributes :param loaded: prevent lazy-loading if set to True """ self.manager = manager self._info = info self._add_details(info) self._loaded = loaded def __repr__(self): reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and k != 'manager') info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) return "<%s %s>" % (self.__class__.__name__, info) @property def human_id(self): """Human-readable ID which can be used for bash completion.""" if self.HUMAN_ID: name = getattr(self, self.NAME_ATTR, None) if name is not None: return strutils.to_slug(name) return None def _add_details(self, info): for (k, v) in info.items(): try: setattr(self, k, v) self._info[k] = v except AttributeError: # In this case we already defined the attribute on the class pass def __getattr__(self, k): if k not in self.__dict__: # NOTE(bcwaldon): disallow lazy-loading if already loaded once if not self.is_loaded(): self.get() return self.__getattr__(k) raise AttributeError(k) else: return self.__dict__[k] def get(self): """Support for lazy loading details. Some clients, such as novaclient have the option to lazy load the details, details which can be loaded with this function. """ # set_loaded() first ... so if we have to bail, we know we tried. self.set_loaded(True) if not hasattr(self.manager, 'get'): return new = self.manager.get(self.id) if new: self._add_details(new._info) def __eq__(self, other): if not isinstance(other, Resource): return NotImplemented # two resources of different types are not equal if not isinstance(other, self.__class__): return False if hasattr(self, 'id') and hasattr(other, 'id'): return self.id == other.id return self._info == other._info def __ne__(self, other): return not self == other def is_loaded(self): return self._loaded def set_loaded(self, val): self._loaded = val def to_dict(self): return copy.deepcopy(self._info) python-manilaclient-2.1.0/manilaclient/common/apiclient/utils.py0000664000175000017500000000550713644133413025136 0ustar zuulzuul00000000000000# # 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 oslo_utils import encodeutils from oslo_utils import uuidutils import six from manilaclient.common._i18n import _ from manilaclient.common.apiclient import exceptions def find_resource(manager, name_or_id, **find_args): """Look for resource in a given manager. Used as a helper for the _find_* methods. Example: .. code-block:: python def _find_hypervisor(cs, hypervisor): #Get a hypervisor by name or ID. return cliutils.find_resource(cs.hypervisors, hypervisor) """ # first try to get entity as integer id try: return manager.get(int(name_or_id)) except (TypeError, ValueError, exceptions.NotFound): pass # now try to get entity as uuid try: if six.PY2: tmp_id = encodeutils.safe_encode(name_or_id) else: tmp_id = encodeutils.safe_decode(name_or_id) if uuidutils.is_uuid_like(tmp_id): return manager.get(tmp_id) except (TypeError, ValueError, exceptions.NotFound): pass # for str id which is not uuid if getattr(manager, 'is_alphanum_id_allowed', False): try: return manager.get(name_or_id) except exceptions.NotFound: pass try: try: return manager.find(human_id=name_or_id, **find_args) except exceptions.NotFound: pass # finally try to find entity by name try: resource = getattr(manager, 'resource_class', None) name_attr = resource.NAME_ATTR if resource else 'name' kwargs = {name_attr: name_or_id} kwargs.update(find_args) return manager.find(**kwargs) except exceptions.NotFound: msg = _("No %(name)s with a name or " "ID of '%(name_or_id)s' exists.") % \ {"name": manager.resource_class.__name__.lower(), "name_or_id": name_or_id} raise exceptions.CommandError(msg) except exceptions.NoUniqueMatch: msg = _("Multiple %(name)s matches found for " "'%(name_or_id)s', use an ID to be more specific.") % \ {"name": manager.resource_class.__name__.lower(), "name_or_id": name_or_id} raise exceptions.CommandError(msg) python-manilaclient-2.1.0/manilaclient/common/apiclient/__init__.py0000664000175000017500000000000013644133413025514 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/config.py0000664000175000017500000002401213644133413021773 0ustar zuulzuul00000000000000# 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 copy import os from oslo_config import cfg import oslo_log._options as log_options from manilaclient import api_versions # 1. Define opts # "auth_opts" are used by functional tests that are located in # directory "%project_root%/manilaclient/tests/functional" auth_opts = [ # Options for user with 'member' role. cfg.StrOpt("username", help="This should be the username of a user WITHOUT " "administrative privileges."), cfg.StrOpt("tenant_name", help="The non-administrative user's tenant name."), cfg.StrOpt("password", secret=True, help="The non-administrative user's password."), cfg.StrOpt("auth_url", help="URL for where to find the OpenStack Identity public " "API endpoint."), cfg.StrOpt("project_domain_name", help=("Project domain Name of user with 'member' role " "as specified for auth v3.")), cfg.StrOpt("project_domain_id", help=("Project domain ID of user with 'member' role " "as specified for auth v3.")), cfg.StrOpt("user_domain_name", help=("User domain Name of user with 'member' role " "as specified for auth v3.")), cfg.StrOpt("user_domain_id", help=("User domain ID of user with 'member' role " "as specified for auth v3.")), # Options for user with 'admin' role. cfg.StrOpt("admin_username", help="This should be the username of a user WITH " "administrative privileges."), cfg.StrOpt("admin_tenant_name", help="The administrative user's tenant name."), cfg.StrOpt("admin_password", secret=True, help="The administrative user's password."), cfg.StrOpt("admin_auth_url", help="URL for where to find the OpenStack Identity admin " "API endpoint."), cfg.StrOpt("admin_project_domain_name", help=("Project domain Name of user with 'admin' role " "as specified for auth v3.")), cfg.StrOpt("admin_project_domain_id", help=("Project domain ID of user with 'admin' role " "as specified for auth v3.")), cfg.StrOpt("admin_user_domain_name", help=("User domain Name of user with 'admin' role " "as specified for auth v3.")), cfg.StrOpt("admin_user_domain_id", help=("User domain ID of user with 'admin' role " "as specified for auth v3.")), # Other auth options cfg.BoolOpt("insecure", default=False, help="Disable SSL certificate verification."), ] base_opts = [ cfg.StrOpt("manila_exec_dir", default=os.environ.get( 'OS_MANILA_EXEC_DIR', os.path.join(os.path.abspath('.'), '.tox/functional/bin')), help="The path to manilaclient to be executed."), cfg.BoolOpt("suppress_errors_in_cleanup", default=True, help="Whether to suppress errors with clean up operation " "or not."), ] share_opts = [ cfg.StrOpt("min_api_microversion", default="1.0", help="The minimum API microversion is configured to be the " "value of the minimum microversion supported by " "Manilaclient functional tests. Defaults to 1.0."), cfg.StrOpt("max_api_microversion", default=api_versions.MAX_VERSION, help="The maximum API microversion is configured to be the " "value of the latest microversion supported by " "Manilaclient functional tests. Defaults to " "manilaclient's max supported API microversion."), cfg.StrOpt("share_network", help="Share network Name or ID, that will be used for shares. " "Some backend drivers require a share network for share " "creation."), cfg.StrOpt("share_network_subnet", help="Share network subnet ID. Some backend drivers require a " "share network for share creation."), cfg.StrOpt("admin_share_network", help="Share network Name or ID, that will be used for shares " "in admin tenant."), cfg.StrOpt("share_type", help="Share type Name or ID, that will be used with share " "creation scheduling. Optional."), cfg.ListOpt("enable_protocols", default=["nfs", "cifs"], help="List of all enabled protocols. The first protocol in " "the list will be used as the default protocol."), cfg.IntOpt("build_interval", default=3, help="Time in seconds between share availability checks."), cfg.IntOpt("build_timeout", default=500, help="Timeout in seconds to wait for a share to become " "available."), cfg.DictOpt('access_types_mapping', default={'nfs': 'ip', 'cifs': 'ip'}, help="Dict contains access types mapping to share " "protocol. It will be used to create access rules " "for shares. Format: ': ',..." "Allowed share protocols: nfs, cifs, cephfs, glusterfs, " "hdfs."), cfg.DictOpt('access_levels_mapping', default={'nfs': 'rw ro', 'cifs': 'rw'}, help="Dict contains access levels mapping to share " "protocol. It will be used to create access rules for " "shares. Format: ': ',... " "Allowed share protocols: nfs, cifs, cephfs, glusterfs, " "hdfs."), cfg.StrOpt("username_for_user_rules", default="stack", help="Username, that will be used in share access tests for " "user type of access."), cfg.StrOpt("replication_type", default="readable", choices=["readable", "writable", "dr"], help="Replication type to be used when running replication " "tests. This option is ignored if run_replication_tests " "is set to False."), cfg.BoolOpt("run_replication_tests", default=True, help="Defines whether to run tests for share replication " "or not. Disable this feature if manila driver used " "doesn't support share replication."), cfg.BoolOpt("run_snapshot_tests", default=True, help="Defines whether to run tests that use share snapshots " "or not. Disable this feature if used driver doesn't " "support it."), cfg.BoolOpt("run_share_servers_tests", default=True, help="Defines whether to run tests that use share servers " "or not. Disable this feature if used driver doesn't " "support it or when autodeletion of share servers " "is enabled."), cfg.BoolOpt("run_migration_tests", default=False, help="Defines whether to run share migration tests or not. " "Disable this feature if there is no more than one " "storage pool being tested or if used driver does not " "support it."), cfg.BoolOpt("run_mount_snapshot_tests", default=False, help="Defines whether to run mountable snapshots tests or " "not. Disable this feature if used driver doesn't " "support it."), cfg.BoolOpt("run_manage_tests", default=False, help="Defines whether to run manage/unmanage tests or " "not. Disable this feature if used driver does not " "support it."), ] # 2. Generate config PROJECT_NAME = 'manilaclient' DEFAULT_CONFIG_FILE = ( os.environ.get('OS_%s_CONFIG_FILE' % PROJECT_NAME.upper()) or '%s.conf' % PROJECT_NAME) DEFAULT_CONFIG_DIR = ( os.environ.get('OS_%s_CONFIG_DIR' % PROJECT_NAME.upper()) or os.path.join(os.path.abspath(os.path.dirname(os.path.dirname(__file__))), "etc/manilaclient") ) DEFAULT_CONFIG_PATH = os.path.join(DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILE) FAILOVER_CONFIG_PATH = '/etc/%(pn)s/%(cn)s' % { 'pn': PROJECT_NAME, 'cn': DEFAULT_CONFIG_FILE} CONFIG_FILES = [] if os.path.isfile(DEFAULT_CONFIG_PATH): CONFIG_FILES.append(DEFAULT_CONFIG_PATH) if os.path.isfile(FAILOVER_CONFIG_PATH): CONFIG_FILES.append(FAILOVER_CONFIG_PATH) CONF = cfg.CONF if CONFIG_FILES: CONF([], project=PROJECT_NAME, default_config_files=CONFIG_FILES) else: CONF([], project=PROJECT_NAME) # 3. Register opts CONF.register_opts(auth_opts) CONF.register_opts(base_opts) CONF.register_opts(share_opts) # 4. Define list_opts for config sample generator def list_opts(): """Return a list of oslo_config options available in Manilaclient.""" opts = [ (None, copy.deepcopy(auth_opts)), (None, copy.deepcopy(base_opts)), (None, copy.deepcopy(share_opts)), ] opts.extend(log_options.list_opts()) return opts python-manilaclient-2.1.0/manilaclient/client.py0000664000175000017500000000452313644133413022011 0ustar zuulzuul00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2011 Piston Cloud Computing, 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. """ OpenStack Client interface. Handles the REST calls and responses. """ from oslo_utils import importutils from manilaclient import api_versions from manilaclient import exceptions def get_client_class(version): version_map = { '1': 'manilaclient.v1.client.Client', '2': 'manilaclient.v2.client.Client', } try: client_path = version_map[str(version)] except (KeyError, ValueError): msg = "Invalid client version '%s'. must be one of: %s" % ( (version, ', '.join(version_map))) raise exceptions.UnsupportedVersion(msg) return importutils.import_class(client_path) def Client(client_version, *args, **kwargs): def _convert_to_api_version(version): """Convert version to an APIVersion object unless it already is one.""" if hasattr(version, 'get_major_version'): api_version = version else: if version in ('1', '1.0'): api_version = api_versions.APIVersion( api_versions.DEPRECATED_VERSION) elif version == '2': api_version = api_versions.APIVersion(api_versions.MIN_VERSION) else: api_version = api_versions.APIVersion(version) return api_version api_version = _convert_to_api_version(client_version) client_class = get_client_class(api_version.get_major_version()) # Make sure the kwarg api_version is set with an APIVersion object. # 1st choice is to use the incoming kwarg. 2nd choice is the positional. kwargs['api_version'] = _convert_to_api_version( kwargs.get('api_version', api_version)) return client_class(*args, **kwargs) python-manilaclient-2.1.0/manilaclient/v1/0000775000175000017500000000000013644133466020513 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/v1/share_types.py0000664000175000017500000000234413644133413023406 0ustar zuulzuul00000000000000# Copyright (c) 2011 Rackspace US, Inc. # Copyright 2015 Chuck Fouts # 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 sys import warnings from manilaclient.v2 import share_types warnings.warn("Module manilaclient.v1.share_types is deprecated (taken as " "a basis for manilaclient.v2.share_types). " "The preferable way to get a client class or object is to use " "the manilaclient.client module.") class MovedModule(object): def __init__(self, new_module): self.new_module = new_module def __getattr__(self, attr): return getattr(self.new_module, attr) sys.modules["manilaclient.v1.share_types"] = MovedModule(share_types) python-manilaclient-2.1.0/manilaclient/v1/shares.py0000664000175000017500000000227313644133413022346 0ustar zuulzuul00000000000000# Copyright 2012 NetApp # Copyright 2015 Chuck Fouts # 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 sys import warnings from manilaclient.v2 import shares warnings.warn("Module manilaclient.v1.shares is deprecated (taken as " "a basis for manilaclient.v2.shares). " "The preferable way to get a client class or object is to use " "the manilaclient.client module.") class MovedModule(object): def __init__(self, new_module): self.new_module = new_module def __getattr__(self, attr): return getattr(self.new_module, attr) sys.modules["manilaclient.v1.shares"] = MovedModule(shares) python-manilaclient-2.1.0/manilaclient/v1/share_servers.py0000664000175000017500000000235413644133413023734 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # Copyright 2015 Chuck Fouts # 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 sys import warnings from manilaclient.v2 import share_servers warnings.warn("Module manilaclient.v1.share_servers is deprecated (taken as " "a basis for manilaclient.v2.share_servers). " "The preferable way to get a client class or object is to use " "the manilaclient.client module.") class MovedModule(object): def __init__(self, new_module): self.new_module = new_module def __getattr__(self, attr): return getattr(self.new_module, attr) sys.modules["manilaclient.v1.share_servers"] = MovedModule(share_servers) python-manilaclient-2.1.0/manilaclient/v1/share_type_access.py0000664000175000017500000000240513644133413024542 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # Copyright 2015 Chuck Fouts # 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 sys import warnings from manilaclient.v2 import share_type_access warnings.warn("Module manilaclient.v1.share_type_access is deprecated (taken " "as a basis for manilaclient.v2.share_type_access). " "The preferable way to get a client class or object is to use " "the manilaclient.client module.") class MovedModule(object): def __init__(self, new_module): self.new_module = new_module def __getattr__(self, attr): return getattr(self.new_module, attr) sys.modules[ "manilaclient.v1.share_type_access"] = MovedModule(share_type_access) python-manilaclient-2.1.0/manilaclient/v1/quota_classes.py0000664000175000017500000000235413644133413023727 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2015 Chuck Fouts # 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 sys import warnings from manilaclient.v2 import quota_classes warnings.warn("Module manilaclient.v1.quota_classes is deprecated (taken as " "a basis for manilaclient.v2.quota_classes). " "The preferable way to get a client class or object is to use " "the manilaclient.client module.") class MovedModule(object): def __init__(self, new_module): self.new_module = new_module def __getattr__(self, attr): return getattr(self.new_module, attr) sys.modules["manilaclient.v1.quota_classes"] = MovedModule(quota_classes) python-manilaclient-2.1.0/manilaclient/v1/share_snapshots.py0000664000175000017500000000235013644133413024261 0ustar zuulzuul00000000000000# Copyright 2012 NetApp # Copyright 2015 Chuck Fouts # 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 sys import warnings from manilaclient.v2 import share_snapshots warnings.warn("Module manilaclient.v1.share_snapshots is deprecated (taken as " "a basis for manilaclient.v2.share_snapshots). " "The preferable way to get a client class or object is to use " "the manilaclient.client module.") class MovedModule(object): def __init__(self, new_module): self.new_module = new_module def __getattr__(self, attr): return getattr(self.new_module, attr) sys.modules["manilaclient.v1.share_snapshots"] = MovedModule(share_snapshots) python-manilaclient-2.1.0/manilaclient/v1/contrib/0000775000175000017500000000000013644133466022153 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/v1/contrib/list_extensions.py0000664000175000017500000000234513644133413025753 0ustar zuulzuul00000000000000# 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. import sys import warnings from manilaclient.v2.contrib import list_extensions warnings.warn( "Module manilaclient.v1.contrib.list_extensions is deprecated " "(taken as a basis for manilaclient.v2.contrib.list_extensions). " "The preferable way to get a client class or object is to use " "the manilaclient.client module.") class MovedModule(object): def __init__(self, new_module): self.new_module = new_module def __getattr__(self, attr): return getattr(self.new_module, attr) sys.modules["manilaclient.v1.contrib.list_extensions"] = MovedModule( list_extensions) python-manilaclient-2.1.0/manilaclient/v1/contrib/__init__.py0000664000175000017500000000000013644133413024242 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/v1/limits.py0000664000175000017500000000231113644133413022353 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2015 Chuck Fouts # 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 sys import warnings from manilaclient.v2 import limits warnings.warn("Module manilaclient.v1.limits is deprecated (taken as a basis " "for manilaclient.v2.limits). " "The preferable way to get a client class or object is to use " "the manilaclient.client module.") class MovedModule(object): def __init__(self, new_module): self.new_module = new_module def __getattr__(self, attr): return getattr(self.new_module, attr) sys.modules["manilaclient.v1.limits"] = MovedModule(limits) python-manilaclient-2.1.0/manilaclient/v1/quotas.py0000664000175000017500000000231113644133413022366 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2015 Chuck Fouts # 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 sys import warnings from manilaclient.v2 import quotas warnings.warn("Module manilaclient.v1.quotas is deprecated (taken as " "a basis for manilaclient.v2.quotas). " "The preferable way to get a client class or object is to use " "the manilaclient.client module.") class MovedModule(object): def __init__(self, new_module): self.new_module = new_module def __getattr__(self, attr): return getattr(self.new_module, attr) sys.modules["manilaclient.v1.quotas"] = MovedModule(quotas) python-manilaclient-2.1.0/manilaclient/v1/services.py0000664000175000017500000000232313644133413022700 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # Copyright 2015 Chuck Fouts # 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 sys import warnings from manilaclient.v2 import services warnings.warn("Module manilaclient.v1.services is deprecated (taken as " "a basis for manilaclient.v2.services). " "The preferable way to get a client class or object is to use " "the manilaclient.client module.") class MovedModule(object): def __init__(self, new_module): self.new_module = new_module def __getattr__(self, attr): return getattr(self.new_module, attr) sys.modules["manilaclient.v1.services"] = MovedModule(services) python-manilaclient-2.1.0/manilaclient/v1/client.py0000664000175000017500000002647313644133413022347 0ustar zuulzuul00000000000000# 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 debtcollector import removals from keystoneauth1 import adapter from keystoneauth1 import session from keystoneclient import client as ks_client import manilaclient from manilaclient.common import constants from manilaclient.common import httpclient from manilaclient import exceptions from manilaclient.v2 import limits from manilaclient.v2 import quota_classes from manilaclient.v2 import quotas from manilaclient.v2 import scheduler_stats from manilaclient.v2 import security_services from manilaclient.v2 import services from manilaclient.v2 import share_networks from manilaclient.v2 import share_servers from manilaclient.v2 import share_snapshots from manilaclient.v2 import share_type_access from manilaclient.v2 import share_types from manilaclient.v2 import shares @removals.removed_class("Client", message="Please use 'v2.Client' instead", removal_version='2.0.0') class Client(object): """Top-level object to access the OpenStack Manila API. Create an instance with your creds:: >>> client = Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) Or, alternatively, you can create a client instance using the keystoneauth1.session API:: >>> from keystoneclient.auth.identity import v2 >>> from keystoneauth1 import session >>> from manilaclient import client >>> auth = v2.Password(auth_url=AUTH_URL, username=USERNAME, password=PASSWORD, tenant_name=PROJECT_ID) >>> sess = session.Session(auth=auth) >>> manila = client.Client(VERSION, session=sess) Then call methods on its managers:: >>> client.shares.list() ... """ @removals.removed_kwarg( 'share_service_name', message="Please use 'service_name' instead", removal_version='2.0.0') @removals.removed_kwarg( 'proxy_tenant_id', message="This is not used anywhere", removal_version='2.0.0') @removals.removed_kwarg( 'proxy_token', message="This is not used anywhere", removal_version='2.0.0') @removals.removed_kwarg( 'os_cache', message="Please use 'use_keyring' instead", removal_version='2.0.0') @removals.removed_kwarg( 'api_key', message="Please use 'password' instead", removal_version='2.0.0') def __init__(self, username=None, api_key=None, project_id=None, auth_url=None, insecure=False, timeout=None, tenant_id=None, project_name=None, region_name=None, endpoint_type='publicURL', extensions=None, service_type=constants.V1_SERVICE_TYPE, service_name=None, retries=None, http_log_debug=False, input_auth_token=None, session=None, auth=None, cacert=None, service_catalog_url=None, user_agent='python-manilaclient', use_keyring=False, force_new_token=False, cached_token_lifetime=300, api_version=manilaclient.API_DEPRECATED_VERSION, user_id=None, user_domain_id=None, user_domain_name=None, project_domain_id=None, project_domain_name=None, cert=None, password=None, **kwargs): self.username = username self.password = password or api_key self.tenant_id = tenant_id or project_id self.tenant_name = project_name self.user_id = user_id self.project_id = project_id or tenant_id self.project_name = project_name self.user_domain_id = user_domain_id self.user_domain_name = user_domain_name self.project_domain_id = project_domain_id self.project_domain_name = project_domain_name self.endpoint_type = endpoint_type self.auth_url = auth_url self.region_name = region_name self.cacert = cacert self.cert = cert self.insecure = insecure self.use_keyring = use_keyring self.force_new_token = force_new_token self.cached_token_lifetime = cached_token_lifetime service_name = kwargs.get("share_service_name", service_name) if input_auth_token and not service_catalog_url: msg = ("For token-based authentication you should " "provide 'input_auth_token' and 'service_catalog_url'.") raise exceptions.ClientException(msg) self.project_id = tenant_id if tenant_id is not None else project_id self.keystone_client = None self.session = session # NOTE(u_glide): token authorization has highest priority. # That's why session and/or password will be ignored # if token is provided. if not input_auth_token: if session: self.keystone_client = adapter.LegacyJsonAdapter( session=session, auth=auth, interface=endpoint_type, service_type=service_type, service_name=service_name, region_name=region_name) input_auth_token = self.keystone_client.session.get_token(auth) else: self.keystone_client = self._get_keystone_client() input_auth_token = self.keystone_client.auth_token if not input_auth_token: raise RuntimeError("Not Authorized") if session and not service_catalog_url: service_catalog_url = self.keystone_client.session.get_endpoint( auth, interface=endpoint_type, service_type=service_type) elif not service_catalog_url: catalog = self.keystone_client.service_catalog.get_endpoints( service_type) for catalog_entry in catalog.get(service_type, []): if (catalog_entry.get("interface") == ( endpoint_type.lower().split("url")[0]) or catalog_entry.get(endpoint_type)): if (region_name and not region_name == ( catalog_entry.get( "region", catalog_entry.get("region_id")))): continue service_catalog_url = catalog_entry.get( "url", catalog_entry.get(endpoint_type)) break if not service_catalog_url: raise RuntimeError("Could not find Manila endpoint in catalog") self.api_version = api_version self.client = httpclient.HTTPClient(service_catalog_url, input_auth_token, user_agent, insecure=insecure, cacert=cacert, timeout=timeout, retries=retries, http_log_debug=http_log_debug, api_version=self.api_version) self.limits = limits.LimitsManager(self) self.services = services.ServiceManager(self) self.security_services = security_services.SecurityServiceManager(self) self.share_networks = share_networks.ShareNetworkManager(self) self.quota_classes = quota_classes.QuotaClassSetManager(self) self.quotas = quotas.QuotaSetManager(self) self.shares = shares.ShareManager(self) self.share_snapshots = share_snapshots.ShareSnapshotManager(self) self.share_types = share_types.ShareTypeManager(self) self.share_type_access = share_type_access.ShareTypeAccessManager(self) self.share_servers = share_servers.ShareServerManager(self) self.pools = scheduler_stats.PoolManager(self) self._load_extensions(extensions) def _load_extensions(self, extensions): if not extensions: return for extension in extensions: if extension.manager_class: setattr(self, extension.name, extension.manager_class(self)) @removals.remove( message="authenticate() method is deprecated. Client automatically " "makes authentication call in the constructor.", removal_version='2.0.0') def authenticate(self): """Authenticate against the server. Normally this is called automatically when you first access the API, but you can call this method to force authentication right now. Returns on success; raises :exc:`exceptions.Unauthorized` if the credentials are wrong. """ pass def _get_keystone_client(self): # First create a Keystone session if self.insecure: verify = False else: verify = self.cacert or True ks_session = session.Session(verify=verify, cert=self.cert) # Discover the supported keystone versions using the given url ks_discover = session.discover.Discover(ks_session, self.auth_url) # Inspect the auth_url to see the supported version. If both v3 and v2 # are supported, then use the highest version if possible. v2_auth_url = ks_discover.url_for('v2.0') v3_auth_url = ks_discover.url_for('v3.0') if v3_auth_url: keystone_client = ks_client.Client( session=ks_session, version=(3, 0), auth_url=v3_auth_url, username=self.username, password=self.password, user_id=self.user_id, user_domain_name=self.user_domain_name, user_domain_id=self.user_domain_id, project_id=self.project_id or self.tenant_id, project_name=self.project_name, project_domain_name=self.project_domain_name, project_domain_id=self.project_domain_id, region_name=self.region_name) elif v2_auth_url: keystone_client = ks_client.Client( session=ks_session, version=(2, 0), auth_url=v2_auth_url, username=self.username, password=self.password, tenant_id=self.tenant_id, tenant_name=self.tenant_name, region_name=self.region_name, cert=self.cert, use_keyring=self.use_keyring, force_new_token=self.force_new_token, stale_duration=self.cached_token_lifetime) else: raise exceptions.CommandError( 'Unable to determine the Keystone version to authenticate ' 'with using the given auth_url.') keystone_client.authenticate() return keystone_client python-manilaclient-2.1.0/manilaclient/v1/share_networks.py0000664000175000017500000000236113644133413024115 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2015 Chuck Fouts # 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 sys import warnings from manilaclient.v2 import share_networks warnings.warn("Module manilaclient.v1.share_networks is deprecated (taken as " "a basis for manilaclient.v2.share_networks). " "The preferable way to get a client class or object is to use " "the manilaclient.client module.") class MovedModule(object): def __init__(self, new_module): self.new_module = new_module def __getattr__(self, attr): return getattr(self.new_module, attr) sys.modules["manilaclient.v1.share_networks"] = MovedModule(share_networks) python-manilaclient-2.1.0/manilaclient/v1/__init__.py0000664000175000017500000000162513644133413022620 0ustar zuulzuul00000000000000# Copyright (c) 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. import sys from manilaclient import v2 class MovedModule(object): def __init__(self, new_module): self.new_module = new_module def __getattr__(self, attr): return getattr(self.new_module, attr) sys.modules["maniliaclient.v1"] = MovedModule(v2) python-manilaclient-2.1.0/manilaclient/v1/security_services.py0000664000175000017500000000240513644133413024630 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2015 Chuck Fouts # 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 sys import warnings from manilaclient.v2 import security_services warnings.warn("Module manilaclient.v1.security_services is deprecated (taken " "as a basis for manilaclient.v2.security_services). " "The preferable way to get a client class or object is to use " "the manilaclient.client module.") class MovedModule(object): def __init__(self, new_module): self.new_module = new_module def __getattr__(self, attr): return getattr(self.new_module, attr) sys.modules[ "manilaclient.v1.security_services"] = MovedModule(security_services) python-manilaclient-2.1.0/manilaclient/v1/scheduler_stats.py0000664000175000017500000000236513644133413024257 0ustar zuulzuul00000000000000# Copyright (c) 2015 Clinton Knight. # Copyright 2015 Chuck Fouts # 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 sys import warnings from manilaclient.v2 import scheduler_stats warnings.warn("Module manilaclient.v1.scheduler_stats is deprecated (taken as " "a basis for manilaclient.v2.scheduler_stats). " "The preferable way to get a client class or object is to use " "the manilaclient.client module.") class MovedModule(object): def __init__(self, new_module): self.new_module = new_module def __getattr__(self, attr): return getattr(self.new_module, attr) sys.modules["manilaclient.v1.scheduler_stats"] = MovedModule(scheduler_stats) python-manilaclient-2.1.0/manilaclient/__init__.py0000664000175000017500000000234013644133413022265 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # Copyright 2015 Chuck Fouts # 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. __all__ = ['__version__'] import pbr.version from manilaclient import api_versions version_info = pbr.version.VersionInfo('python-manilaclient') # We have a circular import problem when we first run python setup.py sdist # It's harmless, so deflect it. try: __version__ = version_info.version_string() except AttributeError: __version__ = None API_MAX_VERSION = api_versions.APIVersion(api_versions.MAX_VERSION) API_MIN_VERSION = api_versions.APIVersion(api_versions.MIN_VERSION) API_DEPRECATED_VERSION = api_versions.APIVersion( api_versions.DEPRECATED_VERSION) python-manilaclient-2.1.0/manilaclient/shell.py0000664000175000017500000007150513644133413021646 0ustar zuulzuul00000000000000# 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. """ from __future__ import print_function import argparse import glob import imp import itertools import logging import os import pkgutil import sys from oslo_utils import encodeutils import six 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' 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(six.text_type(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(ManilaClientArgumentParser, self).__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, "error: %(errmsg)s\nTry '%(mainp)s help %(subp)s'" " for more information.\n" % {'errmsg': message.split(choose_from)[0], 'mainp': progparts[0], 'subp': progparts[2]}) 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( ManilaClientArgumentParser, self)._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(object): 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) 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 _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 = imp.load_source(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 = six.text_type(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 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=args.os_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) args.func(self.cs, args) 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("'%s' is not a valid subcommand" % args.command) 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 = '%s%s' % (heading[0].upper(), heading[1:]) super(OpenStackHelpFormatter, self).start_section(heading) def main(): try: if sys.version_info >= (3, 0): OpenStackManilaShell().main(sys.argv[1:]) else: OpenStackManilaShell().main( map(encodeutils.safe_decode, 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("ERROR: %s" % six.text_type(e), file=sys.stderr) sys.exit(1) if __name__ == "__main__": main() python-manilaclient-2.1.0/manilaclient/tests/0000775000175000017500000000000013644133466021327 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/functional/0000775000175000017500000000000013644133466023471 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/functional/exceptions.py0000664000175000017500000000421613644133413026217 0ustar zuulzuul00000000000000# 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 tempest.lib import exceptions """ Exceptions for functional tests. """ class ResourceReleaseFailed(exceptions.TempestException): message = "Failed to release resource '%(res_type)s' with id '%(res_id)s'." class InvalidResource(exceptions.TempestException): message = "Provided invalid resource: %(message)s" class InvalidData(exceptions.TempestException): message = "Provided invalid data: %(message)s" class ShareTypeNotFound(exceptions.NotFound): message = "Share type '%(share_type)s' was not found." class InvalidConfiguration(exceptions.TempestException): message = "Invalid configuration: %(reason)s" class ShareBuildErrorException(exceptions.TempestException): message = "Share %(share)s failed to build and is in ERROR status." class ShareReplicaBuildErrorException(exceptions.TempestException): message = ("Share replica %(replica)s failed to build and is in ERROR " "status.") class SnapshotBuildErrorException(exceptions.TempestException): message = "Snapshot %(snapshot)s failed to build and is in ERROR status." class AccessRuleCreateErrorException(exceptions.TempestException): message = "Access rule %(access)s failed to create and is in ERROR state." class AccessRuleDeleteErrorException(exceptions.TempestException): message = "Access rule %(access)s failed to delete and is in ERROR state." class ShareMigrationException(exceptions.TempestException): message = ("Share %(share_id)s failed to migrate from " "host %(src)s to host %(dest)s.") python-manilaclient-2.1.0/manilaclient/tests/functional/test_scheduler_stats.py0000664000175000017500000000355013644133413030271 0ustar zuulzuul00000000000000# 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') python-manilaclient-2.1.0/manilaclient/tests/functional/test_quotas.py0000664000175000017500000003143513644133413026414 0ustar zuulzuul00000000000000# 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 = 'quota-show --tenant-id %s --share-type %s' % ( self.project_id, 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 = 'quota-show --tenant-id %s' % 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 = "Microversion '%s' not supported." % microversion 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 = 'quota-show --tenant-id %s ' % 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 %s --share-groups %s ' '--share-group-snapshots %s') % ( 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 = 'quota-delete --tenant-id %s --share-type %s' % ( self.project_id, 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 %s --share-groups %s ' '--share-group-snapshots %s') % ( 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 = 'quota-update %s %s 13' % (self.project_id, arg) 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 = 'quota-update %s %s 10' % (self.project_id, arg) 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 = 'quota-update %s --share-type %s %s 13' % ( self.project_id, self.st_id, arg) 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 = "Microversion '%s' not supported." % microversion raise self.skipException(msg) # Get project quotas cmd = 'quota-show --tenant-id %s ' % 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 %s ' '--replica-gigabytes %s') % ( st_custom_quotas['share_replicas'], st_custom_quotas['replica_gigabytes']) # Update quotas for share type cmd = ('quota-update %s --share-type %s ' '--shares %s --gigabytes %s --snapshots %s ' '--snapshot-gigabytes %s') % ( 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 = 'quota-delete --tenant-id %s --share-type %s' % ( self.project_id, 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 = 'quota-show --tenant-id %s --share-type %s' % ( self.project_id, 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 %s --share-type %s --shares %s' % ( 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 = 'quota-delete --tenant-id %s --share-type %s' % ( self.project_id, 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( 'quota-%s' % 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") python-manilaclient-2.1.0/manilaclient/tests/functional/osc/0000775000175000017500000000000013644133466024255 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/functional/osc/base.py0000664000175000017500000001406513644133413025537 0ustar zuulzuul00000000000000# 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 six import time from tempest.lib.cli import base from tempest.lib.common.utils import data_utils from manilaclient import config CONF = config.CONF class OSCClientTestBase(base.ClientTestBase): """Base class for OSC manila functional tests""" def _get_clients(self): return base.CLIClient( username=CONF.admin_username or CONF.username, password=CONF.admin_password or CONF.password, tenant_name=CONF.admin_tenant_name or CONF.tenant_name, uri=CONF.admin_auth_url or CONF.auth_url, cli_dir=CONF.manila_exec_dir, insecure=CONF.insecure, project_domain_name=(CONF.admin_project_domain_name or CONF.project_domain_name or None), project_domain_id=(CONF.admin_project_domain_id or CONF.project_domain_id or None), user_domain_name=(CONF.admin_user_domain_name or CONF.user_domain_name or None), user_domain_id=(CONF.admin_user_domain_id or CONF.user_domain_id or None) ) def _get_property_from_output(self, output): """Creates a dictionary from the given output""" obj = {} items = self.parser.listing(output) for item in items: obj[item['Field']] = six.text_type(item['Value']) return obj def _wait_for_object_status(self, object_name, object_id, status, timeout=CONF.build_timeout, interval=CONF.build_interval): """Waits for a object to reach a given status.""" start_time = time.time() while time.time() - start_time < timeout: if status == self.openstack( '%(obj)s show -c status -f value %(id)s' % {'obj': object_name, 'id': object_id}).rstrip(): break time.sleep(interval) else: self.fail("%s %s did not reach status %s after %d seconds." % (object_name, object_id, status, timeout)) def check_object_deleted(self, object_name, object_id, timeout=CONF.build_timeout, interval=CONF.build_interval): """Check that object deleted successfully""" cmd = '%s list -c ID -f value' % object_name start_time = time.time() while time.time() - start_time < timeout: if object_id not in self.openstack(cmd): break time.sleep(interval) else: self.fail("%s %s not deleted after %d seconds." % (object_name, object_id, timeout)) def openstack(self, action, flags='', params='', fail_ok=False, merge_stderr=False): """Executes openstack command for given action""" if '--os-share-api-version' not in flags: flags = ( flags + '--os-share-api-version %s' % CONF.max_api_microversion) return self.clients.openstack(action, flags=flags, params=params, fail_ok=fail_ok, merge_stderr=merge_stderr) def listing_result(self, object_name, command): """Returns output for the given command as list of dictionaries""" output = self.openstack(object_name, params=command) result = self.parser.listing(output) return result def dict_result(self, object_name, command): """Returns output for the given command as dictionary""" output = self.openstack(object_name, params=command) result_dict = self._get_property_from_output(output) return result_dict def create_share(self, share_protocol=None, size=None, name=None, snapshot_id=None, properties=None, share_network=None, description=None, public=False, share_type=None, availability_zone=None, share_group=None, add_cleanup=True): name = name or data_utils.rand_name('autotest_share_name') # share_type = dhss_false until we have implemented # share network commands for osc share_type = share_type or 'dhss_false' cmd = ('create ' '%(protocol)s %(size)s %(name)s %(desc)s %(public)s %(stype)s' % {'protocol': share_protocol or 'NFS', 'size': size or '1', 'name': '--name %s' % name, 'desc': '--description %s' % description, 'public': '--public %s' % public, 'stype': '--share-type %s' % share_type}) if snapshot_id: cmd = cmd + ' --snapshot-id %s' % snapshot_id if properties: for key, value in properties.items(): cmd = (cmd + ' --property %(key)s=%(value)s' % {'key': key, 'value': value}) if share_network: cmd = cmd + ' --share-network %s' % share_network if availability_zone: cmd = cmd + ' --availability-zone %s' % availability_zone if share_group: cmd = cmd + ' --share-group %s' % share_group share_object = self.dict_result('share', cmd) self._wait_for_object_status( 'share', share_object['id'], 'available') if add_cleanup: self.addCleanup( self.openstack, 'share delete %s' % share_object['id'] ) return share_object python-manilaclient-2.1.0/manilaclient/tests/functional/osc/__init__.py0000664000175000017500000000000013644133413026344 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/functional/osc/test_shares.py0000664000175000017500000000464013644133413027147 0ustar zuulzuul00000000000000# 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.tests.functional.osc import base class SharesCLITest(base.OSCClientTestBase): def test_openstack_share_create(self): share_name = 'test_create_share' share = self.create_share(name=share_name) self.assertEqual(share['share_proto'], 'NFS') self.assertEqual(share['size'], '1') self.assertEqual(share['name'], share_name) shares_list = self.listing_result('share', 'list') self.assertIn(share['id'], [item['ID'] for item in shares_list]) def test_openstack_share_list(self): share = self.create_share() shares_list = self.listing_result('share', 'list') self.assertTableStruct(shares_list, [ 'ID', 'Name', 'Size', 'Share Proto', 'Status', 'Is Public', 'Share Type Name', 'Host', 'Availability Zone' ]) self.assertIn(share['id'], [item['ID'] for item in shares_list]) def test_openstack_share_show(self): share = self.create_share() result = self.dict_result('share', 'show %s' % share['id']) self.assertEqual(share['id'], result['id']) listing_result = self.listing_result('share', 'show %s' % share['id']) self.assertTableStruct(listing_result, [ 'Field', 'Value' ]) def test_openstack_share_delete(self): share = self.create_share(add_cleanup=False) shares_list = self.listing_result('share', 'list') self.assertIn(share['id'], [item['ID'] for item in shares_list]) self.openstack('share delete %s' % share['id']) self.check_object_deleted('share', share['id']) shares_list_after_delete = self.listing_result('share', 'list') self.assertNotIn( share['id'], [item['ID'] for item in shares_list_after_delete]) python-manilaclient-2.1.0/manilaclient/tests/functional/test_share_types.py0000664000175000017500000005002013644133413027415 0ustar zuulzuul00000000000000# 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 = 'driver_handles_share_servers : %s' % 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(('{} : {}'.format(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('%s : %s' % (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('driver_handles_share_servers : %s' % dhss, extra_specs) python-manilaclient-2.1.0/manilaclient/tests/functional/base.py0000664000175000017500000004471113644133413024754 0ustar zuulzuul00000000000000# 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(object): """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(BaseTestCase, self).setUp() self.addCleanup(self.clear_resources) @classmethod def tearDownClass(cls): super(BaseTestCase, cls).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( "Microversion '%s' is not supported." % microversion) @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, 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, 'microversion': microversion, } 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: client.wait_for_resource_status(share['id'], constants.STATUS_AVAILABLE) return share @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, 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, } 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, microversion=None): client = client or cls.get_user_client() share_replica = client.create_share_replica( share_id, 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 python-manilaclient-2.1.0/manilaclient/tests/functional/test_availability_zones.py0000664000175000017500000000414213644133413030763 0ustar zuulzuul00000000000000# 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']) python-manilaclient-2.1.0/manilaclient/tests/functional/test_shares_listing.py0000664000175000017500000003147413644133413030121 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # 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(SharesListReadWriteTest, self).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.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.get_user_client()) for share_id in (self.private_share['id'], self.public_share['id'], self.admin_private_share['id']): self.get_admin_client().wait_for_resource_status( share_id, constants.STATUS_AVAILABLE) 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: 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 for k, v in filters.items(): if k in ('share_network', 'share-network'): k = 'share_network_id' if v != 'deleting' and get[k] == 'deleting': continue self.assertEqual(v, get[k]) 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=u'共享名称', description=u'共享描述', public=True, client=self.get_user_client()) filters = {'name~': u'名称'} shares = self.user_client.list_shares(filters=filters) self.assertGreater(len(shares), 0) filters = {'description~': u'描述'} 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)) python-manilaclient-2.1.0/manilaclient/tests/functional/test_shares_metadata.py0000664000175000017500000001241713644133413030224 0ustar zuulzuul00000000000000# 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(SharesMetadataReadWriteTest, self).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 = 'key%s' % i self.assertIn(key, metadata) self.assertEqual('value%s' % 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]) python-manilaclient-2.1.0/manilaclient/tests/functional/utils.py0000664000175000017500000001160413644133413025175 0ustar zuulzuul00000000000000# 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 six from tempest.lib.cli import output_parser import testtools from manilaclient import api_versions from manilaclient import config CONF = config.CONF def multi_line_row_table(output_lines, group_by_column_index=0): parsed_table = output_parser.table(output_lines) rows = parsed_table['values'] row_index = 0 def get_column_index(column_name, headers, default): return next( (i for i, h in enumerate(headers) if h.lower() == column_name), default ) if group_by_column_index is None: group_by_column_index = get_column_index( 'id', parsed_table['headers'], 0) def is_embedded_table(parsed_rows): def is_table_border(t): return six.text_type(t).startswith('+') return (isinstance(parsed_rows, list) and len(parsed_rows) > 3 and is_table_border(parsed_rows[0]) and is_table_border(parsed_rows[-1])) def merge_cells(master_cell, value_cell): if value_cell: if not isinstance(master_cell, list): master_cell = [master_cell] master_cell.append(value_cell) if is_embedded_table(master_cell): return multi_line_row_table('\n'.join(master_cell), None) return master_cell def is_empty_row(row): empty_cells = 0 for cell in row: if cell == '': empty_cells += 1 return len(row) == empty_cells while row_index < len(rows): row = rows[row_index] line_with_value = row_index > 0 and row[group_by_column_index] == '' if line_with_value and not is_empty_row(row): rows[row_index - 1] = list(map(merge_cells, rows[row_index - 1], rows.pop(row_index))) else: row_index += 1 return parsed_table def listing(output_lines): """Return list of dicts with basic item info parsed from cli output.""" items = [] table_ = multi_line_row_table(output_lines) for row in table_['values']: item = {} for col_idx, col_key in enumerate(table_['headers']): item[col_key] = row[col_idx] items.append(item) return items def details(output_lines): """Returns dict parsed from CLI output.""" result = listing(output_lines) d = {} for item in result: d.update({item['Property']: item['Value']}) return d def is_microversion_supported(microversion): return ( api_versions.APIVersion(CONF.min_api_microversion) <= api_versions.APIVersion(microversion) <= api_versions.APIVersion(CONF.max_api_microversion) ) def skip_if_microversion_not_supported(microversion): """Decorator for tests that are microversion-specific.""" if not is_microversion_supported(microversion): reason = ("Skipped. Test requires microversion %s that is not " "allowed to be used by configuration." % microversion) return testtools.skip(reason) return lambda f: f def choose_matching_backend(share, pools, share_type): extra_specs = {} # convert extra-specs in provided type to dict format pair = [x.strip() for x in share_type['required_extra_specs'].split(':')] if len(pair) == 2: value = (True if six.text_type(pair[1]).lower() == 'true' else False if six.text_type(pair[1]).lower() == 'false' else pair[1]) extra_specs[pair[0]] = value selected_pool = next( (x for x in pools if (x['Name'] != share['host'] and all( y in ast.literal_eval(x['Capabilities']).items() for y in extra_specs.items()))), None) return selected_pool['Name'] def share_network_subnets_are_supported(): return is_microversion_supported('2.51') def get_subnet_by_availability_zone_name(client, share_network_id, az_name): subnets = client.get_share_network_subnets(share_network_id) return next((subnet for subnet in subnets if subnet['availability_zone'] == az_name), None) def get_default_subnet(client, share_network_id): return get_subnet_by_availability_zone_name(client, share_network_id, 'None') python-manilaclient-2.1.0/manilaclient/tests/functional/test_export_locations.py0000664000175000017500000001501413644133413030467 0ustar zuulzuul00000000000000# 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(ExportLocationReadWriteTest, self).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]) python-manilaclient-2.1.0/manilaclient/tests/functional/test_limits.py0000664000175000017500000000175213644133413026400 0ustar zuulzuul00000000000000# 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') python-manilaclient-2.1.0/manilaclient/tests/functional/test_share_networks.py0000664000175000017500000002770013644133413030136 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # 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 from manilaclient.tests.functional import base from manilaclient.tests.functional import utils @ddt.ddt class ShareNetworksReadWriteTest(base.BaseTestCase): def setUp(self): super(ShareNetworksReadWriteTest, self).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=u'网络名称', description=u'网络描述', neutron_net_id='fake_neutron_net_id', neutron_subnet_id='fake_neutron_subnet_id', ) filters = {'name~': u'名称'} share_networks = self.admin_client.list_share_networks( filters=filters) self.assertGreater(len(share_networks), 0) filters = {'description~': u'描述'} share_networks = self.admin_client.list_share_networks( filters=filters) self.assertGreater(len(share_networks), 0) python-manilaclient-2.1.0/manilaclient/tests/functional/test_share_access.py0000664000175000017500000003206713644133413027525 0ustar zuulzuul00000000000000# 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(ShareAccessReadWriteBase, self).setUp() if self.protocol not in CONF.enable_protocols: message = "%s tests are disabled." % self.protocol raise self.skipException(message) if self.access_level not in CONF.access_levels_mapping.get( self.protocol, '').split(' '): raise self.skipException("%(level)s tests for %(protocol)s share " "access are disabled." % { 'level': self.access_level, 'protocol': self.protocol }) self.access_types = CONF.access_types_mapping.get( self.protocol, '').split(' ') if not self.access_types: raise self.skipException("No access levels were provided for %s " "share access tests." % self.protocol) 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': ['99.88.77.%d' % 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': ['foo_user_%d' % i for i in int_range], 'cert': ['tenant_%d.example.com' % i for i in int_range], 'ipv6': ['2001:db8::%d' % 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( "'%(access_type)s' access rules is disabled for protocol " "'%(protocol)s'." % {"access_type": access_type, "protocol": 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) python-manilaclient-2.1.0/manilaclient/tests/functional/client.py0000664000175000017500000022427013644133413025320 0ustar zuulzuul00000000000000# 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 import six 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' 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, six.text_type(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', six.text_type(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(ManilaCLIClient, self).__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 += ' --endpoint-type %s' % 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 += ' --os-share-api-version %s' % 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 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, six.string_types): dhss = six.text_type(dhss) if not isinstance(is_public, six.string_types): is_public = six.text_type(is_public) cmd = ('type-create %(name)s %(dhss)s --is-public %(is_public)s ') % { 'name': name, 'dhss': dhss, 'is_public': is_public} if description is not None: cmd += " --description " + description if snapshot_support is not None: if not isinstance(snapshot_support, six.string_types): snapshot_support = six.text_type(snapshot_support) cmd += " --snapshot-support " + snapshot_support if create_share_from_snapshot is not None: if not isinstance(create_share_from_snapshot, six.string_types): create_share_from_snapshot = six.text_type( 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, six.string_types): revert_to_snapshot = six.text_type( revert_to_snapshot) cmd += (" --revert-to-snapshot-support " + revert_to_snapshot) if mount_snapshot is not None: if not isinstance(mount_snapshot, six.string_types): mount_snapshot = six.text_type( 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, six.string_types): extra_specs[k] = six.text_type(v) extra_spec_str += "{}='{}' ".format(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 = ('type-update %(share_type_id)s ') % { 'share_type_id': share_type_id} if is_public is not None: if not isinstance(is_public, six.string_types): is_public = six.text_type(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( 'type-delete %s' % 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 = ( "--os-username %(username)s " "--os-project-name %(project_name)s " "--os-password %(password)s " "--os-identity-api-version %(identity_api_version)s " ) % { "username": CONF.admin_username, "project_name": CONF.admin_tenant_name, "password": CONF.admin_password, "identity_api_version": identity_api_version, } if identity_api_version == "3": if CONF.admin_project_domain_name: flags += ( "--os-project-domain-name %s " % CONF.admin_project_domain_name) elif CONF.admin_project_domain_id: flags += ( "--os-project-domain-id %s " % CONF.admin_project_domain_id) if CONF.admin_user_domain_name: flags += ( "--os-user-domain-name %s " % CONF.admin_user_domain_name) elif CONF.admin_user_domain_id: flags += ( "--os-user-domain-id %s " % CONF.admin_user_domain_id) project_id = self.openstack( 'project show -f value -c id %s' % 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)s %(project)s' % 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)s %(project)s' % data, microversion=microversion) @not_found_wrapper def list_share_type_access(self, share_type_id, microversion=None): projects_raw = self.manila( 'type-access-list %s' % 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='Provided invalid extra specs - %s' % extra_specs) cmd = 'type-key %s set ' % share_type_name_or_id for key, value in extra_specs.items(): cmd += '%(key)s=%(value)s ' % {'key': key, 'value': 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='Provided invalid extra specs - %s' % extra_specs_keys) cmd = 'type-key %s unset ' % share_type_name_or_id for key in extra_specs_keys: cmd += '%s ' % 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, nova_net_id=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 nova_net_id: text -- ID of Nova network :param neutron_net_id: text -- ID of Neutron network :param neutron_subnet_id: text -- ID of Neutron subnet NOTE: 'nova_net_id' and 'neutron_net_id'/'neutron_subnet_id' are mutually exclusive. """ params = self._combine_share_network_data( name=name, description=description, nova_net_id=nova_net_id, neutron_net_id=neutron_net_id, neutron_subnet_id=neutron_subnet_id, availability_zone=availability_zone ) share_network_raw = self.manila( 'share-network-create %s' % params, microversion=microversion) share_network = output_parser.details(share_network_raw) return share_network def _combine_share_network_data(self, name=None, description=None, nova_net_id=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 nova_net_id is not None: data['--nova_net_id'] = nova_net_id 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)s=%(v)s " % {'k': key, 'v': 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( 'share-network-show %s' % 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, nova_net_id=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 nova_net_id: text -- ID of some Nova network :param neutron_net_id: text -- ID of some Neutron network :param neutron_subnet_id: text -- ID of some Neutron subnet NOTE: 'nova_net_id' and 'neutron_net_id'/'neutron_subnet_id' are mutually exclusive. """ sn_params = self._combine_share_network_data( name=name, description=description, nova_net_id=nova_net_id, neutron_net_id=neutron_net_id, neutron_subnet_id=neutron_subnet_id) share_network_raw = self.manila( 'share-network-update %(sn)s %(params)s' % 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('share-network-delete %s' % share_network, microversion=microversion) @staticmethod def _stranslate_to_cli_optional_param(param): if len(param) < 1 or not isinstance(param, six.string_types): 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 += '%(k)s=%(v)s ' % { 'k': self._stranslate_to_cli_optional_param(k), 'v': v} share_networks_raw = self.manila(cmd, microversion=microversion) share_networks = utils.listing(share_networks_raw) return share_networks 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) # 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)s=%(v)s " % 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( 'share-network-subnet-create %(sn)s %(params)s' % {'sn': share_network, 'params': 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( 'share-network-subnet-show %(share_net)s %(share_subnet)s' % { 'share_net': share_network, 'share_subnet': 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( 'share-network-subnet-delete %(share_net)s %(share_subnet)s' % { 'share_net': share_network, 'share_subnet': 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, 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 microversion: str -- API microversion that should be used. """ cmd = 'create %(share_protocol)s %(size)s ' % { 'share_protocol': share_protocol, 'size': size} if share_network is not None: cmd += '--share-network %s ' % share_network if share_type is not None: cmd += '--share-type %s ' % share_type if name is None: name = data_utils.rand_name('autotest_share_name') cmd += '--name %s ' % name if description is None: description = data_utils.rand_name('autotest_share_description') cmd += '--description %s ' % description if public: cmd += '--public' if snapshot is not None: cmd += '--snapshot %s ' % snapshot if metadata: metadata_cli = '' for k, v in metadata.items(): metadata_cli += '%(k)s=%(v)s ' % {'k': k, 'v': v} if metadata_cli: cmd += '--metadata %s ' % metadata_cli 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('show %s' % 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 = 'update %s ' % share if name: cmd += '--name %s ' % name if description: cmd += '--description %s ' % description is_public = strutils.bool_from_string(is_public, strict=True) cmd += '--is-public %s ' % is_public return self.manila(cmd, microversion=microversion) @not_found_wrapper @forbidden_wrapper def delete_share(self, shares, 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. """ if not isinstance(shares, list): shares = [shares] cmd = 'delete ' for share in shares: cmd += '%s ' % share return self.manila(cmd, microversion=microversion) def list_shares(self, all_tenants=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 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. """ cmd = 'list ' if all_tenants: cmd += '--all-tenants ' if is_public: cmd += '--public ' if filters and isinstance(filters, dict): for k, v in filters.items(): cmd += '%(k)s=%(v)s ' % { 'k': self._stranslate_to_cli_optional_param(k), 'v': 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 += '--share-id %s' % share_id if filters and isinstance(filters, dict): for k, v in filters.items(): cmd += '%(k)s=%(v)s ' % { 'k': self._stranslate_to_cli_optional_param(k), 'v': 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 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 = ("Resource %(resource_id)s failed to reach " "%(status)s status within the required time " "(%(build_timeout)s)." % {"resource_id": resource_id, "status": status, "build_timeout": 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)s failed to reach a status in' '%(status)s when migrating from host %(src)s to ' 'host %(dest)s within the required time ' '%(timeout)s.' % { 'src': share['host'], 'dest': dest_host, 'share_id': share['id'], 'timeout': self.build_timeout, 'status': six.text_type(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 = ('Provided invalid data for setting of share metadata - ' '%s' % data) raise exceptions.InvalidData(message=msg) if update_all: cmd = 'metadata-update-all %s ' % share else: cmd = 'metadata %s set ' % share for k, v in data.items(): cmd += '%(k)s=%(v)s ' % {'k': k, 'v': 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 - ' '%s' % keys) raise exceptions.InvalidData(message=msg) cmd = 'metadata %s unset ' % share for key in keys: cmd += '%s ' % 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( 'metadata-show %s' % 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 = 'snapshot-create %(share)s ' % {'share': share} if name is None: name = data_utils.rand_name('autotest_snapshot_name') cmd += '--name %s ' % name if description is None: description = data_utils.rand_name('autotest_snapshot_description') cmd += '--description %s ' % description if force: cmd += '--force %s' % 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('snapshot-show %s' % 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 = "snapshot-export-location-list %s" % 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 = "snapshot-instance-export-location-list %s" % 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( "snapshot-delete %s" % 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 += '--snapshot %s' % 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 = 'snapshot-instance-show %s ' % 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 = 'snapshot-instance-reset-state %s ' % id if state: cmd += '--state %s' % 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 = ( "Snapshot %(snapshot_name)s failed to reach %(status)s " "status within the required time (%(timeout)s s)." % { "snapshot_name": snapshot_name, "status": status, "timeout": self.build_timeout}) 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 = 'snapshot-access-list %s ' % entity_id else: cmd = 'access-list %s ' % entity_id if columns is not None: cmd += ' --columns ' + columns if metadata: metadata_cli = '' for k, v in metadata.items(): metadata_cli += '%(k)s=%(v)s ' % {'k': k, 'v': v} if metadata_cli: cmd += ' --metadata %s ' % 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("access-show %s" % 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' ' metadata - %s' % metadata) raise exceptions.InvalidData(message=msg) cmd = "access-metadata %s set " % access_id for k, v in metadata.items(): cmd += '%(k)s=%(v)s ' % {'k': k, 'v': 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='Provided invalid keys - %s' % keys) cmd = 'access-metadata %s unset ' % access_id for key in keys: cmd += '%s ' % 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( 'snapshot-access-allow %(id)s %(type)s %(access_to)s' % { 'id': snapshot_id, 'type': access_type, 'access_to': 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( 'snapshot-access-deny %(share_id)s %(access_id)s' % { 'share_id': snapshot_id, 'access_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 = ('access-allow --access-level %(level)s %(id)s %(type)s ' '%(access_to)s' % { 'level': access_level, 'id': share_id, 'type': access_type, 'access_to': access_to}) if metadata: metadata_cli = '' for k, v in metadata.items(): metadata_cli += '%(k)s=%(v)s ' % {'k': k, 'v': v} if metadata_cli: cmd += ' --metadata %s ' % 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( 'access-deny %(share_id)s %(access_id)s' % { 'share_id': share_id, 'access_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 = ( "Access rule %(access)s failed to reach %(state)s state " "within the required time (%(build_timeout)s s)." % { "access": access_id, "state": state, "build_timeout": self.build_timeout}) 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 = ( "Access rule %(access)s failed to reach deleted state " "within the required time (%(timeout)s s)." % {"access": access_id, "timeout": self.build_timeout}) raise tempest_lib_exc.TimeoutException(message) def reset_task_state(self, share_id, state, version=None): state = '--task_state %s' % state if state else '' return self.manila('reset-task-state %(state)s %(share)s' % { 'state': state, 'share': 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 = ('migration-start %(share)s %(host)s ' '--writable %(writable)s --nondisruptive %(nondisruptive)s ' '--preserve-metadata %(preserve_metadata)s ' '--preserve-snapshots %(preserve_snapshots)s') % { 'share': share_id, 'host': dest_host, 'writable': writable, 'nondisruptive': nondisruptive, 'preserve_metadata': preserve_metadata, 'preserve_snapshots': preserve_snapshots, } if force_host_assisted_migration: cmd += (' --force-host-assisted-migration %s' % force_host_assisted_migration) if new_share_network: cmd += ' --new-share-network %s' % new_share_network if new_share_type: cmd += ' --new-share-type %s' % new_share_type return self.manila(cmd) def migration_complete(self, share_id): return self.manila('migration-complete %s' % share_id) def migration_cancel(self, share_id): return self.manila('migration-cancel %s' % share_id) def migration_get_progress(self, share_id): result = self.manila('migration-get-progress %s' % 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, 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. """ cmd = 'security-service-create %s ' % 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) 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, microversion=None): cmd = 'security-service-update %s ' % 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) 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): data = '' if name is not None: data += '--name %s ' % name if description is not None: data += '--description %s ' % description if dns_ip is not None: data += '--dns-ip %s ' % dns_ip if ou is not None: data += '--ou %s ' % ou if server is not None: data += '--server %s ' % server if domain is not None: data += '--domain %s ' % domain if user is not None: data += '--user %s ' % user if password is not None: data += '--password %s ' % password 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 = "share-export-location-list %s" % 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( 'snapshot-export-location-show %(snapshot)s %(el_uuid)s' % { 'snapshot': snapshot, 'el_uuid': 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( 'snapshot-instance-export-location-show %(snapshot)s %(el_uuid)s' % {'snapshot': snapshot, 'el_uuid': 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( 'share-export-location-show %(share)s %(el_uuid)s' % { 'share': share, 'el_uuid': 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 = "share-instance-export-location-list %s" % 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 ' '%(share_instance)s %(el_uuid)s' % { 'share_instance': share_instance, 'el_uuid': 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( 'share-server-show %s' % 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 += '%(k)s=%(v)s ' % { 'k': self._stranslate_to_cli_optional_param(k), 'v': 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('share-server-delete %s' % 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('unmanage %s ' % server_id) def unmanage_server(self, share_server_id): return self.manila('share-server-unmanage %s ' % share_server_id) def share_server_manage(self, host, share_network, identifier, driver_options=None): if driver_options: command = ('share-server-manage %s %s %s %s' % (host, share_network, identifier, driver_options)) else: command = ('share-server-manage %s %s %s' % (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( 'manage %s %s %s --share-server-id %s' % (host, protocol, export_location, share_server)) managed_share = output_parser.details(managed_share_raw) return managed_share['id'] # 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 = ('No message for resource with id %s was created in' ' the required time (%s s).' % (resource_id, self.build_timeout)) 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( 'message-show %s' % 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('message-delete %s' % 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, microversion=None): """Create a share replica. :param share: str -- Name or ID of a share to create a replica of """ cmd = "share-replica-create %s" % share replica = self.manila(cmd, microversion=microversion) return output_parser.details(replica) @not_found_wrapper def get_share_replica(self, replica, microversion=None): cmd = "share-replica-show %s" % 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( "share-replica-delete %s" % 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 = ( "Share replica %(id)s failed to reach %(status)s " "status within the required time " "(%(build_timeout)s s)." % { "id": share_replica, "status": status, "build_timeout": self.build_timeout}) 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 = "share-replica-export-location-list %s" % 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 ' '%(share_replica)s %(el_uuid)s' % { 'share_replica': share_replica, 'el_uuid': export_location_uuid, }, microversion=microversion) export = output_parser.details(export_raw) return export python-manilaclient-2.1.0/manilaclient/tests/functional/test_share_servers.py0000664000175000017500000001734213644133413027754 0ustar zuulzuul00000000000000# 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 ShareServersReadOnlyTest(base.BaseTestCase): def setUp(self): super(ShareServersReadOnlyTest, self).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(ShareServersReadWriteBase, self).setUp() if not CONF.run_share_servers_tests: message = "share-servers tests are disabled." raise self.skipException(message) if self.protocol not in CONF.enable_protocols: message = "%s tests are disabled." % self.protocol 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' def load_tests(loader, tests, _): result = [] for test_case in tests: if type(test_case._tests[0]) is ShareServersReadWriteBase: continue result.append(test_case) return loader.suiteClass(result) python-manilaclient-2.1.0/manilaclient/tests/functional/test_snapshot_instances_export_locations.py0000664000175000017500000001103213644133413034451 0ustar zuulzuul00000000000000# 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(SnapshotInstanceExportLocationReadWriteTest, self).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'])) python-manilaclient-2.1.0/manilaclient/tests/functional/test_snapshot_access.py0000664000175000017500000001676313644133413030267 0ustar zuulzuul00000000000000# 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(SnapshotAccessReadBase, self).setUp() if self.protocol not in CONF.enable_protocols: message = "%s tests are disabled." % self.protocol raise self.skipException(message) self.access_types = CONF.access_types_mapping.get( self.protocol, '').split(' ') if not self.access_types: raise self.skipException("No access types were provided for %s " "snapshot access tests." % self.protocol) self.share = self.create_share(share_protocol=self.protocol, public=True, client=self.get_user_client()) int_range = range(0, 10) self.access_to = { 'ip': ['99.88.77.%d' % i for i in int_range], 'user': ['foo_user_%d' % i for i in int_range], 'cert': ['tenant_%d.example.com' % i 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( "'%(access_type)s' access rules is disabled for protocol " "'%(protocol)s'." % {"access_type": access_type, "protocol": 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) python-manilaclient-2.1.0/manilaclient/tests/functional/test_common.py0000664000175000017500000000434213644133413026365 0ustar zuulzuul00000000000000# 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('Optional arguments:') 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') python-manilaclient-2.1.0/manilaclient/tests/functional/test_share_replica_export_locations.py0000664000175000017500000001042313644133413033347 0ustar zuulzuul00000000000000# 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]) python-manilaclient-2.1.0/manilaclient/tests/functional/test_snapshot_instances.py0000664000175000017500000001155013644133413031002 0ustar zuulzuul00000000000000# 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(SnapshotInstancesTest, self).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') python-manilaclient-2.1.0/manilaclient/tests/functional/test_messages.py0000664000175000017500000000530313644133413026702 0ustar zuulzuul00000000000000# 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(MessagesReadWriteTest, self).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']) python-manilaclient-2.1.0/manilaclient/tests/functional/__init__.py0000664000175000017500000000000013644133413025560 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/functional/test_shares.py0000664000175000017500000002140313644133413026357 0ustar zuulzuul00000000000000# 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 manilaclient.common import constants from manilaclient import config from manilaclient.tests.functional import base from manilaclient.tests.functional import utils CONF = config.CONF class SharesReadWriteBase(base.BaseTestCase): protocol = None def setUp(self): super(SharesReadWriteBase, self).setUp() if self.protocol not in CONF.enable_protocols: message = "%s tests are disabled" % self.protocol 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(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'], new_name, new_description, 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']) @ddt.ddt class SharesTestMigration(base.BaseTestCase): def setUp(self): super(SharesTestMigration, self).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' python-manilaclient-2.1.0/manilaclient/tests/functional/test_services.py0000664000175000017500000000330113644133413026712 0ustar zuulzuul00000000000000# 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') python-manilaclient-2.1.0/manilaclient/tests/functional/test_snapshots_export_locations.py0000664000175000017500000000700013644133413032565 0ustar zuulzuul00000000000000# 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(SnapshotExportLocationReadWriteTest, self).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'])) python-manilaclient-2.1.0/manilaclient/tests/functional/test_share_network_subnets.py0000664000175000017500000001101613644133413031507 0ustar zuulzuul00000000000000# 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(ShareNetworkSubnetsReadWriteTest, self).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']) python-manilaclient-2.1.0/manilaclient/tests/functional/test_security_services.py0000664000175000017500000000460313644133413030647 0ustar zuulzuul00000000000000# 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(SecurityServiceReadWriteTest, self).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' @ddt.data( {'name': 'test_name'}, {'description': 'test_description'}, {'user': 'test_username'}, {'password': 'test_password'}, {'server': 'test_server'}, {'domain': 'test_domain'}, {'dns_ip': 'test_dns_ip'}, {'ou': 'test_ou'}, {'name': '""'}, {'description': '""'}, {'user': '""'}, {'password': '""'}, {'server': '""'}, {'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, } 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]) python-manilaclient-2.1.0/manilaclient/tests/unit/0000775000175000017500000000000013644133466022306 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/unit/test_functional_utils.py0000664000175000017500000001711713644133413027300 0ustar zuulzuul00000000000000# 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 utils as func_utils from manilaclient.tests.unit import utils @ddt.ddt class ShellTest(utils.TestCase): OUTPUT_LINES_SIMPLE = """ +----+------+---------+ | ID | Name | Status | +----+------+---------+ | 11 | foo | BUILD | | 21 | bar | ERROR | +----+------+---------+ """ OUTPUT_LINES_ONE_MULTI_ROW = """ +----+------+---------+ | ID | Name | Status | +----+------+---------+ | 11 | foo | BUILD | | 21 | bar | ERROR | | | | ERROR2 | | 31 | bee | None | +----+------+---------+ """ OUTPUT_LINES_COMPLICATED_MULTI_ROW = """ +----+------+---------+ | ID | Name | Status | +----+------+---------+ | 11 | foo | BUILD | | 21 | bar | ERROR | | | | ERROR2 | | | | ERROR3 | | 31 | bee | None | | | bee2 | | | | bee3 | | | 41 | rand | None | | | rend | None2 | | | | | +----+------+---------+ """ OUTPUT_LINES_COMPLICATED_MULTI_ROW_WITH_SHIFTED_ID = """ +----+----+------+---------+ | ** | ID | Name | Status | +----+----+------+---------+ | ** | 11 | foo | BUILD | | | 21 | bar | ERROR | | | | | ERROR2 | | | | | ERROR3 | | | | | | | ** | 31 | bee | None | | | | bee2 | | | | | | | +----+----+------+---------+ """ OUTPUT_LINES_NESTED_TABLE = """ +----+----+------+--------------+ | ** | ID | Name | Status | +----+----+------+--------------+ | ** | 11 | foo | +----+----+ | | | | | | aa | bb | | | | | | +----+----+ | | | | | +----+----+ | | | 21 | bar | ERROR | | | | | ERROR2 | | | | | ERROR3 | +----+----+------+--------------+ """ OUTPUT_LINES_NESTED_TABLE_MULTI_LINE = """ +----+----+------+--------------+ | ** | ID | Name | Status | +----+----+------+--------------+ | ** | 11 | foo | +----+----+ | | | | | | id | bb | | | | | | +----+----+ | | | | | | 01 | a1 | | | | | | | | a2 | | | | | | +----+----+ | | | 21 | bar | ERROR | | | | | ERROR2 | | | | | ERROR3 | +----+----+------+--------------+ """ OUTPUT_LINES_DETAILS = """ +----------+--------+ | Property | Value | +----------+--------+ | foo | BUILD | | bar | ERROR | | | ERROR2 | | | ERROR3 | | bee | None | +----------+--------+ """ @ddt.data({'input': OUTPUT_LINES_SIMPLE, 'valid_values': [ ['11', 'foo', 'BUILD'], ['21', 'bar', 'ERROR'] ]}, {'input': OUTPUT_LINES_ONE_MULTI_ROW, 'valid_values': [ ['11', 'foo', 'BUILD'], ['21', 'bar', ['ERROR', 'ERROR2']], ['31', 'bee', 'None'], ]}, {'input': OUTPUT_LINES_COMPLICATED_MULTI_ROW, 'valid_values': [ ['11', 'foo', 'BUILD'], ['21', 'bar', ['ERROR', 'ERROR2', 'ERROR3']], ['31', ['bee', 'bee2', 'bee3'], 'None'], ['41', ['rand', 'rend'], ['None', 'None2']], ['', '', ''] ]}) @ddt.unpack def test_multi_line_row_table(self, input, valid_values): actual_result = func_utils.multi_line_row_table(input) self.assertEqual(['ID', 'Name', 'Status'], actual_result['headers']) self.assertEqual(valid_values, actual_result['values']) def test_multi_line_row_table_shifted_id_column(self): input = self.OUTPUT_LINES_COMPLICATED_MULTI_ROW_WITH_SHIFTED_ID valid_values = [ ['**', '11', 'foo', 'BUILD'], ['', '21', 'bar', ['ERROR', 'ERROR2', 'ERROR3']], ['', '', '', ''], ['**', '31', ['bee', 'bee2'], 'None'], ['', '', '', ''] ] actual_result = func_utils.multi_line_row_table( input, group_by_column_index=1) self.assertEqual(['**', 'ID', 'Name', 'Status'], actual_result['headers']) self.assertEqual(valid_values, actual_result['values']) @ddt.data({'input': OUTPUT_LINES_NESTED_TABLE, 'valid_nested': { 'headers': ['aa', 'bb'], 'values': [] }}, {'input': OUTPUT_LINES_NESTED_TABLE_MULTI_LINE, 'valid_nested': { 'headers': ['id', 'bb'], 'values': [['01', ['a1', 'a2']]] }},) @ddt.unpack def test_nested_tables(self, input, valid_nested): actual_result = func_utils.multi_line_row_table( input, group_by_column_index=1) self.assertEqual(['**', 'ID', 'Name', 'Status'], actual_result['headers']) self.assertEqual(2, len(actual_result['values'])) self.assertEqual(valid_nested, actual_result['values'][0][3]) @ddt.data({'input': OUTPUT_LINES_DETAILS, 'valid_values': [ ['foo', 'BUILD'], ['bar', ['ERROR', 'ERROR2', 'ERROR3']], ['bee', 'None'], ]}) @ddt.unpack def test_details(self, input, valid_values): actual_result = func_utils.multi_line_row_table(input) self.assertEqual(['Property', 'Value'], actual_result['headers']) self.assertEqual(valid_values, actual_result['values']) @ddt.data({'input_data': OUTPUT_LINES_DETAILS, 'output_data': [ {'Property': 'foo', 'Value': 'BUILD'}, {'Property': 'bar', 'Value': ['ERROR', 'ERROR2', 'ERROR3']}, {'Property': 'bee', 'Value': 'None'}]}, {'input_data': OUTPUT_LINES_SIMPLE, 'output_data': [ {'ID': '11', 'Name': 'foo', 'Status': 'BUILD'}, {'ID': '21', 'Name': 'bar', 'Status': 'ERROR'}, ]}, {'input_data': OUTPUT_LINES_ONE_MULTI_ROW, 'output_data': [ {'ID': '11', 'Name': 'foo', 'Status': 'BUILD'}, {'ID': '21', 'Name': 'bar', 'Status': ['ERROR', 'ERROR2']}, {'ID': '31', 'Name': 'bee', 'Status': 'None'}, ]}, {'input_data': OUTPUT_LINES_COMPLICATED_MULTI_ROW, 'output_data': [ {'ID': '11', 'Name': 'foo', 'Status': 'BUILD'}, {'ID': '21', 'Name': 'bar', 'Status': ['ERROR', 'ERROR2', 'ERROR3']}, {'ID': '31', 'Name': ['bee', 'bee2', 'bee3'], 'Status': 'None'}, {'ID': '41', 'Name': ['rand', 'rend'], 'Status': ['None', 'None2']}, {'ID': '', 'Name': '', 'Status': ''}, ]}) @ddt.unpack def test_listing(self, input_data, output_data): actual_result = func_utils.listing(input_data) self.assertEqual(output_data, actual_result) python-manilaclient-2.1.0/manilaclient/tests/unit/fakes.py0000664000175000017500000000561613644133413023751 0ustar zuulzuul00000000000000# 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. """ A fake server that "responds" to API methods with pre-canned responses. All of these responses come from the spec, so if for some reason the spec's wrong the tests might raise AssertionError. I've indicated in comments the places where actual behavior differs from the spec. """ from __future__ import print_function def assert_has_keys(dictonary, required=None, optional=None): if required is None: required = [] if optional is None: optional = [] for k in required: try: assert k in dictonary except AssertionError: extra_keys = set(dictonary).difference(set(required + optional)) raise AssertionError("found unexpected keys: %s" % list(extra_keys)) class FakeClient(object): def assert_called(self, method, url, body=None, pos=-1, **kwargs): """Assert than an API method was just called.""" expected = (method, url) called = self.client.callstack[pos][0:2] assert self.client.callstack, ("Expected %s %s but no calls " "were made." % expected) assert expected == called, 'Expected %s %s; got %s %s' % ( expected + called) if body is not None: actual = self.client.callstack[pos][2] assert actual == body, "Expected %s; got %s" % (body, actual) def assert_called_anytime(self, method, url, body=None, clear_callstack=True): """Assert than an API method was called anytime in the test.""" expected = (method, url) assert self.client.callstack, ("Expected %s %s but no calls " "were made." % expected) found = False for entry in self.client.callstack: if expected == entry[0:2]: found = True break assert found, 'Expected %s %s; got %s' % ( expected, self.client.callstack) if body is not None: try: assert entry[2] == body except AssertionError: print(entry[2]) print("!=") print(body) raise if clear_callstack: self.client.callstack = [] def clear_callstack(self): self.client.callstack = [] def authenticate(self): pass python-manilaclient-2.1.0/manilaclient/tests/unit/test_shell.py0000664000175000017500000004224113644133413025021 0ustar zuulzuul00000000000000# 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 sys import ddt import fixtures import mock from six import moves 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 = moves.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) def test_main_success(self): 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_PROJECT_DOMAIN_ID': 'foo_project_domain_id', 'OS_USER_DOMAIN_NAME': 'foo_user_domain_name', 'OS_USER_DOMAIN_ID': 'foo_user_domain_id', 'OS_CERT': 'foo_cert', } self.set_env_vars(env_vars) 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=env_vars['OS_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 += "%s=%s " % (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="", 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 += "%s=%s " % (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="", 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', '.*?(?m)^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', ) 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 = moves.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('foo %s' % 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, 'foo %s' % 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('bar %s' % 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, 'bar %s' % 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('quuz %s' % 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, 'quuz %s' % options_str) python-manilaclient-2.1.0/manilaclient/tests/unit/test_utils.py0000664000175000017500000000210713644133413025047 0ustar zuulzuul00000000000000# 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 six import testtools from manilaclient import utils class TestCommonUtils(testtools.TestCase): def test_unicode_key_value_to_string(self): src = {u'key': u'\u70fd\u7231\u5a77'} expected = {'key': '\xe7\x83\xbd\xe7\x88\xb1\xe5\xa9\xb7'} if six.PY2: self.assertEqual(expected, utils.unicode_key_value_to_string(src)) else: # u'xxxx' in PY3 is str, we will not get extra 'u' from cli # output in PY3 self.assertEqual(src, utils.unicode_key_value_to_string(src)) python-manilaclient-2.1.0/manilaclient/tests/unit/osc/0000775000175000017500000000000013644133466023072 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/unit/osc/osc_utils.py0000664000175000017500000000477313644133413025453 0ustar zuulzuul00000000000000# Copyright 2012-2013 OpenStack Foundation # Copyright 2013 Nebula Inc. # # 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 fixtures import os import testtools from openstackclient.tests.unit import fakes class ParserException(Exception): pass class TestCase(testtools.TestCase): def setUp(self): testtools.TestCase.setUp(self) if (os.environ.get("OS_STDOUT_CAPTURE") == "True" or os.environ.get("OS_STDOUT_CAPTURE") == "1"): stdout = self.useFixture(fixtures.StringStream("stdout")).stream self.useFixture(fixtures.MonkeyPatch("sys.stdout", stdout)) if (os.environ.get("OS_STDERR_CAPTURE") == "True" or os.environ.get("OS_STDERR_CAPTURE") == "1"): stderr = self.useFixture(fixtures.StringStream("stderr")).stream self.useFixture(fixtures.MonkeyPatch("sys.stderr", stderr)) def assertNotCalled(self, m, msg=None): """Assert a function was not called""" if m.called: if not msg: msg = 'method %s should not have been called' % m self.fail(msg) class TestCommand(TestCase): """Test cliff command classes""" def setUp(self): super(TestCommand, self).setUp() # Build up a fake app self.fake_stdout = fakes.FakeStdout() self.fake_log = fakes.FakeLog() self.app = fakes.FakeApp(self.fake_stdout, self.fake_log) self.app.client_manager = fakes.FakeClientManager() self.app.options = fakes.FakeOptions() def check_parser(self, cmd, args, verify_args): cmd_parser = cmd.get_parser('check_parser') try: parsed_args = cmd_parser.parse_args(args) except SystemExit: raise ParserException("Argument parse failed") for av in verify_args: attr, value = av if attr: self.assertIn(attr, parsed_args) self.assertEqual(value, getattr(parsed_args, attr)) return parsed_args python-manilaclient-2.1.0/manilaclient/tests/unit/osc/osc_fakes.py0000664000175000017500000001522713644133413025400 0ustar zuulzuul00000000000000# Copyright 2013 Nebula Inc. # # 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 mock from oslo_serialization import jsonutils import sys from keystoneauth1 import fixture import requests AUTH_TOKEN = "foobar" AUTH_URL = "http://0.0.0.0" USERNAME = "itchy" PASSWORD = "scratchy" PROJECT_NAME = "poochie" REGION_NAME = "richie" INTERFACE = "catchy" VERSION = "3" TEST_RESPONSE_DICT = fixture.V2Token(token_id=AUTH_TOKEN, user_name=USERNAME) _s = TEST_RESPONSE_DICT.add_service('identity', name='keystone') _s.add_endpoint(AUTH_URL + ':5000/v2.0') _s = TEST_RESPONSE_DICT.add_service('network', name='neutron') _s.add_endpoint(AUTH_URL + ':9696') _s = TEST_RESPONSE_DICT.add_service('compute', name='nova') _s.add_endpoint(AUTH_URL + ':8774/v2.1') _s = TEST_RESPONSE_DICT.add_service('image', name='glance') _s.add_endpoint(AUTH_URL + ':9292') _s = TEST_RESPONSE_DICT.add_service('object', name='swift') _s.add_endpoint(AUTH_URL + ':8080/v1') TEST_RESPONSE_DICT_V3 = fixture.V3Token(user_name=USERNAME) TEST_RESPONSE_DICT_V3.set_project_scope() TEST_VERSIONS = fixture.DiscoveryList(href=AUTH_URL) def to_unicode_dict(catalog_dict): """Converts dict to unicode dict""" if isinstance(catalog_dict, dict): return {to_unicode_dict(key): to_unicode_dict(value) for key, value in catalog_dict.items()} elif isinstance(catalog_dict, list): return [to_unicode_dict(element) for element in catalog_dict] elif isinstance(catalog_dict, str): return catalog_dict + u"" else: return catalog_dict class FakeStdout(object): def __init__(self): self.content = [] def write(self, text): self.content.append(text) def make_string(self): result = '' for line in self.content: result = result + line return result class FakeLog(object): def __init__(self): self.messages = {} def debug(self, msg): self.messages['debug'] = msg def info(self, msg): self.messages['info'] = msg def warning(self, msg): self.messages['warning'] = msg def error(self, msg): self.messages['error'] = msg def critical(self, msg): self.messages['critical'] = msg class FakeApp(object): def __init__(self, _stdout, _log): self.stdout = _stdout self.client_manager = None self.stdin = sys.stdin self.stdout = _stdout or sys.stdout self.stderr = sys.stderr self.log = _log class FakeOptions(object): def __init__(self, **kwargs): self.os_beta_command = False class FakeClient(object): def __init__(self, **kwargs): self.endpoint = kwargs['endpoint'] self.token = kwargs['token'] class FakeClientManager(object): _api_version = { 'image': '2', } def __init__(self): self.compute = None self.identity = None self.image = None self.object_store = None self.volume = None self.network = None self.session = None self.auth_ref = None self.auth_plugin_name = None self.network_endpoint_enabled = True def get_configuration(self): return { 'auth': { 'username': USERNAME, 'password': PASSWORD, 'token': AUTH_TOKEN, }, 'region': REGION_NAME, 'identity_api_version': VERSION, } def is_network_endpoint_enabled(self): return self.network_endpoint_enabled class FakeModule(object): def __init__(self, name, version): self.name = name self.__version__ = version # Workaround for openstacksdk case self.version = mock.Mock() self.version.__version__ = version class FakeResource(object): def __init__(self, manager=None, info=None, loaded=False, methods=None): """Set attributes and methods for a resource. :param manager: The resource manager :param Dictionary info: A dictionary with all attributes :param bool loaded: True if the resource is loaded in memory :param Dictionary methods: A dictionary with all methods """ info = info or {} methods = methods or {} self.__name__ = type(self).__name__ self.manager = manager self._info = info self._add_details(info) self._add_methods(methods) self._loaded = loaded def _add_details(self, info): for (k, v) in info.items(): setattr(self, k, v) def _add_methods(self, methods): """Fake methods with MagicMock objects. For each <@key, @value> pairs in methods, add an callable MagicMock object named @key as an attribute, and set the mock's return_value to @value. When users access the attribute with (), @value will be returned, which looks like a function call. """ for (name, ret) in methods.items(): method = mock.Mock(return_value=ret) setattr(self, name, method) def __repr__(self): reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and k != 'manager') info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) return "<%s %s>" % (self.__class__.__name__, info) def keys(self): return self._info.keys() def to_dict(self): return self._info @property def info(self): return self._info def __getitem__(self, item): return self._info.get(item) def get(self, item, default=None): return self._info.get(item, default) class FakeResponse(requests.Response): def __init__(self, headers=None, status_code=200, data=None, encoding=None): super(FakeResponse, self).__init__() headers = headers or {} self.status_code = status_code self.headers.update(headers) self._content = jsonutils.dump_as_bytes(data) class FakeModel(dict): def __getattr__(self, key): try: return self[key] except KeyError: raise AttributeError(key) python-manilaclient-2.1.0/manilaclient/tests/unit/osc/v2/0000775000175000017500000000000013644133466023421 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/unit/osc/v2/fakes.py0000664000175000017500000002607313644133413025064 0ustar zuulzuul00000000000000# 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 copy import datetime import mock import random import uuid from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from manilaclient.tests.unit.osc import osc_fakes from manilaclient.tests.unit.osc import osc_utils class FakeShareClient(object): def __init__(self, **kwargs): super(FakeShareClient, self).__init__() self.auth_token = kwargs['token'] self.management_url = kwargs['endpoint'] self.shares = mock.Mock() self.share_access_rules = mock.Mock() self.share_types = mock.Mock() self.share_type_access = mock.Mock() self.shares.resource_class = osc_fakes.FakeResource(None, {}) self.share_export_locations = mock.Mock() self.share_export_locations.resource_class = ( osc_fakes.FakeResource(None, {})) class ManilaParseException(Exception): """The base exception class for all exceptions this library raises.""" def __init__(self, message=None, details=None): self.message = message or "Argument parse exception" self.details = details or None def __str__(self): return self.message class TestShare(osc_utils.TestCommand): def setUp(self): super(TestShare, self).setUp() self.app.client_manager.share = FakeShareClient( endpoint=osc_fakes.AUTH_URL, token=osc_fakes.AUTH_TOKEN ) self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client( endpoint=osc_fakes.AUTH_URL, token=osc_fakes.AUTH_TOKEN ) class FakeShare(object): """Fake one or more shares.""" @staticmethod def create_one_share(attrs=None, methods=None): """Create a fake share. :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with flavor_id, image_id, and so on """ attrs = attrs or {} methods = methods or {} # set default attributes. share_info = { "status": None, "share_server_id": None, "project_id": 'project-id-' + uuid.uuid4().hex, "name": 'share-name-' + uuid.uuid4().hex, "share_type": 'share-type-' + uuid.uuid4().hex, "share_type_name": "default", "availability_zone": None, "created_at": 'time-' + uuid.uuid4().hex, "share_network_id": None, "share_group_id": None, "share_proto": "NFS", "host": None, "access_rules_status": "active", "has_replicas": False, "replication_type": None, "task_state": None, "snapshot_support": True, "snapshot_id": None, "is_public": True, "metadata": {}, "id": 'share-id-' + uuid.uuid4().hex, "size": random.randint(1, 20), "description": 'share-description-' + uuid.uuid4().hex, "user_id": 'share-user-id-' + uuid.uuid4().hex, "create_share_from_snapshot_support": False, "mount_snapshot_support": False, "revert_to_snapshot_support": False, "source_share_group_snapshot_member_id": None, } # Overwrite default attributes. share_info.update(attrs) share = osc_fakes.FakeResource(info=copy.deepcopy(share_info), methods=methods, loaded=True) return share @staticmethod def create_shares(attrs=None, count=2): """Create multiple fake shares. :param Dictionary attrs: A dictionary with all share attributes :param Integer count: The number of shares to be faked :return: A list of FakeResource objects """ shares = [] for n in range(0, count): shares.append(FakeShare.create_one_share(attrs)) return shares @staticmethod def get_shares(shares=None, count=2): """Get an iterable MagicMock object with a list of faked shares. If a shares list is provided, then initialize the Mock object with the list. Otherwise create one. :param List shares: A list of FakeResource objects faking shares :param Integer count: The number of shares to be faked :return An iterable Mock object with side_effect set to a list of faked shares """ if shares is None: shares = FakeShare.create_shares(count) return mock.Mock(side_effect=shares) @staticmethod def get_share_columns(share=None): """Get the shares columns from a faked shares object. :param shares: A FakeResource objects faking shares :return A tuple which may include the following keys: ('id', 'name', 'description', 'status', 'size', 'share_type', 'metadata', 'snapshot', 'availability_zone') """ if share is not None: return tuple(k for k in sorted(share.keys())) return tuple([]) @staticmethod def get_share_data(share=None): """Get the shares data from a faked shares object. :param shares: A FakeResource objects faking shares :return A tuple which may include the following values: ('ce26708d', 'fake name', 'fake description', 'available', 20, 'fake share type', "Manila='zorilla', Zorilla='manila', Zorilla='zorilla'", 1, 'nova') """ data_list = [] if share is not None: for x in sorted(share.keys()): if x == 'tags': # The 'tags' should be format_list data_list.append( format_columns.ListColumn(share.info.get(x))) else: data_list.append(share.info.get(x)) return tuple(data_list) class FakeShareType(object): """Fake one or more share types""" @staticmethod def create_one_sharetype(attrs=None, methods=None): """Create a fake share type :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with project_id, resource and so on """ attrs = attrs or {} methods = methods or {} share_type_info = { "required_extra_specs": { "driver_handles_share_servers": True }, "share_type_access:is_public": True, "extra_specs": { "replication_type": "readable", "driver_handles_share_servers": True, "mount_snapshot_support": False, "revert_to_snapshot_support": False, "create_share_from_snapshot_support": True, "snapshot_support": True }, "id": 'share-type-id-' + uuid.uuid4().hex, "name": 'share-type-name-' + uuid.uuid4().hex, "is_default": False, "description": 'share-type-description-' + uuid.uuid4().hex } share_type_info.update(attrs) share_type = osc_fakes.FakeResource(info=copy.deepcopy( share_type_info), methods=methods, loaded=True) return share_type @staticmethod def create_share_types(attrs=None, count=2): """Create multiple fake share types. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share types to be faked :return: A list of FakeResource objects """ share_types = [] for n in range(0, count): share_types.append(FakeShareType.create_one_sharetype(attrs)) return share_types @staticmethod def get_share_types(share_types=None, count=2): """Get an iterable MagicMock object with a list of faked types. If types list is provided, then initialize the Mock object with the list. Otherwise create one. :param List types: A list of FakeResource objects faking types :param Integer count: The number of types to be faked :return An iterable Mock object with side_effect set to a list of faked types """ if share_types is None: share_types = FakeShareType.create_share_types(count) return mock.Mock(side_effect=share_types) class FakeShareExportLocation(object): """Fake one or more export locations""" @staticmethod def create_one_export_location(attrs=None): """Create a fake share export location :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with project_id, resource and so on """ attrs = attrs or {} share_export_location_info = { "fake_uuid": "foo_el_uuid", "fake_path": "/foo/el/path", "fake_share_instance_id": 'share-instance-id' + uuid.uuid4().hex, "is_admin_only": False, "created_at": 'time-' + uuid.uuid4().hex, "updated_at": 'time-' + uuid.uuid4().hex, } share_export_location_info.update(attrs) share_export_location = osc_fakes.FakeResource(info=copy.deepcopy( share_export_location_info), loaded=True) return share_export_location class FakeShareAccessRule(object): """Fake one or more share access rules""" @staticmethod def create_one_access_rule(attrs=None): """Create a fake share access rule :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with project_id, resource and so on """ share_access_rule = { 'id': 'access_rule-id-' + uuid.uuid4().hex, 'share_id': 'share-id-' + uuid.uuid4().hex, 'access_level': 'rw', 'access_to': 'demo', 'access_type': 'user', 'state': 'queued_to_apply', 'access_key': None, 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'properties': {} } share_access_rule.update(attrs) share_access_rule = osc_fakes.FakeResource(info=copy.deepcopy( share_access_rule), loaded=True) return share_access_rule python-manilaclient-2.1.0/manilaclient/tests/unit/osc/v2/test_share_access_rules.py0000664000175000017500000002751313644133413030667 0ustar zuulzuul00000000000000# 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 osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.osc.v2 import share_access_rules as osc_share_access_rules from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes ACCESS_RULE_ATTRIBUTES = [ 'id', 'share_id', 'access_level', 'access_to', 'access_type', 'state', 'access_key', 'created_at', 'updated_at', 'properties' ] class TestShareAccess(manila_fakes.TestShare): def setUp(self): super(TestShareAccess, self).setUp() self.shares_mock = self.app.client_manager.share.shares self.app.client_manager.share.api_version = api_versions.APIVersion( '2.51') self.shares_mock.reset_mock() self.access_rules_mock = ( self.app.client_manager.share.share_access_rules) self.access_rules_mock.reset_mock() class TestShareAccessCreate(TestShareAccess): def setUp(self): super(TestShareAccessCreate, self).setUp() self.share = manila_fakes.FakeShare.create_one_share( attrs={"is_public": False}, methods={'allow': None}) self.shares_mock.get.return_value = self.share self.access_rule = ( manila_fakes.FakeShareAccessRule.create_one_access_rule( attrs={"share_id": self.share.id})) self.share.allow.return_value = self.access_rule._info # Get the command object to test self.cmd = osc_share_access_rules.ShareAccessAllow(self.app, None) def test_share_access_create_user(self): arglist = [ self.share.id, 'user', 'demo', ] verifylist = [ ("share", self.share.id), ("access_type", "user"), ("access_to", "demo"), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.get.assert_called_with(self.share.id) self.share.allow.assert_called_with( access_type="user", access="demo", access_level=None, metadata={} ) self.assertEqual(ACCESS_RULE_ATTRIBUTES, columns) self.assertCountEqual(self.access_rule._info.values(), data) def test_share_access_create_properties(self): arglist = [ self.share.id, 'user', 'demo', '--properties', 'key=value' ] verifylist = [ ("share", self.share.id), ("access_type", "user"), ("access_to", "demo"), ('properties', ['key=value']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.get.assert_called_with(self.share.id) self.share.allow.assert_called_with( access_type="user", access="demo", access_level=None, metadata={'key': 'value'} ) self.assertEqual(ACCESS_RULE_ATTRIBUTES, columns) self.assertCountEqual(self.access_rule._info.values(), data) def test_access_rule_create_access_level(self): arglist = [ self.share.id, 'user', 'demo', '--access-level', 'ro' ] verifylist = [ ("share", self.share.id), ("access_type", "user"), ("access_to", "demo"), ('access_level', 'ro') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.get.assert_called_with(self.share.id) self.share.allow.assert_called_with( access_type="user", access="demo", access_level='ro', metadata={} ) self.assertEqual(ACCESS_RULE_ATTRIBUTES, columns) self.assertCountEqual(self.access_rule._info.values(), data) class TestShareAccessDelete(TestShareAccess): def setUp(self): super(TestShareAccessDelete, self).setUp() self.share = manila_fakes.FakeShare.create_one_share( methods={'deny': None}) self.shares_mock.get.return_value = self.share self.access_rule = ( manila_fakes.FakeShareAccessRule.create_one_access_rule( attrs={"share_id": self.share.id})) # Get the command object to test self.cmd = osc_share_access_rules.ShareAccessDeny(self.app, None) def test_share_access_delete(self): arglist = [ self.share.id, self.access_rule.id ] verifylist = [ ("share", self.share.id), ("id", self.access_rule.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.shares_mock.get.assert_called_with(self.share.id) self.share.deny.assert_called_with(self.access_rule.id) self.assertIsNone(result) class TestShareAccessList(TestShareAccess): access_rules_columns = [ 'id', 'access_type', 'access_to', 'access_level', 'state', 'access_key', 'created_at', 'updated_at' ] def setUp(self): super(TestShareAccessList, self).setUp() self.share = manila_fakes.FakeShare.create_one_share() self.access_rule_1 = ( manila_fakes.FakeShareAccessRule.create_one_access_rule( attrs={"share_id": self.share.id})) self.access_rule_2 = ( manila_fakes.FakeShareAccessRule.create_one_access_rule( attrs={"share_id": self.share.id, "access_to": "admin"})) self.access_rules = [self.access_rule_1, self.access_rule_2] self.shares_mock.get.return_value = self.share self.access_rules_mock.access_list.return_value = self.access_rules self.values_list = (oscutils.get_dict_properties( a._info, self.access_rules_columns) for a in self.access_rules) # Get the command object to test self.cmd = osc_share_access_rules.ListShareAccess(self.app, None) def test_access_rules_list(self): arglist = [ self.share.id ] verifylist = [ ("share", self.share.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.get.assert_called_with(self.share.id) self.access_rules_mock.access_list.assert_called_with( self.share, {}) self.assertEqual(self.access_rules_columns, columns) self.assertEqual(tuple(self.values_list), tuple(data)) def test_access_rules_list_filter_properties(self): arglist = [ self.share.id, '--properties', 'key=value' ] verifylist = [ ("share", self.share.id), ('properties', ['key=value']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.get.assert_called_with(self.share.id) self.access_rules_mock.access_list.assert_called_with( self.share, {'metadata': {'key': 'value'}}) self.assertEqual(self.access_rules_columns, columns) self.assertEqual(tuple(self.values_list), tuple(data)) def test_access_rules_list_columns(self): expected_columns = [ 'id', 'access_to' ] expected_data = (oscutils.get_dict_properties( a._info, expected_columns) for a in self.access_rules) arglist = [ self.share.id, '--columns', 'id,access_to' ] verifylist = [ ("share", self.share.id), ('columns', 'id,access_to') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.get.assert_called_with(self.share.id) self.access_rules_mock.access_list.assert_called_with( self.share, {}) self.assertEqual(expected_columns, columns) self.assertEqual(tuple(expected_data), tuple(data)) class TestShareAccessShow(TestShareAccess): def setUp(self): super(TestShareAccessShow, self).setUp() self.share = manila_fakes.FakeShare.create_one_share() self.access_rule = ( manila_fakes.FakeShareAccessRule.create_one_access_rule( attrs={"share_id": self.share.id})) self.access_rules_mock.get.return_value = self.access_rule # Get the command object to test self.cmd = osc_share_access_rules.ShowShareAccess(self.app, None) def test_access_rule_show(self): arglist = [ self.access_rule.id ] verifylist = [ ("access_id", self.access_rule.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.access_rules_mock.get.assert_called_with(self.access_rule.id) self.assertEqual(ACCESS_RULE_ATTRIBUTES, columns) self.assertEqual(tuple(self.access_rule._info.values()), data) class TestShareAccessSet(TestShareAccess): def setUp(self): super(TestShareAccessSet, self).setUp() self.share = manila_fakes.FakeShare.create_one_share() self.access_rule = ( manila_fakes.FakeShareAccessRule.create_one_access_rule( attrs={"share_id": self.share.id})) self.access_rules_mock.get.return_value = self.access_rule # Get the command object to test self.cmd = osc_share_access_rules.SetShareAccess(self.app, None) def test_access_rule_set(self): arglist = [ self.access_rule.id, '--property', 'key1=value1' ] verifylist = [ ("access_id", self.access_rule.id), ('property', {'key1': 'value1'}) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.access_rules_mock.set_metadata.assert_called_with( self.access_rule, {'key1': 'value1'}) self.assertIsNone(result) class TestShareAccessUnset(TestShareAccess): def setUp(self): super(TestShareAccessUnset, self).setUp() self.share = manila_fakes.FakeShare.create_one_share() self.access_rule = ( manila_fakes.FakeShareAccessRule.create_one_access_rule( attrs={"share_id": self.share.id})) self.access_rules_mock.get.return_value = self.access_rule # Get the command object to test self.cmd = osc_share_access_rules.UnsetShareAccess(self.app, None) def test_access_rule_unset(self): arglist = [ self.access_rule.id, '--property', 'key1' ] verifylist = [ ("access_id", self.access_rule.id), ('property', ['key1']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.access_rules_mock.unset_metadata.assert_called_with( self.access_rule, ['key1']) self.assertIsNone(result) python-manilaclient-2.1.0/manilaclient/tests/unit/osc/v2/test_share_type_access.py0000664000175000017500000001432513644133413030513 0ustar zuulzuul00000000000000# 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 osc_lib import exceptions from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from manilaclient.common.apiclient.exceptions import BadRequest from manilaclient.osc.v2 import share_type_access as osc_share_type_access from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShareTypeAccess(manila_fakes.TestShare): def setUp(self): super(TestShareTypeAccess, self).setUp() self.type_access_mock = ( self.app.client_manager.share.share_type_access) self.type_access_mock.reset_mock() self.share_types_mock = self.app.client_manager.share.share_types self.share_types_mock.reset_mock() self.projects_mock = self.app.client_manager.identity.projects self.projects_mock.reset_mock() class TestShareTypeAccessAllow(TestShareTypeAccess): def setUp(self): super(TestShareTypeAccessAllow, self).setUp() self.project = identity_fakes.FakeProject.create_one_project() self.share_type = manila_fakes.FakeShareType.create_one_sharetype( attrs={'share_type_access:is_public': False}) self.share_types_mock.get.return_value = self.share_type self.type_access_mock.add_project_access.return_value = None # Get the command object to test self.cmd = osc_share_type_access.ShareTypeAccessAllow(self.app, None) def test_share_type_access_create(self): arglist = [ self.share_type.id, self.project.id ] verifylist = [ ('share_type', self.share_type.id), ('project_id', self.project.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.type_access_mock.add_project_access.assert_called_with( self.share_type, self.project.id) self.assertIsNone(result) def test_share_type_access_create_throws_exception(self): arglist = [ self.share_type.id, 'invalid_project_format' ] verifylist = [ ('share_type', self.share_type.id), ('project_id', 'invalid_project_format') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.type_access_mock.add_project_access.side_effect = BadRequest() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareTypeAccessList(TestShareTypeAccess): columns = ['Project_ID'] data = (('',), ('',)) def setUp(self): super(TestShareTypeAccessList, self).setUp() self.type_access_mock.list.return_value = ( self.columns, self.data) # Get the command object to test self.cmd = osc_share_type_access.ListShareTypeAccess(self.app, None) def test_share_type_access_list(self): share_type = manila_fakes.FakeShareType.create_one_sharetype( attrs={'share_type_access:is_public': False}) self.share_types_mock.get.return_value = share_type arglist = [ share_type.id, ] verifylist = [ ('share_type', share_type.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.type_access_mock.list.assert_called_once_with( share_type) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) def test_share_type_access_list_public_type(self): share_type = manila_fakes.FakeShareType.create_one_sharetype( attrs={'share_type_access:is_public': True}) self.share_types_mock.get.return_value = share_type arglist = [ share_type.id, ] verifylist = [ ('share_type', share_type.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareTypeAccessDeny(TestShareTypeAccess): def setUp(self): super(TestShareTypeAccessDeny, self).setUp() self.project = identity_fakes.FakeProject.create_one_project() self.share_type = manila_fakes.FakeShareType.create_one_sharetype( attrs={'share_type_access:is_public': False}) self.share_types_mock.get.return_value = self.share_type self.type_access_mock.remove_project_access.return_value = None # Get the command object to test self.cmd = osc_share_type_access.ShareTypeAccessDeny(self.app, None) def test_share_type_access_delete(self): arglist = [ self.share_type.id, self.project.id ] verifylist = [ ('share_type', self.share_type.id), ('project_id', self.project.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.type_access_mock.remove_project_access.assert_called_with( self.share_type, self.project.id) self.assertIsNone(result) def test_share_type_access_delete_exception(self): arglist = [ self.share_type.id, 'invalid_project_format' ] verifylist = [ ('share_type', self.share_type.id), ('project_id', 'invalid_project_format') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.type_access_mock.remove_project_access.side_effect = BadRequest() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) python-manilaclient-2.1.0/manilaclient/tests/unit/osc/v2/test_share.py0000664000175000017500000007124513644133413026135 0ustar zuulzuul00000000000000# Copyright 2019 Red Hat 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 argparse import mock import uuid from mock import call from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from manilaclient.common import cliutils from manilaclient.osc.v2 import share as osc_shares from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShare(manila_fakes.TestShare): def setUp(self): super(TestShare, self).setUp() self.shares_mock = self.app.client_manager.share.shares self.shares_mock.reset_mock() self.projects_mock = self.app.client_manager.identity.projects self.projects_mock.reset_mock() self.users_mock = self.app.client_manager.identity.users self.users_mock.reset_mock() def setup_shares_mock(self, count): shares = manila_fakes.FakeShare.create_shares(count=count) self.shares_mock.get = manila_fakes.FakeShare.get_shares( shares, 0) return shares class TestShareCreate(TestShare): def setUp(self): super(TestShareCreate, self).setUp() self.new_share = manila_fakes.FakeShare.create_one_share() self.shares_mock.create.return_value = self.new_share # Get the command object to test self.cmd = osc_shares.CreateShare(self.app, None) self.datalist = tuple(self.new_share._info.values()) self.columns = tuple(self.new_share._info.keys()) def test_share_create_required_args(self): """Verifies required arguments.""" arglist = [ self.new_share.share_proto, str(self.new_share.size), ] verifylist = [ ('share_proto', self.new_share.share_proto), ('size', self.new_share.size) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.create.assert_called_with( availability_zone=None, description=None, is_public=False, metadata={}, name=None, share_group_id=None, share_network=None, share_proto=self.new_share.share_proto, share_type=None, size=self.new_share.size, snapshot_id=None ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) def test_share_create_missing_required_arg(self): """Verifies missing required arguments.""" arglist = [ self.new_share.share_proto, ] verifylist = [ ('share_proto', self.new_share.share_proto) ] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_create_metadata(self): arglist = [ self.new_share.share_proto, str(self.new_share.size), '--property', 'Manila=zorilla', '--property', 'Zorilla=manila' ] verifylist = [ ('share_proto', self.new_share.share_proto), ('size', self.new_share.size), ('property', {'Manila': 'zorilla', 'Zorilla': 'manila'}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.create.assert_called_with( availability_zone=None, description=None, is_public=False, metadata={'Manila': 'zorilla', 'Zorilla': 'manila'}, name=None, share_group_id=None, share_network=None, share_proto=self.new_share.share_proto, share_type=None, size=self.new_share.size, snapshot_id=None ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) # TODO(vkmc) Add test with snapshot when # we implement snapshot support in OSC # def test_share_create_with_snapshot(self): class TestShareDelete(TestShare): def setUp(self): super(TestShareDelete, self).setUp() self.shares_mock.delete = mock.Mock() self.shares_mock.delete.return_value = None # Get the command object to test self.cmd = osc_shares.DeleteShare(self.app, None) def test_share_delete_one(self): shares = self.setup_shares_mock(count=1) arglist = [ shares[0].name ] verifylist = [ ("force", False), ("share_group", None), ('shares', [shares[0].name]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.shares_mock.delete.assert_called_with(shares[0], None) self.assertIsNone(result) def test_share_delete_many(self): shares = self.setup_shares_mock(count=3) arglist = [v.id for v in shares] verifylist = [ ("force", False), ("share_group", None), ('shares', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) calls = [call(s, None) for s in shares] self.shares_mock.delete.assert_has_calls(calls) self.assertIsNone(result) def test_share_delete_with_force(self): shares = self.setup_shares_mock(count=1) arglist = [ '--force', shares[0].name, ] verifylist = [ ('force', True), ("share_group", None), ('shares', [shares[0].name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.shares_mock.force_delete.assert_called_once_with(shares[0]) self.assertIsNone(result) def test_share_delete_wrong_name(self): shares = self.setup_shares_mock(count=1) arglist = [ shares[0].name + '-wrong-name' ] verifylist = [ ("force", False), ("share_group", None), ('shares', [shares[0].name + '-wrong-name']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertIsNone(result) def test_share_delete_no_name(self): # self.setup_shares_mock(count=1) arglist = [] verifylist = [ ("force", False), ("share_group", None), ('shares', '') ] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) class TestShareList(TestShare): project = identity_fakes.FakeProject.create_one_project() user = identity_fakes.FakeUser.create_one_user() columns = [ 'ID', 'Name', 'Size', 'Share Proto', 'Status', 'Is Public', 'Share Type Name', 'Host', 'Availability Zone' ] def setUp(self): super(TestShareList, self).setUp() self.new_share = manila_fakes.FakeShare.create_one_share() self.shares_mock.list.return_value = [self.new_share] self.users_mock.get.return_value = self.user self.projects_mock.get.return_value = self.project # Get the command object to test self.cmd = osc_shares.ListShare(self.app, None) def _get_data(self): data = (( self.new_share.id, self.new_share.name, self.new_share.size, self.new_share.share_proto, self.new_share.status, self.new_share.is_public, self.new_share.share_type_name, self.new_share.host, self.new_share.availability_zone, ),) return data def _get_search_opts(self): search_opts = { 'all_tenants': False, 'is_public': False, 'metadata': {}, 'extra_specs': {}, 'limit': None, 'name': None, 'status': None, 'host': None, 'share_server_id': None, 'share_network_id': None, 'share_type_id': None, 'snapshot_id': None, 'share_group_id': None, 'project_id': None, 'user_id': None, 'offset': None, } return search_opts def test_share_list_no_options(self): arglist = [] verifylist = [ ('long', False), ('all_projects', False), ('name', None), ('status', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) cmd_columns, cmd_data = self.cmd.take_action(parsed_args) search_opts = { 'all_tenants': False, 'is_public': False, 'metadata': {}, 'extra_specs': {}, 'limit': None, 'name': None, 'status': None, 'host': None, 'share_server_id': None, 'share_network_id': None, 'share_type_id': None, 'snapshot_id': None, 'share_group_id': None, 'project_id': None, 'user_id': None, 'offset': None, } self.shares_mock.list.assert_called_once_with( search_opts=search_opts, ) self.assertEqual(self.columns, cmd_columns) data = self._get_data() self.assertEqual(data, tuple(cmd_data)) def test_share_list_project(self): arglist = [ '--project', self.project.name, ] verifylist = [ ('project', self.project.name), ('long', False), ('all_projects', False), ('status', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) cmd_columns, cmd_data = self.cmd.take_action(parsed_args) search_opts = { 'all_tenants': False, 'is_public': False, 'metadata': {}, 'extra_specs': {}, 'limit': None, 'name': None, 'status': None, 'host': None, 'share_server_id': None, 'share_network_id': None, 'share_type_id': None, 'snapshot_id': None, 'share_group_id': None, 'project_id': None, 'user_id': None, 'offset': None, } search_opts['project_id'] = self.project.id search_opts['all_tenants'] = True self.shares_mock.list.assert_called_once_with( search_opts=search_opts, ) self.assertEqual(self.columns, cmd_columns) data = self._get_data() self.assertEqual(data, tuple(cmd_data)) def test_share_list_project_domain(self): arglist = [ '--project', self.project.name, '--project-domain', self.project.domain_id, ] verifylist = [ ('project', self.project.name), ('project_domain', self.project.domain_id), ('long', False), ('all_projects', False), ('status', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) cmd_columns, cmd_data = self.cmd.take_action(parsed_args) search_opts = { 'all_tenants': False, 'is_public': False, 'metadata': {}, 'extra_specs': {}, 'limit': None, 'name': None, 'status': None, 'host': None, 'share_server_id': None, 'share_network_id': None, 'share_type_id': None, 'snapshot_id': None, 'share_group_id': None, 'project_id': None, 'user_id': None, 'offset': None, } search_opts['project_id'] = self.project.id search_opts['all_tenants'] = True self.shares_mock.list.assert_called_once_with( search_opts=search_opts, ) self.assertEqual(self.columns, cmd_columns) data = self._get_data() self.assertEqual(data, tuple(cmd_data)) def test_share_list_user(self): arglist = [ '--user', self.user.name, ] verifylist = [ ('user', self.user.name), ('long', False), ('all_projects', False), ('status', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) cmd_columns, cmd_data = self.cmd.take_action(parsed_args) search_opts = { 'all_tenants': False, 'is_public': False, 'metadata': {}, 'extra_specs': {}, 'limit': None, 'name': None, 'status': None, 'host': None, 'share_server_id': None, 'share_network_id': None, 'share_type_id': None, 'snapshot_id': None, 'share_group_id': None, 'project_id': None, 'user_id': None, 'offset': None, } search_opts['user_id'] = self.user.id self.shares_mock.list.assert_called_once_with( search_opts=search_opts, ) self.assertEqual(self.columns, cmd_columns) data = self._get_data() self.assertEqual(data, tuple(cmd_data)) def test_share_list_user_domain(self): arglist = [ '--user', self.user.name, '--user-domain', self.user.domain_id, ] verifylist = [ ('user', self.user.name), ('user_domain', self.user.domain_id), ('long', False), ('all_projects', False), ('status', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) cmd_columns, cmd_data = self.cmd.take_action(parsed_args) search_opts = { 'all_tenants': False, 'is_public': False, 'metadata': {}, 'extra_specs': {}, 'limit': None, 'name': None, 'status': None, 'host': None, 'share_server_id': None, 'share_network_id': None, 'share_type_id': None, 'snapshot_id': None, 'share_group_id': None, 'project_id': None, 'user_id': None, 'offset': None, } search_opts['user_id'] = self.user.id self.shares_mock.list.assert_called_once_with( search_opts=search_opts, ) self.assertEqual(self.columns, cmd_columns) data = self._get_data() self.assertEqual(data, tuple(cmd_data)) def test_share_list_name(self): arglist = [ '--name', self.new_share.name, ] verifylist = [ ('long', False), ('all_projects', False), ('name', self.new_share.name), ('status', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) cmd_columns, cmd_data = self.cmd.take_action(parsed_args) search_opts = { 'all_tenants': False, 'is_public': False, 'metadata': {}, 'extra_specs': {}, 'limit': None, 'name': None, 'status': None, 'host': None, 'share_server_id': None, 'share_network_id': None, 'share_type_id': None, 'snapshot_id': None, 'share_group_id': None, 'project_id': None, 'user_id': None, 'offset': None, } search_opts['name'] = self.new_share.name self.shares_mock.list.assert_called_once_with( search_opts=search_opts, ) self.assertEqual(self.columns, cmd_columns) data = self._get_data() self.assertEqual(data, tuple(cmd_data)) def test_share_list_status(self): arglist = [ '--status', self.new_share.status, ] verifylist = [ ('long', False), ('all_projects', False), ('name', None), ('status', self.new_share.status), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) cmd_columns, cmd_data = self.cmd.take_action(parsed_args) search_opts = { 'all_tenants': False, 'is_public': False, 'metadata': {}, 'extra_specs': {}, 'limit': None, 'name': None, 'status': None, 'host': None, 'share_server_id': None, 'share_network_id': None, 'share_type_id': None, 'snapshot_id': None, 'share_group_id': None, 'project_id': None, 'user_id': None, 'offset': None, } search_opts['status'] = self.new_share.status self.shares_mock.list.assert_called_once_with( search_opts=search_opts, ) self.assertEqual(self.columns, cmd_columns) data = self._get_data() self.assertEqual(data, tuple(cmd_data)) def test_share_list_all_tenants(self): arglist = [ '--all-projects', ] verifylist = [ ('long', False), ('all_projects', True), ('name', None), ('status', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) cmd_columns, cmd_data = self.cmd.take_action(parsed_args) search_opts = { 'all_tenants': False, 'is_public': False, 'metadata': {}, 'extra_specs': {}, 'limit': None, 'name': None, 'status': None, 'host': None, 'share_server_id': None, 'share_network_id': None, 'share_type_id': None, 'snapshot_id': None, 'share_group_id': None, 'project_id': None, 'user_id': None, 'offset': None, } search_opts['all_tenants'] = True self.shares_mock.list.assert_called_once_with( search_opts=search_opts, ) self.assertEqual(self.columns, cmd_columns) data = self._get_data() self.assertEqual(data, tuple(cmd_data)) def test_share_list_long(self): arglist = [ '--long', ] verifylist = [ ('long', True), ('all_projects', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) cmd_columns, cmd_data = self.cmd.take_action(parsed_args) search_opts = { 'all_tenants': False, 'is_public': False, 'metadata': {}, 'extra_specs': {}, 'limit': None, 'name': None, 'status': None, 'host': None, 'share_server_id': None, 'share_network_id': None, 'share_type_id': None, 'snapshot_id': None, 'share_group_id': None, 'project_id': None, 'user_id': None, 'offset': None, } self.shares_mock.list.assert_called_once_with( search_opts=search_opts, ) collist = [ 'ID', 'Name', 'Size', 'Share Protocol', 'Status', 'Is Public', 'Share Type Name', 'Availability Zone', 'Description', 'Share Network ID', 'Share Server ID', 'Share Type', 'Share Group ID', 'Host', 'User ID', 'Project ID', 'Access Rules Status', 'Source Snapshot ID', 'Supports Creating Snapshots', 'Supports Cloning Snapshots', 'Supports Mounting snapshots', 'Supports Reverting to Snapshot', 'Migration Task Status', 'Source Share Group Snapshot Member ID', 'Replication Type', 'Has Replicas', 'Created At', 'Properties', ] self.assertEqual(collist, cmd_columns) data = (( self.new_share.id, self.new_share.name, self.new_share.size, self.new_share.share_proto, self.new_share.status, self.new_share.is_public, self.new_share.share_type_name, self.new_share.availability_zone, self.new_share.description, self.new_share.share_network_id, self.new_share.share_server_id, self.new_share.share_type, self.new_share.share_group_id, self.new_share.host, self.new_share.user_id, self.new_share.project_id, self.new_share.access_rules_status, self.new_share.snapshot_id, self.new_share.snapshot_support, self.new_share.create_share_from_snapshot_support, self.new_share.mount_snapshot_support, self.new_share.revert_to_snapshot_support, self.new_share.task_state, self.new_share.source_share_group_snapshot_member_id, self.new_share.replication_type, self.new_share.has_replicas, self.new_share.created_at, self.new_share.metadata ),) self.assertEqual(data, tuple(cmd_data)) def test_share_list_with_marker_and_limit(self): arglist = [ "--marker", self.new_share.id, "--limit", "2", ] verifylist = [ ('long', False), ('all_projects', False), ('name', None), ('status', None), ('marker', self.new_share.id), ('limit', 2), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) cmd_columns, cmd_data = self.cmd.take_action(parsed_args) self.assertEqual(self.columns, cmd_columns) search_opts = { 'all_tenants': False, 'is_public': False, 'metadata': {}, 'extra_specs': {}, 'limit': 2, 'name': None, 'status': None, 'host': None, 'share_server_id': None, 'share_network_id': None, 'share_type_id': None, 'snapshot_id': None, 'share_group_id': None, 'project_id': None, 'user_id': None, 'offset': self.new_share.id } data = self._get_data() self.shares_mock.list.assert_called_once_with( search_opts=search_opts ) self.assertEqual(data, tuple(cmd_data)) def test_share_list_negative_limit(self): arglist = [ "--limit", "-2", ] verifylist = [ ("limit", -2), ] self.assertRaises(argparse.ArgumentTypeError, self.check_parser, self.cmd, arglist, verifylist) class TestShareShow(TestShare): def setUp(self): super(TestShareShow, self).setUp() self._share = manila_fakes.FakeShare.create_one_share() self.shares_mock.get.return_value = self._share self._export_location = ( manila_fakes.FakeShareExportLocation.create_one_export_location()) # Get the command object to test self.cmd = osc_shares.ShowShare(self.app, None) def test_share_show(self): arglist = [ self._share.id ] verifylist = [ ("share", self._share.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) cliutils.transform_export_locations_to_string_view = mock.Mock() cliutils.transform_export_locations_to_string_view.return_value = dict( self._export_location) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.get.assert_called_with(self._share.id) self.assertEqual( manila_fakes.FakeShare.get_share_columns(self._share), columns) self.assertEqual( manila_fakes.FakeShare.get_share_data(self._share), data) class TestShareSet(TestShare): def setUp(self): super(TestShareSet, self).setUp() self._share = manila_fakes.FakeShare.create_one_share() self.shares_mock.get.return_value = self._share # Get the command object to test self.cmd = osc_shares.SetShare(self.app, None) def test_share_set_property(self): arglist = [ '--property', 'Zorilla=manila', self._share.id, ] verifylist = [ ('property', {'Zorilla': 'manila'}), ('share', self._share.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.shares_mock.set_metadata.assert_called_with( self._share.id, {'Zorilla': 'manila'}) def test_share_set_name(self): new_name = uuid.uuid4().hex arglist = [ '--name', new_name, self._share.id, ] verifylist = [ ('name', new_name), ('share', self._share.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.shares_mock.update.assert_called_with( self._share.id, display_name=parsed_args.name) def test_share_set_description(self): new_description = uuid.uuid4().hex arglist = [ '--description', new_description, self._share.id, ] verifylist = [ ('description', new_description), ('share', self._share.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.shares_mock.update.assert_called_with( self._share.id, display_description=parsed_args.description) def test_share_set_visibility(self): arglist = [ '--public', 'true', self._share.id, ] verifylist = [ ('public', 'true'), ('share', self._share.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.shares_mock.update.assert_called_with( self._share.id, is_public='true') class TestShareUnset(TestShare): def setUp(self): super(TestShareUnset, self).setUp() self._share = manila_fakes.FakeShare.create_one_share() self.shares_mock.get.return_value = self._share # Get the command objects to test self.cmd = osc_shares.UnsetShare(self.app, None) def test_share_unset_property(self): arglist = [ '--property', 'Manila', self._share.id, ] verifylist = [ ('property', ['Manila']), ('share', self._share.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.shares_mock.delete_metadata.assert_called_with( self._share.id, parsed_args.property) def test_share_unset_name(self): arglist = [ '--name', self._share.id, ] verifylist = [ ('name', True), ('share', self._share.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.shares_mock.update.assert_called_with( self._share.id, display_name=None) def test_share_unset_description(self): arglist = [ '--description', self._share.id, ] verifylist = [ ('description', True), ('share', self._share.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.shares_mock.update.assert_called_with( self._share.id, display_description=None) python-manilaclient-2.1.0/manilaclient/tests/unit/osc/v2/__init__.py0000664000175000017500000000000013644133413025510 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/unit/osc/v2/test_share_type.py0000664000175000017500000004571713644133413027203 0ustar zuulzuul00000000000000# 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 mock import call from osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.common.apiclient.exceptions import BadRequest from manilaclient.common.apiclient.exceptions import NotFound from manilaclient.osc.v2 import share_types as osc_share_types from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes COLUMNS = [ 'id', 'name', 'visibility', 'is_default', 'required_extra_specs', 'optional_extra_specs', 'description' ] class TestShareType(manila_fakes.TestShare): def setUp(self): super(TestShareType, self).setUp() self.shares_mock = self.app.client_manager.share.share_types self.shares_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( "2.51") class TestShareTypeCreate(TestShareType): def setUp(self): super(TestShareTypeCreate, self).setUp() self.new_share_type = manila_fakes.FakeShareType.create_one_sharetype() self.shares_mock.create.return_value = self.new_share_type # Get the command object to test self.cmd = osc_share_types.CreateShareType(self.app, None) self.data = [ self.new_share_type.id, self.new_share_type.name, 'public', self.new_share_type.is_default, 'driver_handles_share_servers : True', ('replication_type : readable\n' 'mount_snapshot_support : False\n' 'revert_to_snapshot_support : False\n' 'create_share_from_snapshot_support : True\n' 'snapshot_support : True'), self.new_share_type.description, ] def test_share_type_create_required_args(self): """Verifies required arguments.""" arglist = [ self.new_share_type.name, 'True' ] verifylist = [ ('name', self.new_share_type.name), ('spec_driver_handles_share_servers', 'True') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.create.assert_called_with( extra_specs={}, is_public=True, name=self.new_share_type.name, spec_driver_handles_share_servers=True ) self.assertCountEqual(COLUMNS, columns) self.assertCountEqual(self.data, data) def test_share_type_create_missing_required_arg(self): """Verifies missing required arguments.""" arglist = [ self.new_share_type.name ] verifylist = [ ('name', self.new_share_type.name) ] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_type_create_private(self): arglist = [ self.new_share_type.name, 'True', '--public', 'False' ] verifylist = [ ('name', self.new_share_type.name), ('spec_driver_handles_share_servers', 'True'), ('public', 'False') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.create.assert_called_with( extra_specs={}, is_public=False, name=self.new_share_type.name, spec_driver_handles_share_servers=True ) self.assertCountEqual(COLUMNS, columns) self.assertCountEqual(self.data, data) def test_share_type_create_extra_specs(self): arglist = [ self.new_share_type.name, 'True', '--extra-specs', 'snapshot_support=true' ] verifylist = [ ('name', self.new_share_type.name), ('spec_driver_handles_share_servers', 'True'), ('extra_specs', ['snapshot_support=true']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.create.assert_called_with( extra_specs={'snapshot_support': 'True'}, is_public=True, name=self.new_share_type.name, spec_driver_handles_share_servers=True ) self.assertCountEqual(COLUMNS, columns) self.assertCountEqual(self.data, data) def test_share_type_create_dhss_invalid_value(self): arglist = [ self.new_share_type.name, 'non_bool_value' ] verifylist = [ ('name', self.new_share_type.name), ('spec_driver_handles_share_servers', 'non_bool_value') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_type_create_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.40") arglist = [ self.new_share_type.name, 'True', '--description', 'Description' ] verifylist = [ ('name', self.new_share_type.name), ('spec_driver_handles_share_servers', 'True'), ('description', 'Description') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_type_create_dhss_defined_twice(self): arglist = [ self.new_share_type.name, 'True', '--extra-specs', 'driver_handles_share_servers=true' ] verifylist = [ ('name', self.new_share_type.name), ('spec_driver_handles_share_servers', 'True'), ('extra_specs', ['driver_handles_share_servers=true']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_type_create_bool_args(self): arglist = [ self.new_share_type.name, 'True', '--snapshot-support', 'true' ] verifylist = [ ('name', self.new_share_type.name), ('spec_driver_handles_share_servers', 'True'), ('snapshot_support', 'true') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.create.assert_called_with( extra_specs={'snapshot_support': 'True'}, is_public=True, name=self.new_share_type.name, spec_driver_handles_share_servers=True ) self.assertCountEqual(COLUMNS, columns) self.assertCountEqual(self.data, data) class TestShareTypeDelete(TestShareType): share_types = manila_fakes.FakeShareType.create_share_types(count=2) def setUp(self): super(TestShareTypeDelete, self).setUp() self.shares_mock.get = manila_fakes.FakeShareType.get_share_types( self.share_types) self.shares_mock.delete.return_value = None # Get the command object to test self.cmd = osc_share_types.DeleteShareType(self.app, None) def test_share_type_delete_one(self): arglist = [ self.share_types[0].id ] verifylist = [ ('share_types', [self.share_types[0].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.shares_mock.delete.assert_called_with(self.share_types[0]) self.assertIsNone(result) def test_share_type_delete_multiple(self): arglist = [] for t in self.share_types: arglist.append(t.id) verifylist = [ ('share_types', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) calls = [] for t in self.share_types: calls.append(call(t)) self.shares_mock.delete.assert_has_calls(calls) self.assertIsNone(result) def test_delete_share_type_with_exception(self): arglist = [ 'non_existing_type', ] verifylist = [ ('share_types', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.shares_mock.delete.side_effect = exceptions.CommandError() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareTypeSet(TestShareType): def setUp(self): super(TestShareTypeSet, self).setUp() self.share_type = manila_fakes.FakeShareType.create_one_sharetype( methods={'set_keys': None, 'update': None}) self.shares_mock.get.return_value = self.share_type # Get the command object to test self.cmd = osc_share_types.SetShareType(self.app, None) def test_share_type_set_extra_specs(self): arglist = [ self.share_type.id, '--extra-specs', 'snapshot_support=true' ] verifylist = [ ('share_type', self.share_type.id), ('extra_specs', ['snapshot_support=true']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.share_type.set_keys.assert_called_with( {'snapshot_support': 'True'}) self.assertIsNone(result) def test_share_type_set_name(self): arglist = [ self.share_type.id, '--name', 'new name' ] verifylist = [ ('share_type', self.share_type.id), ('name', 'new name') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.share_type.update.assert_called_with( name='new name') self.assertIsNone(result) def test_share_type_set_description(self): arglist = [ self.share_type.id, '--description', 'new description' ] verifylist = [ ('share_type', self.share_type.id), ('description', 'new description') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.share_type.update.assert_called_with( description='new description') self.assertIsNone(result) def test_share_type_set_visibility(self): arglist = [ self.share_type.id, '--public', 'false' ] verifylist = [ ('share_type', self.share_type.id), ('public', 'false') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.share_type.update.assert_called_with( is_public=False) self.assertIsNone(result) def test_share_type_set_extra_specs_exception(self): arglist = [ self.share_type.id, '--extra-specs', 'snapshot_support=true' ] verifylist = [ ('share_type', self.share_type.id), ('extra_specs', ['snapshot_support=true']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.share_type.set_keys.side_effect = BadRequest() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_type_set_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.49") arglist = [ self.share_type.id, '--name', 'new name', ] verifylist = [ ('share_type', self.share_type.id), ('name', 'new name'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareTypeUnset(TestShareType): def setUp(self): super(TestShareTypeUnset, self).setUp() self.share_type = manila_fakes.FakeShareType.create_one_sharetype( methods={'unset_keys': None}) self.shares_mock.get.return_value = self.share_type # Get the command object to test self.cmd = osc_share_types.UnsetShareType(self.app, None) def test_share_type_unset_extra_specs(self): arglist = [ self.share_type.id, 'snapshot_support' ] verifylist = [ ('share_type', self.share_type.id), ('extra_specs', ['snapshot_support']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.share_type.unset_keys.assert_called_with(['snapshot_support']) self.assertIsNone(result) def test_share_type_unset_exception(self): arglist = [ self.share_type.id, 'snapshot_support' ] verifylist = [ ('share_type', self.share_type.id), ('extra_specs', ['snapshot_support']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.share_type.unset_keys.side_effect = NotFound() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareTypeList(TestShareType): share_types = manila_fakes.FakeShareType.create_share_types() def setUp(self): super(TestShareTypeList, self).setUp() self.shares_mock.list.return_value = self.share_types # Get the command object to test self.cmd = osc_share_types.ListShareType(self.app, None) self.values = (oscutils.get_dict_properties( s._info, COLUMNS) for s in self.share_types) def test_share_type_list_no_options(self): arglist = [] verifylist = [ ('all', False) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.list.assert_called_once_with( search_opts={}, show_all=False ) self.assertEqual(COLUMNS, columns) self.assertEqual(list(self.values), list(data)) def test_share_type_list_all(self): arglist = [ '--all', ] verifylist = [ ('all', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.list.assert_called_once_with( search_opts={}, show_all=True) self.assertEqual(COLUMNS, columns) self.assertEqual(list(self.values), list(data)) def test_share_type_list_extra_specs(self): arglist = [ '--extra-specs', 'snapshot_support=true' ] verifylist = [ ('extra_specs', ['snapshot_support=true']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.list.assert_called_once_with( search_opts={'extra_specs': {'snapshot_support': 'True'}}, show_all=False) self.assertEqual(COLUMNS, columns) self.assertEqual(list(self.values), list(data)) def test_share_type_list_columns(self): arglist = [ '--columns', 'id,name' ] verifylist = [ ('columns', 'id,name') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) arg_columns = [ 'id', 'name' ] arg_columns_data = (oscutils.get_item_properties( s, arg_columns) for s in self.share_types) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.list.assert_called_once_with( search_opts={}, show_all=False) self.assertEqual(arg_columns, columns) self.assertEqual(list(arg_columns_data), list(data)) def test_share_type_list_api_versions_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.42") arglist = [ '--extra-specs', 'snapshot_support=true' ] verifylist = [ ('extra_specs', ['snapshot_support=true']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_type_list_columns_invalid_value(self): arglist = [ '--columns', 'invalid_column_name' ] verifylist = [ ('columns', 'invalid_column_name') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareTypeShow(TestShareType): def setUp(self): super(TestShareTypeShow, self).setUp() self.share_type = manila_fakes.FakeShareType.create_one_sharetype() self.shares_mock.get.return_value = self.share_type # Get the command object to test self.cmd = osc_share_types.ShowShareType(self.app, None) self.data = [ self.share_type.id, self.share_type.name, 'public', self.share_type.is_default, 'driver_handles_share_servers : True', ('replication_type : readable\n' 'mount_snapshot_support : False\n' 'revert_to_snapshot_support : False\n' 'create_share_from_snapshot_support : True\n' 'snapshot_support : True'), self.share_type.description, ] def test_share_type_show(self): arglist = [ self.share_type.id ] verifylist = [ ("share_type", self.share_type.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.get.assert_called_with(self.share_type.id) self.assertCountEqual(COLUMNS, columns) self.assertCountEqual(self.data, data) python-manilaclient-2.1.0/manilaclient/tests/unit/osc/__init__.py0000664000175000017500000000000013644133413025161 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/unit/v2/0000775000175000017500000000000013644133466022635 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/unit/v2/fakes.py0000664000175000017500000012554413644133413024303 0ustar zuulzuul00000000000000# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # Copyright 2011 OpenStack Foundation # # 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 __future__ import print_function import manilaclient from manilaclient import api_versions from manilaclient.tests.unit.v2 import fake_clients as fakes from manilaclient.v2 import client class FakeClient(fakes.FakeClient): def __init__(self, *args, **kwargs): client.Client.__init__( self, manilaclient.API_MAX_VERSION, 'username', 'password', 'project_id', 'auth_url', input_auth_token='token', extensions=kwargs.get('extensions'), service_catalog_url='http://localhost:8786', api_version=kwargs.get("api_version", manilaclient.API_MAX_VERSION) ) self.client = FakeHTTPClient(**kwargs) fake_share_instance = { 'id': 1234, 'share_id': 'fake', 'status': 'available', 'availability_zone': 'fake', 'share_network_id': 'fake', 'share_server_id': 'fake', } def get_fake_export_location(): return { 'uuid': 'foo_el_uuid', 'path': '/foo/el/path', 'share_instance_id': 'foo_share_instance_id', 'is_admin_only': False, 'created_at': '2015-12-17T13:14:15Z', 'updated_at': '2015-12-17T14:15:16Z', } def get_fake_snapshot_export_location(): return { 'uuid': 'foo_el_uuid', 'path': '/foo/el/path', 'share_snapshot_instance_id': 'foo_share_instance_id', 'is_admin_only': False, 'created_at': '2017-01-17T13:14:15Z', 'updated_at': '2017-01-17T14:15:16Z', } class FakeHTTPClient(fakes.FakeHTTPClient): def get_(self, **kw): body = { "versions": [ { "status": "CURRENT", "updated": "2015-07-30T11:33:21Z", "links": [ { "href": "http://docs.openstack.org/", "type": "text/html", "rel": "describedby", }, { "href": "http://localhost:8786/v2/", "rel": "self", } ], "min_version": "2.0", "version": self.default_headers[ "X-Openstack-Manila-Api-Version"], "id": "v2.0", } ] } return (200, {}, body) def get_availability_zones(self): availability_zones = { "availability_zones": [ {"id": "368c5780-ad72-4bcf-a8b6-19e45f4fafoo", "name": "foo", "created_at": "2016-07-08T14:13:12.000000", "updated_at": "2016-07-08T15:14:13.000000"}, {"id": "368c5780-ad72-4bcf-a8b6-19e45f4fabar", "name": "bar", "created_at": "2016-07-08T14:13:12.000000", "updated_at": "2016-07-08T15:14:13.000000"}, ] } return (200, {}, availability_zones) def get_os_services(self, **kw): services = { "services": [ {"status": "enabled", "binary": "manila-scheduler", "zone": "foozone", "state": "up", "updated_at": "2015-10-09T13:54:09.000000", "host": "lucky-star", "id": 1}, {"status": "enabled", "binary": "manila-share", "zone": "foozone", "state": "up", "updated_at": "2015-10-09T13:54:05.000000", "host": "lucky-star", "id": 2}, ] } return (200, {}, services) get_services = get_os_services def put_os_services_enable(self, **kw): return (200, {}, {'host': 'foo', 'binary': 'manila-share', 'disabled': False}) put_services_enable = put_os_services_enable def put_os_services_disable(self, **kw): return (200, {}, {'host': 'foo', 'binary': 'manila-share', 'disabled': True}) put_services_disable = put_os_services_disable def get_v2(self, **kw): body = { "versions": [ { "status": "CURRENT", "updated": "2015-07-30T11:33:21Z", "links": [ { "href": "http://docs.openstack.org/", "type": "text/html", "rel": "describedby", }, { "href": "http://localhost:8786/v2/", "rel": "self", } ], "min_version": "2.0", "version": "2.5", "id": "v1.0", } ] } return (200, {}, body) def get_shares_1234(self, **kw): share = {'share': {'id': 1234, 'name': 'sharename'}} return (200, {}, share) def get_share_servers_1234(self, **kw): share_servers = { 'share_server': { 'id': 1234, 'share_network_id': 'fake_network_id_1', 'backend_details': {}, }, } return (200, {}, share_servers) def get_share_servers_5678(self, **kw): share_servers = { 'share_server': { 'id': 5678, 'share_network_id': 'fake_network_id_2', }, } return (200, {}, share_servers) def get_shares_1111(self, **kw): share = {'share': {'id': 1111, 'name': 'share1111'}} return (200, {}, share) def get_shares(self, **kw): endpoint = "http://127.0.0.1:8786/v2" share_id = '1234' shares = { 'shares': [ { 'id': share_id, 'name': 'sharename', 'links': [ {"href": endpoint + "/fake_project/shares/" + share_id, "rel": "self"}, ], }, ] } return (200, {}, shares) def get_shares_detail(self, **kw): endpoint = "http://127.0.0.1:8786/v2" share_id = '1234' shares = { 'shares': [ { 'id': share_id, 'name': 'sharename', 'status': 'fake_status', 'size': 1, 'host': 'fake_host', 'export_location': 'fake_export_location', 'snapshot_id': 'fake_snapshot_id', 'links': [ {"href": endpoint + "/fake_project/shares/" + share_id, "rel": "self"}, ], }, ], 'count': 2, } return (200, {}, shares) def get_snapshots_1234(self, **kw): snapshot = {'snapshot': {'id': 1234, 'name': 'sharename'}} return (200, {}, snapshot) def get_share_servers(self, **kw): share_servers = { 'share_servers': [ { 'id': 1234, 'host': 'fake_host', 'status': 'fake_status', 'share_network': 'fake_share_nw', 'project_id': 'fake_project_id', 'updated_at': 'fake_updated_at', 'name': 'fake_name', 'share_name': 'fake_share_name', } ] } return (200, {}, share_servers) def post_snapshots_1234_action(self, body, **kw): _body = None resp = 202 assert len(list(body)) == 1 action = list(body)[0] if action in ('reset_status', 'os-reset_status'): assert 'status' in body.get( 'reset_status', body.get('os-reset_status')) elif action in ('force_delete', 'os-force_delete'): assert body[action] is None elif action in ('unmanage', ): assert body[action] is None elif action in 'allow_access': assert 'access_type' in body['allow_access'] assert 'access_to' in body['allow_access'] _body = {'snapshot_access': body['allow_access']} elif action in 'deny_access': assert 'access_id' in body['deny_access'] else: raise AssertionError("Unexpected action: %s" % action) return (resp, {}, _body) post_snapshots_5678_action = post_snapshots_1234_action def post_snapshots_manage(self, body, **kw): _body = {'snapshot': {'id': 'fake'}} resp = 202 if not ('share_id' in body['snapshot'] and 'provider_location' in body['snapshot'] and 'driver_options' in body['snapshot']): resp = 422 result = (resp, {}, _body) return result def _share_instances(self): instances = { 'share_instances': [ fake_share_instance ] } return (200, {}, instances) def put_quota_sets_1234(self, *args, **kwargs): return (200, {}, {}) def get_quota_sets_1234(self, *args, **kwargs): quota_set = { 'quota_set': { 'id': '1234', 'shares': 50, 'gigabytes': 1000, 'snapshots': 50, 'snapshot_gigabytes': 1000, 'share_networks': 10, } } return (200, {}, quota_set) def get_quota_sets_1234_detail(self, *args, **kwargs): quota_set = { 'quota_set': { 'id': '1234', 'shares': {'in_use': 0, 'limit': 50, 'reserved': 0}, 'gigabytes': {'in_use': 0, 'limit': 10000, 'reserved': 0}, 'snapshots': {'in_use': 0, 'limit': 50, 'reserved': 0}, 'snapshot_gigabytes': {'in_use': 0, 'limit': 1000, 'reserved': 0}, 'share_networks': {'in_use': 0, 'limit': 10, 'reserved': 0}, } } return (200, {}, quota_set) def get_share_instances(self, **kw): return self._share_instances() def get_share_instances_1234_export_locations(self, **kw): export_locations = { 'export_locations': [ get_fake_export_location(), ] } return (200, {}, export_locations) get_shares_1234_export_locations = ( get_share_instances_1234_export_locations) def get_share_instances_1234_export_locations_fake_el_uuid(self, **kw): export_location = {'export_location': get_fake_export_location()} return (200, {}, export_location) get_shares_1234_export_locations_fake_el_uuid = ( get_share_instances_1234_export_locations_fake_el_uuid) def get_shares_fake_instances(self, **kw): return self._share_instances() def get_shares_1234_instances(self, **kw): return self._share_instances() def get_share_instances_1234(self): return (200, {}, {'share_instance': fake_share_instance}) def post_share_instances_1234_action(self, body, **kw): _body = None resp = 202 assert len(list(body)) == 1 action = list(body)[0] if action in ('reset_status', 'os-reset_status'): assert 'status' in body.get( 'reset_status', body.get('os-reset_status')) elif action == 'os-force_delete': assert body[action] is None else: raise AssertionError("Unexpected share action: %s" % action) return (resp, {}, _body) def get_snapshots(self, **kw): snapshots = { 'snapshots': [ { 'id': 1234, 'status': 'available', 'name': 'sharename', } ] } return (200, {}, snapshots) def get_snapshots_detail(self, **kw): snapshots = {'snapshots': [{ 'id': 1234, 'created_at': '2012-08-27T00:00:00.000000', 'share_size': 1, 'share_id': 4321, 'status': 'available', 'name': 'sharename', 'display_description': 'description', 'share_proto': 'type', 'export_location': 'location', }]} return (200, {}, snapshots) def post_os_share_manage(self, body, **kw): _body = {'share': {'id': 'fake'}} resp = 202 if not ('service_host' in body['share'] and 'share_type' in body['share'] and 'export_path' in body['share'] and 'protocol' in body['share'] and 'driver_options' in body['share']): resp = 422 result = (resp, {}, _body) return result post_shares_manage = post_os_share_manage def post_share_servers_manage(self, body, **kw): _body = {'share_server': {'id': 'fake'}} resp = 202 if not ('host' in body['share_server'] and 'share_network' in body['share_server'] and 'identifier' in body['share_server']): resp = 422 result = (resp, {}, _body) return result def post_share_servers_1234_action(self, body, **kw): _body = None assert len(list(body)) == 1 action = list(body)[0] if action in ('reset_status', ): assert 'status' in body.get( 'reset_status', body.get('os-reset_status')) _body = { 'reset_status': {'status': body['reset_status']['status']} } elif action in ('unmanage', ): assert 'force' in body[action] resp = 202 result = (resp, {}, _body) return result def post_os_share_unmanage_1234_unmanage(self, **kw): _body = None resp = 202 result = (resp, {}, _body) return result def post_shares_1234_action(self, body, **kw): _body = None resp = 202 assert len(list(body)) == 1 action = list(body)[0] if action in ('os-allow_access', 'allow_access'): expected = ['access_to', 'access_type'] actual = sorted(list(body[action])) err_msg = "expected '%s', actual is '%s'" % (expected, actual) assert expected == actual, err_msg _body = {'access': {}} elif action in ('os-deny_access', 'deny_access'): assert list(body[action]) == ['access_id'] elif action in ('os-access_list', 'access_list'): assert body[action] is None elif action in ('os-reset_status', 'reset_status'): assert 'status' in body.get( 'reset_status', body.get('os-reset_status')) elif action in ('os-force_delete', 'force_delete'): assert body[action] is None elif action in ('os-extend', 'os-shrink', 'extend', 'shrink'): assert body[action] is not None assert body[action]['new_size'] is not None elif action in ('unmanage', ): assert body[action] is None elif action in ('revert', ): assert body[action] is not None assert body[action]['snapshot_id'] is not None elif action in ( 'migration_cancel', 'migration_complete', 'migration_get_progress'): assert body[action] is None if 'migration_get_progress' == action: _body = {'total_progress': 50} return 200, {}, _body elif action in ( 'os-migrate_share', 'migrate_share', 'migration_start'): assert 'host' in body[action] elif action == 'reset_task_state': assert 'task_state' in body[action] else: raise AssertionError("Unexpected share action: %s" % action) return (resp, {}, _body) def post_shares_1111_action(self, body, **kw): _body = None resp = 202 assert len(list(body)) == 1 action = list(body)[0] if action in ('allow_access', 'os-allow_access'): expected = ['access_level', 'access_to', 'access_type'] actual = sorted(list(body[action])) err_msg = "expected '%s', actual is '%s'" % (expected, actual) assert expected == actual, err_msg _body = {'access': {}} elif action in ('access_list', 'os-access_list'): assert body[action] is None _body = { 'access_list': [{ 'access_level': 'rw', 'state': 'active', 'id': '1122', 'access_type': 'ip', 'access_to': '10.0.0.7' }] } else: raise AssertionError("Unexpected share action: %s" % action) return (resp, {}, _body) def get_share_access_rules(self, **kw): access = { 'access_list': [{ 'access_level': 'rw', 'state': 'active', 'id': '1122', 'access_type': 'ip', 'access_to': '10.0.0.7', 'metadata': {'key1': 'v1'} }] } return (200, {}, access) def get_share_access_rules_9999(self, **kw): access = { 'access': { 'access_level': 'rw', 'state': 'active', 'id': '9999', 'access_type': 'ip', 'access_to': '10.0.0.7', 'metadata': {'key1': 'v1'} } } return (200, {}, access) def put_share_access_rules_9999_metadata(self, **kw): return (200, {}, {'metadata': {'key1': 'v1', 'key2': 'v2'}}) def delete_share_access_rules_9999_metadata_key1(self, **kw): return (200, {}, None) def get_shares_2222(self, **kw): share = {'share': {'id': 2222, 'name': 'sharename'}} return (200, {}, share) def post_shares_2222_action(self, body, **kw): return (202, {}, {'access': {}}) def post_share_networks(self, **kwargs): return (202, {}, {'share_network': {}}) def post_share_networks_1234_subnets(self, **kwargs): return (202, {}, {'share_network_subnet': {}}) def post_shares(self, **kwargs): return (202, {}, {'share': {}}) def post_snapshots(self, **kwargs): return (202, {}, {'snapshot': {}}) def delete_shares_1234(self, **kw): return (202, {}, None) def delete_snapshots_1234(self, **kwargs): return (202, {}, None) def delete_share_servers_1234(self, **kwargs): return (202, {}, None) def delete_share_servers_5678(self, **kwargs): return (202, {}, None) def delete_security_services_fake_security_service1(self, **kwargs): return (202, {}, None) def delete_security_services_fake_security_service2(self, **kwargs): return (202, {}, None) def delete_share_networks_fake_share_network1(self, **kwargs): return (202, {}, None) def delete_share_networks_fake_share_network2(self, **kwargs): return (202, {}, None) def delete_share_networks_1234_subnets_fake_subnet1(self, **kwargs): return (202, {}, None) def delete_share_networks_1234_subnets_fake_subnet2(self, **kwargs): return (202, {}, None) def delete_snapshots_fake_snapshot1(self, **kwargs): return (202, {}, None) def delete_snapshots_fake_snapshot2(self, **kwargs): return (202, {}, None) def post_snapshots_fake_snapshot_force1_action(self, **kwargs): return (202, {}, None) def post_snapshots_fake_snapshot_force2_action(self, **kwargs): return (202, {}, None) def delete_types_fake_type1(self, **kwargs): return (202, {}, None) def delete_types_fake_type2(self, **kwargs): return (202, {}, None) def delete_share_servers_fake_share_server1(self, **kwargs): return (202, {}, None) def delete_share_servers_fake_share_server2(self, **kwargs): return (202, {}, None) def put_share_networks_1111(self, **kwargs): share_network = {'share_network': {'id': 1111}} return (200, {}, share_network) def put_shares_1234(self, **kwargs): share = {'share': {'id': 1234, 'name': 'sharename'}} return (200, {}, share) def put_snapshots_1234(self, **kwargs): snapshot = {'snapshot': {'id': 1234, 'name': 'snapshot_name'}} return (200, {}, snapshot) def get_share_networks_1111(self, **kw): share_nw = {'share_network': {'id': 1111, 'name': 'fake_share_nw'}} return (200, {}, share_nw) def post_share_networks_1234_action(self, **kw): share_nw = {'share_network': {'id': 1111, 'name': 'fake_share_nw'}} return (200, {}, share_nw) def get_share_networks_detail(self, **kw): share_nw = { 'share_networks': [ {'id': 1234, 'name': 'fake_share_nw'}, {'id': 4321, 'name': 'duplicated_name'}, {'id': 4322, 'name': 'duplicated_name'}, ] } return (200, {}, share_nw) def get_share_networks_1234_subnets_fake_subnet_id(self, **kw): subnet = { 'share_network_subnet': { 'id': 'fake_subnet_id', }, } return (200, {}, subnet) def get_security_services(self, **kw): security_services = { 'security_services': [ { 'id': 1111, 'name': 'fake_security_service', 'type': 'fake_type', 'status': 'fake_status', }, ], } return (200, {}, security_services) def get_security_services_detail(self, **kw): security_services = { 'security_services': [ { 'id': 1111, 'name': 'fake_security_service', 'description': 'fake_description', 'share_network_id': 'fake_share-network_id', 'user': 'fake_user', 'password': 'fake_password', 'domain': 'fake_domain', 'server': 'fake_server', 'dns_ip': 'fake_dns_ip', 'ou': 'fake_ou', 'type': 'fake_type', 'status': 'fake_status', 'project_id': 'fake_project_id', 'updated_at': 'fake_updated_at', }, ], } return (200, {}, security_services) def get_security_services_1111(self, **kw): ss = {'security_service': {'id': 1111, 'name': 'fake_ss'}} return (200, {}, ss) def put_security_services_1111(self, **kwargs): ss = {'security_service': {'id': 1111, 'name': 'fake_ss'}} return (200, {}, ss) def get_scheduler_stats_pools(self, **kw): pools = { 'pools': [ { 'name': 'host1@backend1#pool1', 'host': 'host1', 'backend': 'backend1', 'pool': 'pool1', }, { 'name': 'host1@backend1#pool2', 'host': 'host1', 'backend': 'backend1', 'pool': 'pool2', } ] } return (200, {}, pools) def get_scheduler_stats_pools_detail(self, **kw): pools = { 'pools': [ { 'name': 'host1@backend1#pool1', 'host': 'host1', 'backend': 'backend1', 'pool': 'pool1', 'capabilities': {'qos': True}, }, { 'name': 'host1@backend1#pool2', 'host': 'host1', 'backend': 'backend1', 'pool': 'pool2', 'capabilities': {'qos': False}, } ] } return (200, {}, pools) fake_share_group = { 'id': '1234', 'availability_zone': 'nova', 'share_network_id': None, 'status': 'available', 'name': 'share group name', 'description': 'my share group', } def get_share_groups_detail(self, **kw): share_groups = {'share_groups': [self.fake_share_group]} return 200, {}, share_groups def get_share_groups_1234(self, **kw): share_group = {'share_group': self.fake_share_group} return 200, {}, share_group def put_share_groups_1234(self, **kwargs): share_group = {'share_group': self.fake_share_group} return 200, {}, share_group def delete_share_groups_1234(self, **kw): return 202, {}, None def post_share_groups_1234_action(self, **kw): return 202, {}, None def post_share_groups(self, body, **kw): share_group = { 'share_group': { 'id': 'fake-sg-id', 'name': 'fake_name', } } return 202, {}, share_group fake_share_group_snapshot = { 'id': '1234', 'status': 'available', 'name': 'share group name', 'description': 'my share group', } def get_share_group_snapshots(self, **kw): sg_snapshots = { 'share_group_snapshots': [self.fake_share_group_snapshot], } return 200, {}, sg_snapshots def get_share_group_snapshots_detail(self, **kw): sg_snapshots = { 'share_group_snapshots': [self.fake_share_group_snapshot], } return 200, {}, sg_snapshots def get_share_group_snapshots_1234(self, **kw): sg_snapshot = {'share_group_snapshot': self.fake_share_group_snapshot} return 200, {}, sg_snapshot def put_share_group_snapshots_1234(self, **kwargs): sg_snapshot = { 'share_group_snapshot': self.fake_share_group_snapshot, } return 200, {}, sg_snapshot def delete_share_group_snapshots_1234(self, **kw): return 202, {}, None def post_share_group_snapshots_1234_action(self, **kw): return 202, {}, None def post_share_group_snapshots(self, body, **kw): sg_snapshot = { 'share_group_snapshot': { 'id': 3, 'name': 'cust_snapshot', } } return 202, {}, sg_snapshot fake_share_replica = { "id": "5678", "share_id": "1234", "availability_zone": "nova", "share_network_id": None, "export_locations": [], "share_server_id": None, "host": "", "status": "error", "replica_state": "error", "created_at": "2015-10-05T18:21:33.000000", "export_location": None, } def delete_share_replicas_1234(self, **kw): return (202, {}, None) def delete_share_replicas_fake_replica_0(self, **kw): return (202, {}, None) def delete_share_replicas_fake_replica_1(self, **kw): return (202, {}, None) def get_share_replicas_detail(self, **kw): replicas = { 'share_replicas': [ self.fake_share_replica, ] } return (200, {}, replicas) def get_share_replicas_5678(self, **kw): replicas = {'share_replica': self.fake_share_replica} return (200, {}, replicas) def get_share_replicas_5678_export_locations(self, **kw): export_locations = { 'export_locations': [ get_fake_export_location(), ] } return (200, {}, export_locations) def get_share_replicas_1234_export_locations(self, **kw): export_locations = { 'export_locations': [ get_fake_export_location(), ] } return (200, {}, export_locations) def get_share_replicas_1234_export_locations_fake_el_uuid(self, **kw): export_location = {'export_location': get_fake_export_location()} return (200, {}, export_location) def post_share_replicas(self, **kw): return (202, {}, {'share_replica': self.fake_share_replica}) def post_share_replicas_1234_action(self, body, **kw): _body = None resp = 202 assert len(list(body)) == 1 action = list(body)[0] if action in ('reset_status', 'reset_replica_state'): attr = action.split('reset_')[1] assert attr in body.get(action) elif action in ('force_delete', 'resync', 'promote'): assert body[action] is None else: raise AssertionError("Unexpected share action: %s" % action) return (resp, {}, _body) # # Set/Unset metadata # def delete_shares_1234_metadata_test_key(self, **kw): return (204, {}, None) def delete_shares_1234_metadata_key1(self, **kw): return (204, {}, None) def delete_shares_1234_metadata_key2(self, **kw): return (204, {}, None) def post_shares_1234_metadata(self, **kw): return (204, {}, {'metadata': {'test_key': 'test_value'}}) def put_shares_1234_metadata(self, **kw): return (200, {}, {"metadata": {"key1": "val1", "key2": "val2"}}) def get_shares_1234_metadata(self, **kw): return (200, {}, {"metadata": {"key1": "val1", "key2": "val2"}}) def get_types_default(self, **kw): return self.get_types_1(**kw) def get_types_1234(self, **kw): return (200, {}, { 'share_type': {'id': 1234, 'name': 'test-type-1234', 'share_type_access:is_public': True, 'description': "test share type desc", 'extra_specs': {'test': 'test'}, 'required_extra_specs': {'test': 'test'}}}) def get_types(self, **kw): req_version = self.default_headers['X-Openstack-Manila-Api-Version'] if not isinstance(req_version, api_versions.APIVersion): req_version = api_versions.APIVersion(req_version) response_body = { 'share_types': [{'id': 1, 'name': 'test-type-1', 'extra_specs': {'test1': 'test1'}, 'required_extra_specs': {'test': 'test'}}, {'id': 2, 'name': 'test-type-2', 'extra_specs': {'test1': 'test1'}, 'required_extra_specs': {'test': 'test'}}] } if req_version >= api_versions.APIVersion('2.46'): response_body['share_types'][0]['is_default'] = False response_body['share_types'][1]['is_default'] = False return 200, {}, response_body def get_types_1(self, **kw): return (200, {}, {'share_type': { 'id': 1, 'name': 'test-type-1', 'extra_specs': {'test': 'test'}, 'required_extra_specs': {'test': 'test'}}}) def get_types_2(self, **kw): return (200, {}, {'share_type': { 'id': 2, 'name': 'test-type-2', 'extra_specs': {'test': 'test'}, 'required_extra_specs': {'test': 'test'}}}) def get_types_3(self, **kw): return (200, {}, { 'share_type': { 'id': 3, 'name': 'test-type-3', 'extra_specs': {}, 'os-share-type-access:is_public': False } }) def get_types_4(self, **kw): return (200, {}, { 'share_type': { 'id': 4, 'name': 'test-type-3', 'extra_specs': {}, 'os-share-type-access:is_public': True } }) def post_types(self, body, **kw): share_type = body['share_type'] required_extra_specs = { "driver_handles_share_servers": share_type[ 'extra_specs']['driver_handles_share_servers'], } return (202, {}, { 'share_type': { 'id': 3, 'name': 'test-type-3', 'is_default': False, 'description': 'test description', 'extra_specs': share_type['extra_specs'], 'required_extra_specs': required_extra_specs, } }) def post_types_3_action(self, body, **kw): _body = None resp = 202 assert len(list(body)) == 1 action = list(body)[0] if action == 'addProjectAccess': assert 'project' in body['addProjectAccess'] elif action == 'removeProjectAccess': assert 'project' in body['removeProjectAccess'] else: raise AssertionError('Unexpected action: %s' % action) return (resp, {}, _body) def post_types_1_extra_specs(self, body, **kw): assert list(body) == ['extra_specs'] return (200, {}, {'extra_specs': {'k': 'v'}}) def delete_types_1_extra_specs_k(self, **kw): return(204, {}, None) def delete_types_1(self, **kw): return (202, {}, None) def get_types_3_os_share_type_access(self, **kw): return (200, {}, {'share_type_access': [ {'share_type_id': '11111111-1111-1111-1111-111111111111', 'project_id': '00000000-0000-0000-0000-000000000000'} ]}) get_types_3_share_type_access = get_types_3_os_share_type_access fake_snapshot_instance = { "id": "1234", "snapshot_id": "5678", "status": "error", } def get_snapshot_instances(self, **kw): instances = { 'snapshot_instances': [ self.fake_snapshot_instance, ] } return (200, {}, instances) def get_snapshot_instances_detail(self, **kw): instances = { 'snapshot_instances': [ { 'id': '1234', 'snapshot_id': '5679', 'created_at': 'fake', 'updated_at': 'fake', 'status': 'fake', 'share_id': 'fake', 'share_instance_id': 'fake', 'progress': 'fake', 'provider_location': 'fake', } ] } return (200, {}, instances) def get_snapshot_instances_1234(self, **kw): instances = {'snapshot_instance': self.fake_snapshot_instance} return (200, {}, instances) def get_snapshot_instances_1234_export_locations_fake_el_id(self, **kw): return (200, {}, {'share_snapshot_export_location': { 'id': 'fake_id', 'path': '/fake_path'}}) def get_snapshots_1234_export_locations_fake_el_id(self, **kw): return (200, {}, {'share_snapshot_export_location': { 'id': 'fake_id', 'path': '/fake_path'}}) def get_snapshot_instances_1234_export_locations( self, **kw): snapshot_export_location = {'share_snapshot_export_locations': [get_fake_export_location()]} return (200, {}, snapshot_export_location) def get_snapshots_1234_export_locations(self): snapshot_export_location = {'share_snapshot_export_locations': [get_fake_export_location()]} return (200, {}, snapshot_export_location) def get_snapshots_1234_access_list(self, **kw): access_list = {'snapshot_access_list': [{ 'state': 'active', 'id': '1234', 'access_type': 'ip', 'access_to': '6.6.6.6' }]} return (200, {}, access_list) def post_snapshot_instances_1234_action(self, body, **kw): _body = None resp = 202 assert len(list(body)) == 1 action = list(body)[0] if action == 'reset_status': assert 'status' in body.get(action) else: raise AssertionError("Unexpected share action: %s" % action) return (resp, {}, _body) def get_share_group_types_default(self, **kw): return self.get_share_group_types_1(**kw) def get_share_group_types(self, **kw): share_group_types = { 'share_group_types': [ { 'id': 1, 'name': 'test-group-type-1', 'group_specs': { 'key1': 'value1', }, 'share_types': [ 'type1', 'type2', ], 'is_public': True, }, { 'id': 2, 'name': 'test-type-2', 'group_specs': { 'key2': 'value2', }, 'share_types': [ 'type3', 'type4', ], 'is_public': False, }, ], } req_version = self.default_headers['X-Openstack-Manila-Api-Version'] if req_version >= api_versions.APIVersion('2.46'): share_group_types['share_group_types'][0]['is_default'] = False share_group_types['share_group_types'][1]['is_default'] = False return 200, {}, share_group_types def get_share_group_types_1(self, **kw): share_group_type = { 'share_group_type': { 'id': 1, 'name': 'test-group-type-1', 'group_specs': { 'key1': 'value1', }, 'share_types': [ 'type1', 'type2', ], 'is_public': True, }, } return 200, {}, share_group_type def get_share_group_types_2(self, **kw): share_group_type = { 'share_type': { 'id': 2, 'name': 'test-group-type-2', 'group_specs': { 'key2': 'value2', }, 'share_types': [ 'type3', 'type4', ], 'is_public': True, }, } return 200, {}, share_group_type def post_share_group_types(self, body, **kw): share_group_type = { 'share_group_type': { 'id': 1, 'name': 'test-group-type-1', 'share_types': body['share_group_type']['share_types'], 'is_public': True, }, } return 202, {}, share_group_type def post_share_group_types_1234_action(self, body, **kw): assert len(list(body)) == 1 action = list(body)[0] if action == 'addProjectAccess': assert 'project' in body['addProjectAccess'] elif action == 'removeProjectAccess': assert 'project' in body['removeProjectAccess'] else: raise AssertionError('Unexpected action: %s' % action) return 202, {}, None def post_share_group_types_1_specs(self, body, **kw): assert list(body) == ['group_specs'] return 200, {}, {'group_specs': {'k': 'v'}} def delete_share_group_types_1_specs_k(self, **kw): return 204, {}, None def delete_share_group_types_1234(self, **kw): return 202, {}, None def get_share_group_types_1234_access(self, **kw): sg_type_access = { 'share_group_type_access': [{ 'group_type_id': '11111111-1111-1111-1111-111111111111', 'project_id': '00000000-0000-0000-0000-000000000000', }], } return 200, {}, sg_type_access fake_message = { 'id': 'fake message id', 'action_id': '001', 'detail_id': '002', 'user_message': 'user message', 'message_level': 'ERROR', 'resource_type': 'SHARE', 'resource_id': 'resource id', 'created_at': '2015-08-27T09:49:58-05:00', 'expires_at': '2015-09-27T09:49:58-05:00', 'request_id': 'req-936666d2-4c8f-4e41-9ac9-237b43f8b848', } def get_messages(self, **kw): messages = { 'messages': [self.fake_message], } return 200, {}, messages def get_messages_1234(self, **kw): message = {'message': self.fake_message} return 200, {}, message def delete_messages_1234(self, **kw): return 202, {}, None def delete_messages_5678(self, **kw): return 202, {}, None def fake_create(url, body, response_key): return {'url': url, 'body': body, 'resp_key': response_key} def fake_update(url, body, response_key): return {'url': url, 'body': body, 'resp_key': response_key} class FakeQuotaSet(object): def __init__(self, dictionary): self.dictionary = dictionary def to_dict(self): return self.dictionary class ShareNetwork(object): id = 'fake share network id' name = 'fake share network name' class ShareType(object): id = 'fake share type id' name = 'fake share type name' class ShareGroupType(object): id = 'fake group type id' name = 'fake group type name' share_types = [ShareType().id] is_public = False class ShareGroupTypeAccess(object): id = 'fake group type access id' name = 'fake group type access name' class ShareGroup(object): id = 'fake group id' share_types = [ShareType().id] group_type_id = ShareGroupType().id share_network_id = ShareNetwork().id name = 'fake name' description = 'fake description' availability_zone = 'fake az' class ShareGroupSnapshot(object): id = 'fake group snapshot id' share_group_id = ShareGroup().id, share_network_id = ShareNetwork().id name = 'fake name' description = 'fake description' class Message(object): id = 'fake message id' action_id = '001' detail_id = '002' user_message = 'user message' message_level = 'ERROR' resource_type = 'SHARE' resource_id = 'resource id' created_at = '2015-08-27T09:49:58-05:00' expires_at = '2015-09-27T09:49:58-05:00' request_id = 'req-936666d2-4c8f-4e41-9ac9-237b43f8b848' python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_group_type_access.py0000664000175000017500000000773413644133413031151 0ustar zuulzuul00000000000000# Copyright 2016 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. import mock import ddt import six import manilaclient from manilaclient import exceptions from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes as fake from manilaclient.v2 import share_group_type_access as type_access @ddt.ddt class ShareGroupTypeAccessTest(utils.TestCase): def setUp(self): super(ShareGroupTypeAccessTest, self).setUp() self.manager = type_access.ShareGroupTypeAccessManager( fake.FakeClient()) fake_group_type_access_info = {'id': fake.ShareGroupTypeAccess.id} self.share_group_type_access = type_access.ShareGroupTypeAccess( self.manager, fake_group_type_access_info, loaded=True) def test_repr(self): result = six.text_type(self.share_group_type_access) self.assertEqual( '' % fake.ShareGroupTypeAccess.id, result) @ddt.ddt class ShareGroupTypeAccessManagerTest(utils.TestCase): def setUp(self): super(ShareGroupTypeAccessManagerTest, self).setUp() self.manager = type_access.ShareGroupTypeAccessManager( fake.FakeClient()) def test_list(self): fake_share_group_type_access = fake.ShareGroupTypeAccess() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_share_group_type_access])) result = self.manager.list(fake.ShareGroupType(), search_opts=None) self.assertEqual([fake_share_group_type_access], result) mock_list.assert_called_once_with( type_access.RESOURCE_PATH % fake.ShareGroupType.id, type_access.RESOURCE_NAME) def test_list_public(self): fake_share_group_type_access = fake.ShareGroupTypeAccess() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_share_group_type_access])) fake_share_group_type = fake.ShareGroupType() fake_share_group_type.is_public = True result = self.manager.list(fake_share_group_type) self.assertIsNone(result) self.assertFalse(mock_list.called) def test_list_using_unsupported_microversion(self): fake_share_group_type_access = fake.ShareGroupTypeAccess() self.manager.api.api_version = manilaclient.API_MIN_VERSION self.assertRaises( exceptions.UnsupportedVersion, self.manager.list, fake_share_group_type_access) def test_add_project_access(self): mock_post = self.mock_object(self.manager.api.client, 'post') self.manager.add_project_access(fake.ShareGroupType(), 'fake_project') expected_body = { 'addProjectAccess': { 'project': 'fake_project', } } mock_post.assert_called_once_with( type_access.RESOURCE_PATH_ACTION % fake.ShareGroupType.id, body=expected_body) def test_remove_project_access(self): mock_post = self.mock_object(self.manager.api.client, 'post') self.manager.remove_project_access( fake.ShareGroupType(), 'fake_project') expected_body = { 'removeProjectAccess': { 'project': 'fake_project', } } mock_post.assert_called_once_with( type_access.RESOURCE_PATH_ACTION % fake.ShareGroupType.id, body=expected_body) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_types.py0000664000175000017500000004472713644133413025420 0ustar zuulzuul00000000000000# 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 copy import ddt import itertools import mock from manilaclient import api_versions from manilaclient import config from manilaclient import exceptions from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import share_types cs = fakes.FakeClient() CONF = config.CONF LATEST_MICROVERSION = CONF.max_api_microversion def get_valid_type_create_data_2_0(): public = [True, False] dhss = [True, False] snapshot = [None, True, False] extra_specs = [None, {'foo': 'bar'}] combos = list(itertools.product(public, dhss, snapshot, extra_specs)) return combos def get_valid_type_create_data_2_24(): public = [True, False] dhss = [True, False] snapshot = [None] create_from_snapshot = [None] extra_specs = [None, {'replication_type': 'writable', 'foo': 'bar'}] snapshot_none_combos = list(itertools.product(public, dhss, snapshot, create_from_snapshot, extra_specs)) public = [True, False] dhss = [True, False] snapshot = [True] create_from_snapshot = [True, False, None] extra_specs = [None, {'replication_type': 'readable', 'foo': 'bar'}] snapshot_true_combos = list(itertools.product(public, dhss, snapshot, create_from_snapshot, extra_specs)) public = [True, False] dhss = [True, False] snapshot = [False] create_from_snapshot = [False, None] extra_specs = [None, {'replication_type': 'dr', 'foo': 'bar'}] snapshot_false_combos = list(itertools.product(public, dhss, snapshot, create_from_snapshot, extra_specs)) return snapshot_none_combos + snapshot_true_combos + snapshot_false_combos def get_valid_type_create_data_2_27(): public = [True, False] dhss = [True, False] snapshot = [None] create_from_snapshot = [None] revert_to_snapshot = [None] extra_specs = [None, {'replication_type': 'writable', 'foo': 'bar'}] snapshot_none_combos = list(itertools.product(public, dhss, snapshot, create_from_snapshot, revert_to_snapshot, extra_specs)) public = [True, False] dhss = [True, False] snapshot = [True] create_from_snapshot = [True, False, None] revert_to_snapshot = [True, False, None] extra_specs = [None, {'replication_type': 'readable', 'foo': 'bar'}] snapshot_true_combos = list(itertools.product(public, dhss, snapshot, create_from_snapshot, revert_to_snapshot, extra_specs)) public = [True, False] dhss = [True, False] snapshot = [False] create_from_snapshot = [False, None] revert_to_snapshot = [False, None] extra_specs = [None, {'replication_type': 'dr', 'foo': 'bar'}] snapshot_false_combos = list(itertools.product(public, dhss, snapshot, create_from_snapshot, revert_to_snapshot, extra_specs)) return snapshot_none_combos + snapshot_true_combos + snapshot_false_combos @ddt.ddt class TypesTest(utils.TestCase): def _get_share_types_manager(self, microversion): version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) return share_types.ShareTypeManager(api=mock_microversion) @ddt.data( {'snapshot_support': 'False'}, {'snapshot_support': 'False', 'foo': 'bar'}, ) def test_init(self, extra_specs): info = {'extra_specs': extra_specs} share_type = share_types.ShareType(share_types.ShareTypeManager, info) self.assertTrue(hasattr(share_type, '_required_extra_specs')) self.assertTrue(hasattr(share_type, '_optional_extra_specs')) self.assertIsInstance(share_type._required_extra_specs, dict) self.assertIsInstance(share_type._optional_extra_specs, dict) self.assertEqual(extra_specs, share_type.get_optional_keys()) def test_list_types(self): tl = cs.share_types.list() cs.assert_called('GET', '/types?is_public=all') for t in tl: self.assertIsInstance(t, share_types.ShareType) self.assertTrue(callable(getattr(t, 'get_required_keys', ''))) self.assertTrue(callable(getattr(t, 'get_optional_keys', ''))) self.assertEqual({'test': 'test'}, t.get_required_keys()) self.assertEqual({'test1': 'test1'}, t.get_optional_keys()) def test_list_types_only_public(self): cs.share_types.list(show_all=False) cs.assert_called('GET', '/types') def test_list_types_search_by_extra_specs(self): search_opts = {'extra_specs': {'aa': 'bb'}} cs.share_types.list(search_opts=search_opts) expect = '/types?extra_specs=%7B%27aa%27%3A+%27bb%27%7D&is_public=all' cs.assert_called('GET', expect) @ddt.data(*get_valid_type_create_data_2_0()) @ddt.unpack def test_create_2_7(self, is_public, dhss, snapshot, extra_specs): extra_specs = copy.copy(extra_specs) manager = self._get_share_types_manager("2.7") self.mock_object(manager, '_create', mock.Mock(return_value="fake")) result = manager.create( 'test-type-3', spec_driver_handles_share_servers=dhss, spec_snapshot_support=snapshot, extra_specs=extra_specs, is_public=is_public) if extra_specs is None: extra_specs = {} expected_extra_specs = dict(extra_specs) expected_body = { "share_type": { "name": 'test-type-3', 'share_type_access:is_public': is_public, "extra_specs": expected_extra_specs, } } expected_body["share_type"]["extra_specs"][ "driver_handles_share_servers"] = dhss expected_body["share_type"]["extra_specs"]['snapshot_support'] = ( True if snapshot is None else snapshot) manager._create.assert_called_once_with( "/types", expected_body, "share_type") self.assertEqual("fake", result) def _add_standard_extra_specs_to_dict(self, extra_specs, create_from_snapshot=None, revert_to_snapshot=None, mount_snapshot=None): # Short-circuit checks to allow for extra specs to be (and remain) None if all(spec is None for spec in [ create_from_snapshot, revert_to_snapshot, mount_snapshot]): return extra_specs extra_specs = extra_specs or {} if create_from_snapshot is not None: extra_specs['create_share_from_snapshot_support'] = ( create_from_snapshot) if revert_to_snapshot is not None: extra_specs['revert_to_snapshot_support'] = ( revert_to_snapshot) if mount_snapshot is not None: extra_specs['mount_snapshot_support'] = ( mount_snapshot) return extra_specs @ddt.data(*get_valid_type_create_data_2_24()) @ddt.unpack def test_create_2_24(self, is_public, dhss, snapshot, create_from_snapshot, extra_specs): extra_specs = copy.copy(extra_specs) extra_specs = self._add_standard_extra_specs_to_dict( extra_specs, create_from_snapshot=create_from_snapshot) manager = self._get_share_types_manager("2.24") self.mock_object(manager, '_create', mock.Mock(return_value="fake")) result = manager.create( 'test-type-3', spec_driver_handles_share_servers=dhss, spec_snapshot_support=snapshot, extra_specs=extra_specs, is_public=is_public) expected_extra_specs = dict(extra_specs or {}) expected_extra_specs["driver_handles_share_servers"] = dhss if snapshot is None: expected_extra_specs.pop("snapshot_support", None) else: expected_extra_specs["snapshot_support"] = snapshot if create_from_snapshot is None: expected_extra_specs.pop("create_share_from_snapshot_support", None) else: expected_extra_specs["create_share_from_snapshot_support"] = ( create_from_snapshot) expected_body = { "share_type": { "name": 'test-type-3', 'share_type_access:is_public': is_public, "extra_specs": expected_extra_specs, } } manager._create.assert_called_once_with( "/types", expected_body, "share_type") self.assertEqual("fake", result) @ddt.data(*get_valid_type_create_data_2_27()) @ddt.unpack def test_create_2_27(self, is_public, dhss, snapshot, create_from_snapshot, revert_to_snapshot, extra_specs): extra_specs = copy.copy(extra_specs) extra_specs = self._add_standard_extra_specs_to_dict( extra_specs, create_from_snapshot=create_from_snapshot, revert_to_snapshot=revert_to_snapshot) manager = self._get_share_types_manager("2.27") self.mock_object(manager, '_create', mock.Mock(return_value="fake")) result = manager.create( 'test-type-3', spec_driver_handles_share_servers=dhss, spec_snapshot_support=snapshot, extra_specs=extra_specs, is_public=is_public) expected_extra_specs = dict(extra_specs or {}) expected_extra_specs["driver_handles_share_servers"] = dhss if snapshot is None: expected_extra_specs.pop("snapshot_support", None) else: expected_extra_specs["snapshot_support"] = snapshot if create_from_snapshot is None: expected_extra_specs.pop("create_share_from_snapshot_support", None) else: expected_extra_specs["create_share_from_snapshot_support"] = ( create_from_snapshot) if revert_to_snapshot is None: expected_extra_specs.pop("revert_to_snapshot_support", None) else: expected_extra_specs["revert_to_snapshot_support"] = ( revert_to_snapshot) expected_body = { "share_type": { "name": 'test-type-3', 'share_type_access:is_public': is_public, "extra_specs": expected_extra_specs, } } manager._create.assert_called_once_with( "/types", expected_body, "share_type") self.assertEqual("fake", result) @ddt.data( (False, False, True, {'snapshot_support': True, 'replication_type': 'fake_repl_type'}), (False, False, False, {'snapshot_support': False, 'replication_type': 'fake_repl_type'}), (False, False, True, {'snapshot_support': False, 'replication_type': 'fake_repl_type'}), (False, False, False, {'snapshot_support': True, 'replication_type': 'fake_repl_type'}), (False, True, None, {'driver_handles_share_servers': True}), (False, False, None, {'driver_handles_share_servers': True}), (False, None, None, {'driver_handles_share_servers': True}), (False, None, None, {'driver_handles_share_servers': None}), ) @ddt.unpack def test_create_error_2_7(self, is_public, dhss, snapshot, extra_specs): manager = self._get_share_types_manager("2.7") self.mock_object(manager, '_create', mock.Mock(return_value="fake")) self.assertRaises( exceptions.CommandError, manager.create, 'test-type-3', spec_driver_handles_share_servers=dhss, spec_snapshot_support=snapshot, extra_specs=extra_specs, is_public=is_public) @ddt.data( (False, True, None, None, {'driver_handles_share_servers': True}), (False, False, False, False, {'snapshot_support': True, 'replication_type': 'fake_repl_type'}), ) @ddt.unpack def test_create_error_2_24(self, is_public, dhss, snapshot, create_from_snapshot, extra_specs): extra_specs = copy.copy(extra_specs) extra_specs = self._add_standard_extra_specs_to_dict( extra_specs, create_from_snapshot=create_from_snapshot) manager = self._get_share_types_manager("2.24") self.mock_object(manager, '_create', mock.Mock(return_value="fake")) self.assertRaises( exceptions.CommandError, manager.create, 'test-type-3', spec_driver_handles_share_servers=dhss, spec_snapshot_support=snapshot, extra_specs=extra_specs, is_public=is_public) @ddt.data( ("2.6", True), ("2.7", True), ("2.24", True), ("2.41", True), ("2.6", False), ("2.7", False), ("2.24", False), ("2.41", False), ) @ddt.unpack def test_create_with_default_values(self, microversion, dhss): manager = self._get_share_types_manager(microversion) self.mock_object(manager, '_create', mock.Mock(return_value="fake")) description = 'test description' if (api_versions.APIVersion(microversion) >= api_versions.APIVersion("2.41")): result = manager.create( 'test-type-3', dhss, description=description) else: result = manager.create('test-type-3', dhss) if (api_versions.APIVersion(microversion) > api_versions.APIVersion("2.6")): is_public_keyname = "share_type_access:is_public" else: is_public_keyname = "os-share-type-access:is_public" expected_body = { "share_type": { "name": 'test-type-3', is_public_keyname: True, "extra_specs": { "driver_handles_share_servers": dhss, "snapshot_support": True, } } } if (api_versions.APIVersion(microversion) >= api_versions.APIVersion("2.24")): del expected_body['share_type']['extra_specs']['snapshot_support'] if (api_versions.APIVersion(microversion) >= api_versions.APIVersion("2.41")): expected_body['share_type']['description'] = description manager._create.assert_called_once_with( "/types", expected_body, "share_type") self.assertEqual("fake", result) def test_set_key(self): t = cs.share_types.get(1) t.set_keys({'k': 'v'}) cs.assert_called('POST', '/types/1/extra_specs', {'extra_specs': {'k': 'v'}}) def test_unset_keys(self): t = cs.share_types.get(1) t.unset_keys(['k']) cs.assert_called('DELETE', '/types/1/extra_specs/k') @ddt.data(*set(('2.50', LATEST_MICROVERSION))) def test_update(self, microversion): manager = self._get_share_types_manager(microversion) self.mock_object(manager, '_update', mock.Mock(return_value="fake")) share_type = 1234 name = "updated-test-type-1234" description = "updated test description" is_public_key_name = "share_type_access:is_public" is_public = False expected_body = { "share_type": { "name": name, is_public_key_name: is_public, } } result = manager.update( share_type, name, is_public, description) expected_body['share_type']['description'] = description manager._update.assert_called_once_with( "/types/%s" % share_type, expected_body, "share_type") self.assertEqual("fake", result) def test_delete(self): cs.share_types.delete(1) cs.assert_called('DELETE', '/types/1') def test_get_keys_from_resource_data(self): manager = mock.Mock() manager.api.client.get = mock.Mock(return_value=(200, {})) valid_extra_specs = {'test': 'test'} share_type = share_types.ShareType(mock.Mock(), {'extra_specs': valid_extra_specs, 'name': 'test'}, loaded=True) actual_result = share_type.get_keys() self.assertEqual(actual_result, valid_extra_specs) self.assertEqual(manager.api.client.get.call_count, 0) @ddt.data({'prefer_resource_data': True, 'resource_extra_specs': {}}, {'prefer_resource_data': False, 'resource_extra_specs': {'fake': 'fake'}}, {'prefer_resource_data': False, 'resource_extra_specs': {}}) @ddt.unpack def test_get_keys_from_api(self, prefer_resource_data, resource_extra_specs): manager = mock.Mock() valid_extra_specs = {'test': 'test'} manager.api.client.get = mock.Mock( return_value=(200, {'extra_specs': valid_extra_specs})) info = { 'name': 'test', 'uuid': 'fake', 'extra_specs': resource_extra_specs } share_type = share_types.ShareType(manager, info, loaded=True) actual_result = share_type.get_keys(prefer_resource_data) self.assertEqual(actual_result, valid_extra_specs) self.assertEqual(manager.api.client.get.call_count, 1) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_shell.py0000664000175000017500000041545113644133413025357 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # 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 ddt import fixtures import itertools import mock from oslo_utils import strutils import six 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_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 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(ShellTest, self).setUp() for var in self.FAKE_ENV: self.useFixture(fixtures.EnvironmentVariable(var, self.FAKE_ENV[var])) 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, } } 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(ShellTest, self).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') self.assert_called( 'GET', '/shares/detail?metadata=%7B%27key%27%3A+%27value%27%7D') 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 + u'ффф') 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 + u'ффф') 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 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) 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( 'type-create test-type-3 false --is-public %s' % six.text_type(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', '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', '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', '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): 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) 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(self, driver_args, valid_params, version=None, network_id=None, subnet_id=None): 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 = type( 'FakeShareNetwork', (object,), {'id': network_id}) self.mock_object( shell_v2, '_find_share_network', mock.Mock(return_value=fake_share_network)) command = ('share-server-manage ' '%(host)s ' '%(share_network_id)s ' '%(identifier)s ' '%(driver_args)s ' % { 'host': 'fake_host', 'share_network_id': fake_share_network.id, 'identifier': '88-as-23-f3-45', 'driver_args': driver_args, }) command += '--share-network-subnet %s' % 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) @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('share-server-reset-state 1234 --state %s ' % status) expected = {'reset_status': {'status': status}} self.assert_called('POST', '/share-servers/1234/action', body=expected) def test_unmanage(self): self.run_command('unmanage 1234') self.assert_called('POST', '/shares/1234/action') 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}}) @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()) def test_snapshot_manage(self, cmd_args, valid_params): shell_v2._find_share.return_value = 'fake_share' self.run_command('snapshot-manage fake_share fake_provider_location ' + cmd_args) expected = { 'snapshot': { 'share_id': 'fake_share', 'provider_location': 'fake_provider_location', 'name': None, 'description': None, } } expected['snapshot'].update(valid_params) self.assert_called('POST', '/snapshots/manage', body=expected) def test_snapshot_unmanage(self): self.run_command('snapshot-unmanage 1234') self.assert_called('POST', '/snapshots/1234/action', body={'unmanage': None}) 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'}}) 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('delete 1234 %s' % 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' ) 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 + u'ффф') 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 + u'ффф') 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', ) @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 = ('type-create test True --%(key)s %(value)s --extra-specs ' '%(key)s=%(value)s' % {'key': key, 'value': 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, 'type-create test false --%s fake' % value, ) @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, 'type-create test false --extra-specs %s=fake' % 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_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('update 1234 %(alias)s %(value)s' % { 'alias': alias, 'value': 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, 'update 1234 %(alias)s %(value)s' % { 'alias': alias, 'value': invalid_val}) # update all attributes self.run_command('update 1234 --name new-name ' '--description=new-description ' '%s True' % alias) 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(object): 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]) def test_extend(self): self.run_command('extend 1234 77') expected = {'extend': {'new_size': 77}} self.assert_called('POST', '/shares/1234/action', body=expected) 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) def test_shrink(self): self.run_command('shrink 1234 77') expected = {'shrink': {'new_size': 77}} self.assert_called('POST', '/shares/1234/action', body=expected) 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('share-network-list %(command)s %(ss_id)s' % {'command': command, 'ss_id': ss.id}) self.assert_called( 'GET', '/share-networks/detail?security_service_id=%s' % 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('share-network-list %s 1234' % command) 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('share-network-list %s 2001-01-01' % command) 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('share-network-list %s 2001-01-01' % command) 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('share-network-list %s fake-id' % command) 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('share-network-list %s fake-id' % command) 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('share-network-list %s local' % command) 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('share-network-list %s 1234' % command) 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('share-network-list %s 4' % command) 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 += ' --%(key)s=%(value)s' % {'key': key, 'value': value} self.run_command(command_str) query = utils.safe_urlencode(sorted([(k.replace('-', '_'), v) for (k, v) in filters.items()])) self.assert_called( 'GET', '/share-networks/detail?%s' % 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 + u'ффф') 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 + u'ффф') 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)s %(subnet_ids)s' % { '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', '/share-networks/1234/subnets/%s' % subnet.id, clear_callstack=False) def test_share_network_subnet_delete_invalid_share_network(self): command = 'share-network-subnet-delete %(net_id)s %(subnet_id)s' % { '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)s %(subnet_id)s' % { '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)s %(subnet_id)s' % args) self.assert_called( 'GET', '/share-networks/%(share_net_id)s/subnets/%(subnet_id)s' % 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)s %(subnet_id)s' % { '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 self.run_command("create nfs 1") self.assert_called("POST", "/shares", body=self.create_share_body) def test_create_public_share(self): expected = self.create_share_body.copy() expected['share']['is_public'] = True self.run_command("create --public nfs 1") 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("create nfs 1 --share-network %s" % sn) expected = self.create_share_body.copy() expected['share']['share_network_id'] = sn 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") expected = self.create_share_body.copy() expected['share']['metadata'] = {"key1": "value1", "key2": "value2"} self.assert_called("POST", "/shares", body=expected) 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, ("access-allow 1234 cert %s" % 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'}) 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(object): id = 'fake-sn-id' sn = FakeShareNetwork() shell_v2._find_share_network.return_value = sn for command in ['--share-network', '--share_network']: self.run_command('security-service-list %(command)s %(sn_id)s' % {'command': command, 'sn_id': sn.id}) self.assert_called( 'GET', '/security-services?share_network_id=%s' % 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 += ' --%(key)s=%(value)s' % {'key': key, 'value': 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( 'quota-show --tenant 1234 --share_type %s' % share_type_id) self.assert_called( 'GET', '/quota-sets/1234?share_type=%s' % 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}), ) @ddt.unpack def test_quota_update(self, cmd, expected_body): self.run_command('quota-update 1234 %s' % 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&' 'pool=pool1&share_type=%s' % 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 + u'ффф') 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 + u'ффф') self.assert_called( 'GET', '/share-groups/detail?description~=%D1%84%D1%84%D1%84') def test_share_group_show(self): self.run_command('share-group-show 1234') self.assert_called('GET', '/share-groups/1234') def test_share_group_create(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_network = type( 'FakeShareNetwork', (object,), {'id': '3456'}) self.mock_object( shell_v2, '_find_share_network', mock.Mock(return_value=fake_share_network)) self.run_command( 'share-group-create --name fake_sg ' '--description my_group --share-types 1234,5678 ' '--share-group-type fake_sg_type ' '--share-network fake_share_network ' '--availability-zone fake_az') 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) @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): self.run_command('share-group-update 1234 %s' % cmd) 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') @mock.patch.object(shell_v2, '_find_share_group', mock.Mock()) def test_share_group_delete(self): fake_group = type('FakeShareGroup', (object,), {'id': '1234'}) shell_v2._find_share_group.return_value = fake_group self.run_command('share-group-delete fake-sg') self.assert_called('DELETE', '/share-groups/1234') @mock.patch.object(shell_v2, '_find_share_group', mock.Mock()) def test_share_group_delete_force(self): fake_group = type('FakeShareGroup', (object,), {'id': '1234'}) shell_v2._find_share_group.return_value = fake_group self.run_command('share-group-delete --force fake-group') self.assert_called( 'POST', '/share-groups/1234/action', {'force_delete': None}) @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()) 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') @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('share-group-snapshot-update 1234 %s' % 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()) 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') @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 ' 'type1,type2 --is-public %s --group-specs ' 'spec1=value1' % six.text_type(public)) 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}) def test_quota_class_update(self, data): cmd = 'quota-class-update test' expected = dict() for k, v in data.items(): cmd += ' %(arg)s %(val)s' % {'arg': k, 'val': 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()) 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') @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(object): 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 = 'fake-replica-%d' % 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 %s' % ' '.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()) 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') def test_share_replica_show(self): self.run_command('share-replica-show 5678') self.assert_called_anytime('GET', '/share-replicas/5678') @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 + ' --columns=%s' % 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', six.text_type(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 %s' % ' '.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', '/security-services/%s' % 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 %s' % ' '.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', '/share-networks/%s' % sn.id, clear_callstack=False) @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 %s' % ' '.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', '/snapshots/%s' % snapshot.id, clear_callstack=False) @ddt.data(('1234', ), ('1234', '5678')) def test_snapshot_force_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-force-delete %s' % ' '.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( 'POST', '/snapshots/%s/action' % snapshot.id, {'force_delete': None}, clear_callstack=False) @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 %s' % ' '.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', '/types/%s' % fake_share_type.id, clear_callstack=False) @ddt.data(('1234', ), ('1234', '5678')) def test_share_server_delete(self, server_ids): fake_share_servers = [ share_servers.ShareServer('fake', {'id': server_id}, True) for server_id in server_ids ] self.mock_object( shell_v2, '_find_share_server', mock.Mock(side_effect=fake_share_servers)) self.run_command('share-server-delete %s' % ' '.join(server_ids)) shell_v2._find_share_server.assert_has_calls([ mock.call(self.shell.cs, s_id) for s_id in server_ids ]) for server in fake_share_servers: self.assert_called_anytime( 'DELETE', '/share-servers/%s' % server.id, clear_callstack=False) @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 %s' % ' '.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', '/messages/%s' % 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) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_scheduler_stats.py0000664000175000017500000000560413644133413027437 0ustar zuulzuul00000000000000# 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. import mock from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import scheduler_stats class PoolTest(utils.TestCase): def setUp(self): super(PoolTest, self).setUp() self.name = 'fake_host@fake_backend#fake_pool' info = { 'name': self.name, 'host': 'fake_host', 'backend': 'fake_backend', 'pool': 'fake_pool', } self.resource_class = scheduler_stats.Pool(manager=self, info=info) def test_get_repr_of_share_server(self): self.assertEqual('' % self.name, repr(self.resource_class)) class PoolManagerTest(utils.TestCase): def setUp(self): super(PoolManagerTest, self).setUp() self.manager = scheduler_stats.PoolManager(fakes.FakeClient()) @mock.patch.object(scheduler_stats.PoolManager, '_list', mock.Mock()) def test_list(self): self.manager.list(detailed=False) self.manager._list.assert_called_once_with( scheduler_stats.RESOURCES_PATH, scheduler_stats.RESOURCES_NAME) @mock.patch.object(scheduler_stats.PoolManager, '_list', mock.Mock()) def test_list_detail(self): self.manager.list() self.manager._list.assert_called_once_with( scheduler_stats.RESOURCES_PATH + '/detail', scheduler_stats.RESOURCES_NAME) @mock.patch.object(scheduler_stats.PoolManager, '_list', mock.Mock()) def test_list_with_one_search_opt(self): host = 'fake_host' query_string = "?host=%s" % host self.manager.list(detailed=False, search_opts={'host': host}) self.manager._list.assert_called_once_with( scheduler_stats.RESOURCES_PATH + query_string, scheduler_stats.RESOURCES_NAME) @mock.patch.object(scheduler_stats.PoolManager, '_list', mock.Mock()) def test_list_detail_with_two_search_opts(self): host = 'fake_host' backend = 'fake_backend' query_string = "?backend=%s&host=%s" % (backend, host) self.manager.list(search_opts={'host': host, 'backend': backend}) self.manager._list.assert_called_once_with( scheduler_stats.RESOURCES_PATH + '/detail' + query_string, scheduler_stats.RESOURCES_NAME) ././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_snapshot_instance_export_locations.pypython-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_snapshot_instance_export_locations.p0000664000175000017500000000344713644133413034436 0ustar zuulzuul00000000000000# 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 manilaclient import extension from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import share_snapshot_instance_export_locations extensions = [ extension.Extension('share_snapshot_export_locations', share_snapshot_instance_export_locations), ] cs = fakes.FakeClient(extensions=extensions) class ShareSnapshotInstanceExportLocationsTest(utils.TestCase): def test_list_snapshot_instance(self): snapshot_instance_id = '1234' cs.share_snapshot_instance_export_locations.list( snapshot_instance_id, search_opts=None) cs.assert_called( 'GET', '/snapshot-instances/%s/export-locations' % snapshot_instance_id) def test_get_snapshot_instance(self): snapshot_instance_id = '1234' el_id = 'fake_el_id' cs.share_snapshot_instance_export_locations.get( el_id, snapshot_instance_id) cs.assert_called( 'GET', ('/snapshot-instances/%(snapshot_id)s/export-locations/' '%(el_id)s') % { 'snapshot_id': snapshot_instance_id, 'el_id': el_id}) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_type_access.py0000664000175000017500000000677413644133413026556 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # Copyright (c) 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 mock from manilaclient import api_versions from manilaclient.tests.unit import utils from manilaclient.v2 import share_type_access PROJECT_UUID = '11111111-1111-1111-111111111111' @ddt.ddt class TypeAccessTest(utils.TestCase): def _get_share_type_access_manager(self, microversion): version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) return share_type_access.ShareTypeAccessManager( api=mock_microversion) @ddt.data( ("1.0", "os-share-type-access"), ("2.0", "os-share-type-access"), ("2.6", "os-share-type-access"), ("2.7", "share_type_access"), ) @ddt.unpack def test_list(self, microversion, action_name): fake_access_list = ['foo', 'bar'] share_type = mock.Mock() share_type.uuid = '3' share_type.is_public = False manager = self._get_share_type_access_manager(microversion) with mock.patch.object(manager, '_list', mock.Mock(return_value=fake_access_list)): access = manager.list(share_type=share_type, search_opts=None) manager._list.assert_called_once_with( "/types/3/%s" % action_name, "share_type_access") self.assertEqual(fake_access_list, access) @ddt.data("1.0", "2.0", "2.6", "2.7") def test_list_public(self, microversion): share_type = mock.Mock() share_type.uuid = '4' share_type.is_public = True manager = self._get_share_type_access_manager(microversion) with mock.patch.object(manager, '_list', mock.Mock(return_value='fake')): access = manager.list(share_type=share_type) self.assertFalse(manager._list.called) self.assertIsNone(access) @ddt.data("1.0", "2.0", "2.6", "2.7") def test_add_project_access(self, microversion): share_type = mock.Mock() manager = self._get_share_type_access_manager(microversion) with mock.patch.object(manager, '_action', mock.Mock(return_value='fake_action')): manager.add_project_access(share_type, PROJECT_UUID) manager._action.assert_called_once_with( 'addProjectAccess', share_type, {'project': PROJECT_UUID}) @ddt.data("1.0", "2.0", "2.6", "2.7") def test_remove_project_access(self, microversion): share_type = mock.Mock() manager = self._get_share_type_access_manager(microversion) with mock.patch.object(manager, '_action', mock.Mock(return_value='fake_action')): manager.remove_project_access(share_type, PROJECT_UUID) manager._action.assert_called_once_with( 'removeProjectAccess', share_type, {'project': PROJECT_UUID}) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_group_types.py0000664000175000017500000001736213644133413030011 0ustar zuulzuul00000000000000# Copyright 2016 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. import mock import ddt import six import manilaclient from manilaclient import exceptions from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes as fake from manilaclient.v2 import share_group_types as types @ddt.ddt class ShareGroupTypeTest(utils.TestCase): def setUp(self): super(ShareGroupTypeTest, self).setUp() self.manager = types.ShareGroupTypeManager(fake.FakeClient()) self.fake_group_specs = {'key1': 'value1', 'key2': 'value2'} self.fake_share_group_type_info = { 'id': fake.ShareGroupType.id, 'share_types': [fake.ShareType.id], 'name': fake.ShareGroupType.name, 'is_public': fake.ShareGroupType.is_public, 'group_specs': self.fake_group_specs, } self.share_group_type = types.ShareGroupType( self.manager, self.fake_share_group_type_info, loaded=True) def test_repr(self): result = six.text_type(self.share_group_type) self.assertEqual( '' % fake.ShareGroupType.name, result) @ddt.data((True, True), (False, False), (None, 'N/A')) @ddt.unpack def test_is_public(self, is_public, expected): fake_share_group_type_info = {'name': 'fake_name'} if is_public is not None: fake_share_group_type_info['is_public'] = is_public share_group_type = types.ShareGroupType( self.manager, fake_share_group_type_info, loaded=True) result = share_group_type.is_public self.assertEqual(expected, result) def test_get_keys(self): self.mock_object(self.manager.api.client, 'get') result = self.share_group_type.get_keys() self.assertEqual(self.fake_group_specs, result) self.assertFalse(self.manager.api.client.get.called) def test_get_keys_force_api_call(self): share_group_type = types.ShareGroupType( self.manager, self.fake_share_group_type_info, loaded=True) share_group_type._group_specs = {} self.manager.api.client.get = mock.Mock(return_value=( None, self.fake_share_group_type_info)) result = share_group_type.get_keys(prefer_resource_data=False) self.assertEqual(self.fake_group_specs, result) self.manager.api.client.get.assert_called_once_with( types.GROUP_SPECS_RESOURCES_PATH % fake.ShareGroupType.id) def test_set_keys(self): mock_manager_create = self.mock_object( self.manager, '_create', mock.Mock(return_value=self.fake_group_specs)) result = self.share_group_type.set_keys(self.fake_group_specs) self.assertEqual(result, self.fake_group_specs) expected_body = {'group_specs': self.fake_group_specs} mock_manager_create.assert_called_once_with( types.GROUP_SPECS_RESOURCES_PATH % fake.ShareGroupType.id, expected_body, types.GROUP_SPECS_RESOURCES_NAME, return_raw=True) def test_unset_keys(self): mock_manager_delete = self.mock_object( self.manager, '_delete', mock.Mock(return_value=None)) result = self.share_group_type.unset_keys(self.fake_group_specs.keys()) self.assertIsNone(result) mock_manager_delete.assert_has_calls([ mock.call(types.GROUP_SPECS_RESOURCE_PATH % (fake.ShareGroupType.id, 'key1')), mock.call(types.GROUP_SPECS_RESOURCE_PATH % (fake.ShareGroupType.id, 'key2')), ], any_order=True) def test_unset_keys_error(self): mock_manager_delete = self.mock_object( self.manager, '_delete', mock.Mock(return_value='error')) result = self.share_group_type.unset_keys( sorted(self.fake_group_specs.keys())) self.assertEqual('error', result) mock_manager_delete.assert_called_once_with( types.GROUP_SPECS_RESOURCE_PATH % (fake.ShareGroupType.id, 'key1')) @ddt.ddt class ShareGroupTypeManagerTest(utils.TestCase): def setUp(self): super(ShareGroupTypeManagerTest, self).setUp() self.manager = types.ShareGroupTypeManager(fake.FakeClient()) self.fake_group_specs = {'key1': 'value1', 'key2': 'value2'} def test_create(self): fake_share_group_type = fake.ShareGroupType() mock_create = self.mock_object( self.manager, '_create', mock.Mock(return_value=fake_share_group_type)) create_args = { 'name': fake.ShareGroupType.name, 'share_types': [fake.ShareType()], 'is_public': False, 'group_specs': self.fake_group_specs, } result = self.manager.create(**create_args) self.assertIs(fake_share_group_type, result) expected_body = { types.RESOURCE_NAME: { 'name': fake.ShareGroupType.name, 'share_types': [fake.ShareType().id], 'is_public': False, 'group_specs': self.fake_group_specs, }, } mock_create.assert_called_once_with( types.RESOURCES_PATH, expected_body, types.RESOURCE_NAME) def test_create_no_share_type(self): create_args = { 'name': fake.ShareGroupType.name, 'share_types': [], 'is_public': False, 'group_specs': self.fake_group_specs, } self.assertRaises(ValueError, self.manager.create, **create_args) def test_create_using_unsupported_microversion(self): self.manager.api.api_version = manilaclient.API_MIN_VERSION self.assertRaises(exceptions.UnsupportedVersion, self.manager.create) def test_get(self): fake_share_group_type = fake.ShareGroupType() mock_get = self.mock_object( self.manager, '_get', mock.Mock(return_value=fake_share_group_type)) result = self.manager.get(fake.ShareGroupType.id) self.assertIs(fake_share_group_type, result) mock_get.assert_called_once_with( types.RESOURCE_PATH % fake.ShareGroupType.id, types.RESOURCE_NAME) def test_list(self): fake_share_group_type = fake.ShareGroupType() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_share_group_type])) result = self.manager.list(search_opts=None) self.assertEqual([fake_share_group_type], result) mock_list.assert_called_once_with( types.RESOURCES_PATH + '?is_public=all', types.RESOURCES_NAME) def test_list_no_public(self): fake_share_group_type = fake.ShareGroupType() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_share_group_type])) result = self.manager.list(show_all=False) self.assertEqual([fake_share_group_type], result) mock_list.assert_called_once_with( types.RESOURCES_PATH, types.RESOURCES_NAME) def test_delete(self): mock_delete = self.mock_object(self.manager, '_delete') self.manager.delete(fake.ShareGroupType()) mock_delete.assert_called_once_with( types.RESOURCE_PATH % fake.ShareGroupType.id) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_snapshot_export_locations.py0000664000175000017500000000316513644133413032740 0ustar zuulzuul00000000000000# 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 manilaclient import extension from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import share_snapshot_export_locations extensions = [ extension.Extension('share_snapshot_export_locations', share_snapshot_export_locations), ] cs = fakes.FakeClient(extensions=extensions) class ShareSnapshotExportLocationsTest(utils.TestCase): def test_list_snapshot(self): snapshot_id = '1234' cs.share_snapshot_export_locations.list(snapshot_id, search_opts=None) cs.assert_called( 'GET', '/snapshots/%s/export-locations' % snapshot_id) def test_get_snapshot(self): snapshot_id = '1234' el_id = 'fake_el_id' cs.share_snapshot_export_locations.get(el_id, snapshot_id) cs.assert_called( 'GET', ('/snapshots/%(snapshot_id)s/export-locations/' '%(el_id)s') % { 'snapshot_id': snapshot_id, 'el_id': el_id}) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_quotas.py0000664000175000017500000002656613644133413025571 0ustar zuulzuul00000000000000# 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. import ddt import mock from manilaclient import api_versions from manilaclient.tests.unit import utils from manilaclient.v2 import quotas REPLICA_QUOTAS_MICROVERSION = '2.53' @ddt.ddt class QuotaSetsTest(utils.TestCase): def _get_manager(self, microversion): version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) return quotas.QuotaSetManager(api=mock_microversion) def _get_resource_path(self, microversion): if (api_versions.APIVersion(microversion) > api_versions.APIVersion("2.6")): return quotas.RESOURCE_PATH return quotas.RESOURCE_PATH_LEGACY @ddt.data("2.6", "2.7", "2.25", "2.38", "2.39") def test_tenant_quotas_get(self, microversion): tenant_id = 'test' manager = self._get_manager(microversion) resource_path = self._get_resource_path(microversion) version = api_versions.APIVersion(microversion) if version >= api_versions.APIVersion('2.25'): expected_url = "%s/test/detail" % resource_path else: expected_url = ("%s/test" % resource_path) with mock.patch.object(manager, '_get', mock.Mock(return_value='fake_get')): manager.get(tenant_id, detail=True) manager._get.assert_called_once_with(expected_url, "quota_set") @ddt.data("2.6", "2.7", "2.25", "2.38", "2.39") def test_user_quotas_get(self, microversion): tenant_id = 'test' user_id = 'fake_user' manager = self._get_manager(microversion) resource_path = self._get_resource_path(microversion) version = api_versions.APIVersion(microversion) if version >= api_versions.APIVersion('2.25'): expected_url = ("%s/test/detail?user_id=fake_user" % resource_path) else: expected_url = ("%s/test?user_id=fake_user" % resource_path) with mock.patch.object(manager, '_get', mock.Mock(return_value='fake_get')): manager.get(tenant_id, user_id=user_id, detail=True) manager._get.assert_called_once_with(expected_url, "quota_set") def test_share_type_quotas_get(self): tenant_id = 'fake_tenant_id' share_type = 'fake_share_type' manager = self._get_manager('2.39') resource_path = self._get_resource_path('2.39') expected_url = ("%s/%s/detail?share_type=%s" % (resource_path, tenant_id, share_type)) with mock.patch.object(manager, '_get', mock.Mock(return_value='fake_get')): manager.get(tenant_id, share_type=share_type, detail=True) manager._get.assert_called_once_with(expected_url, "quota_set") @ddt.data("2.6", "2.7", "2.38", "2.39") def test_tenant_quotas_defaults(self, microversion): tenant_id = 'test' manager = self._get_manager(microversion) resource_path = self._get_resource_path(microversion) expected_url = "%s/test/defaults" % resource_path with mock.patch.object(manager, '_get', mock.Mock(return_value='fake_get')): manager.defaults(tenant_id) manager._get.assert_called_once_with(expected_url, "quota_set") @ddt.data( ("2.6", {}), ("2.6", {"force": True}), ("2.7", {}), ("2.7", {"force": True}), ("2.38", {}), ("2.38", {"force": True}), ("2.39", {}), ("2.39", {"force": True}), ("2.53", {}), ("2.53", {"force": True, "share_replicas": 8, "replica_gigabytes": 9}), ) @ddt.unpack def test_update_quota(self, microversion, extra_data): tenant_id = 'test' manager = self._get_manager(microversion) resource_path = self._get_resource_path(microversion) expected_url = "%s/test" % resource_path expected_body = { 'quota_set': { 'tenant_id': tenant_id, 'shares': 1, 'snapshots': 2, 'gigabytes': 3, 'snapshot_gigabytes': 4, 'share_networks': 5, }, } expected_body['quota_set'].update(extra_data) with mock.patch.object(manager, '_update', mock.Mock(return_value='fake_update')): manager.update( tenant_id, shares=1, snapshots=2, gigabytes=3, snapshot_gigabytes=4, share_networks=5, **extra_data) manager._update.assert_called_once_with( expected_url, expected_body, "quota_set") @ddt.data("2.6", "2.7", "2.38", "2.39", "2.40", REPLICA_QUOTAS_MICROVERSION) def test_update_user_quota(self, microversion): tenant_id = 'test' user_id = 'fake_user' manager = self._get_manager(microversion) resource_path = self._get_resource_path(microversion) expected_url = "%s/test?user_id=fake_user" % resource_path expected_body = { 'quota_set': { 'tenant_id': tenant_id, 'shares': 1, 'snapshots': 2, 'gigabytes': 3, 'snapshot_gigabytes': 4, 'share_networks': 5, }, } kwargs = { 'shares': expected_body['quota_set']['shares'], 'snapshots': expected_body['quota_set']['snapshots'], 'gigabytes': expected_body['quota_set']['gigabytes'], 'snapshot_gigabytes': expected_body['quota_set'][ 'snapshot_gigabytes'], 'share_networks': expected_body['quota_set']['share_networks'], 'user_id': user_id, } if (api_versions.APIVersion(microversion) >= api_versions.APIVersion('2.40')): expected_body['quota_set']['share_groups'] = 6 expected_body['quota_set']['share_group_snapshots'] = 7 kwargs['share_groups'] = expected_body['quota_set'][ 'share_groups'] kwargs['share_group_snapshots'] = expected_body['quota_set'][ 'share_group_snapshots'] if (api_versions.APIVersion(microversion) >= api_versions.APIVersion(REPLICA_QUOTAS_MICROVERSION)): expected_body['quota_set']['share_replicas'] = 8 expected_body['quota_set']['replica_gigabytes'] = 9 kwargs['share_replicas'] = expected_body['quota_set'][ 'share_replicas'] kwargs['replica_gigabytes'] = expected_body['quota_set'][ 'replica_gigabytes'] with mock.patch.object(manager, '_update', mock.Mock(return_value='fake_update')): manager.update(tenant_id, **kwargs) manager._update.assert_called_once_with( expected_url, expected_body, "quota_set") @ddt.data('2.39', REPLICA_QUOTAS_MICROVERSION) def test_update_share_type_quota(self, microversion): tenant_id = 'fake_tenant_id' share_type = 'fake_share_type' manager = self._get_manager(microversion) resource_path = self._get_resource_path(microversion) expected_url = "%s/%s?share_type=%s" % ( resource_path, tenant_id, share_type) expected_body = { 'quota_set': { 'tenant_id': tenant_id, 'shares': 1, 'snapshots': 2, 'gigabytes': 3, 'snapshot_gigabytes': 4, }, } kwargs = {} if microversion >= REPLICA_QUOTAS_MICROVERSION: expected_body['quota_set']['share_replicas'] = 8 expected_body['quota_set']['replica_gigabytes'] = 9 kwargs = {'share_replicas': 8, 'replica_gigabytes': 9} with mock.patch.object(manager, '_update', mock.Mock(return_value='fake_update')): manager.update( tenant_id, shares=1, snapshots=2, gigabytes=3, snapshot_gigabytes=4, share_type=share_type, **kwargs) manager._update.assert_called_once_with( expected_url, expected_body, "quota_set") def test_update_share_type_quotas_for_share_networks(self): manager = self._get_manager("2.39") with mock.patch.object(manager, '_update', mock.Mock(return_value='fake_delete')): self.assertRaises( ValueError, manager.update, 'fake_tenant_id', share_type='fake_share_type', share_networks=13, ) manager._update.assert_not_called() @ddt.data("2.6", "2.7", "2.38", "2.39") def test_quotas_delete(self, microversion): tenant_id = 'test' manager = self._get_manager(microversion) resource_path = self._get_resource_path(microversion) expected_url = "%s/test" % resource_path with mock.patch.object(manager, '_delete', mock.Mock(return_value='fake_delete')): manager.delete(tenant_id) manager._delete.assert_called_once_with(expected_url) @ddt.data("2.6", "2.7", "2.38", "2.39") def test_user_quotas_delete(self, microversion): tenant_id = 'test' user_id = 'fake_user' manager = self._get_manager(microversion) resource_path = self._get_resource_path(microversion) expected_url = "%s/test?user_id=fake_user" % resource_path with mock.patch.object(manager, '_delete', mock.Mock(return_value='fake_delete')): manager.delete(tenant_id, user_id=user_id) manager._delete.assert_called_once_with(expected_url) def test_share_type_quotas_delete(self): tenant_id = 'test' share_type = 'fake_st' manager = self._get_manager("2.39") resource_path = self._get_resource_path("2.39") expected_url = "%s/test?share_type=fake_st" % resource_path with mock.patch.object(manager, '_delete', mock.Mock(return_value='fake_delete')): manager.delete(tenant_id, share_type=share_type) manager._delete.assert_called_once_with(expected_url) @ddt.data('get', 'update', 'delete') def test_share_type_quotas_using_old_microversion(self, operation): manager = self._get_manager("2.38") with mock.patch.object(manager, '_%s' % operation, mock.Mock(return_value='fake_delete')): self.assertRaises( TypeError, getattr(manager, operation), 'fake_tenant_id', share_type='fake_share_type', ) getattr(manager, '_%s' % operation).assert_not_called() python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_export_locations.py0000664000175000017500000000361213644133413031016 0ustar zuulzuul00000000000000# 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 mock from manilaclient import api_versions from manilaclient import extension from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import share_export_locations extensions = [ extension.Extension('share_export_locations', share_export_locations), ] cs = fakes.FakeClient(extensions=extensions) @ddt.ddt class ShareExportLocationsTest(utils.TestCase): def _get_manager(self, microversion): version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) return ( share_export_locations.ShareExportLocationManager( api=mock_microversion) ) def test_list_of_export_locations(self): share_id = '1234' cs.share_export_locations.list(share_id, search_opts=None) cs.assert_called( 'GET', '/shares/%s/export_locations' % share_id) def test_get_single_export_location(self): share_id = '1234' el_uuid = 'fake_el_uuid' cs.share_export_locations.get(share_id, el_uuid) cs.assert_called( 'GET', ('/shares/%(share_id)s/export_locations/' '%(el_uuid)s') % { 'share_id': share_id, 'el_uuid': el_uuid}) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_groups.py0000664000175000017500000002655013644133413026747 0ustar zuulzuul00000000000000# Copyright 2016 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. import mock import ddt import six import manilaclient from manilaclient import exceptions from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes as fake from manilaclient.v2 import share_groups @ddt.ddt class ShareGroupTest(utils.TestCase): def setUp(self): super(ShareGroupTest, self).setUp() self.manager = share_groups.ShareGroupManager(fake.FakeClient()) self.share_group = share_groups.ShareGroup( self.manager, {'id': 'fake_id'}) self.fake_kwargs = {'key': 'value'} def test_repr(self): result = six.text_type(self.share_group) self.assertEqual('', result) def test_update(self): mock_manager_update = self.mock_object(self.manager, 'update') self.share_group.update(**self.fake_kwargs) mock_manager_update.assert_called_once_with( self.share_group, **self.fake_kwargs) def test_delete(self): mock_manager_delete = self.mock_object(self.manager, 'delete') self.share_group.delete() mock_manager_delete.assert_called_once_with( self.share_group, force=False) @ddt.data(True, False) def test_delete_force(self, force): mock_manager_delete = self.mock_object(self.manager, 'delete') self.share_group.delete(force=force) mock_manager_delete.assert_called_once_with( self.share_group, force=force) def test_reset_state(self): mock_manager_reset_state = self.mock_object( self.manager, 'reset_state') self.share_group.reset_state('fake_state') mock_manager_reset_state.assert_called_once_with( self.share_group, 'fake_state') @ddt.ddt class ShareGroupManagerTest(utils.TestCase): def setUp(self): super(ShareGroupManagerTest, self).setUp() self.manager = share_groups.ShareGroupManager(fake.FakeClient()) def test_create(self): fake_share_group = fake.ShareGroup() mock_create = self.mock_object( self.manager, '_create', mock.Mock(return_value=fake_share_group)) create_args = { 'name': fake.ShareGroup.name, 'description': fake.ShareGroup.description, 'availability_zone': fake.ShareGroup.availability_zone, 'share_group_type': fake.ShareGroupType(), 'share_types': [fake.ShareType()], 'share_network': fake.ShareNetwork(), } result = self.manager.create(**create_args) self.assertIs(fake_share_group, result) expected_body = { share_groups.RESOURCE_NAME: { 'name': fake.ShareGroup.name, 'description': fake.ShareGroup.description, 'share_group_type_id': fake.ShareGroupType().id, 'share_network_id': fake.ShareNetwork().id, 'share_types': [fake.ShareType().id], 'availability_zone': fake.ShareGroup.availability_zone, }, } mock_create.assert_called_once_with( share_groups.RESOURCES_PATH, expected_body, share_groups.RESOURCE_NAME) def test_create_default_type(self): fake_share_group = fake.ShareGroup() mock_create = self.mock_object( self.manager, '_create', mock.Mock(return_value=fake_share_group)) create_args = { 'name': fake.ShareGroup.name, 'description': fake.ShareGroup.description, 'availability_zone': fake.ShareGroup.availability_zone, } result = self.manager.create(**create_args) self.assertIs(fake_share_group, result) expected_body = {share_groups.RESOURCE_NAME: create_args} mock_create.assert_called_once_with( share_groups.RESOURCES_PATH, expected_body, share_groups.RESOURCE_NAME) def test_create_from_snapshot(self): fake_share_group = fake.ShareGroup() mock_create = self.mock_object( self.manager, '_create', mock.Mock(return_value=fake_share_group)) create_args = { 'name': fake.ShareGroup.name, 'description': fake.ShareGroup.description, 'availability_zone': fake.ShareGroup.availability_zone, 'source_share_group_snapshot': fake.ShareGroupSnapshot(), } result = self.manager.create(**create_args) self.assertIs(fake_share_group, result) expected_body = { share_groups.RESOURCE_NAME: { 'name': fake.ShareGroup.name, 'description': fake.ShareGroup.description, 'availability_zone': fake.ShareGroup.availability_zone, 'source_share_group_snapshot_id': fake.ShareGroupSnapshot().id, }, } mock_create.assert_called_once_with( share_groups.RESOURCES_PATH, expected_body, share_groups.RESOURCE_NAME) def test_create_using_unsupported_microversion(self): self.manager.api.api_version = manilaclient.API_MIN_VERSION self.assertRaises(exceptions.UnsupportedVersion, self.manager.create) def test_create_invalid_arguments(self): create_args = { 'name': fake.ShareGroup.name, 'description': fake.ShareGroup.description, 'share_types': [fake.ShareType().id], 'source_share_group_snapshot': fake.ShareGroupSnapshot(), } self.assertRaises(ValueError, self.manager.create, **create_args) def test_get(self): fake_share_group = fake.ShareGroup() mock_get = self.mock_object( self.manager, '_get', mock.Mock(return_value=fake_share_group)) result = self.manager.get(fake.ShareGroup.id) self.assertIs(fake_share_group, result) mock_get.assert_called_once_with( share_groups.RESOURCE_PATH % fake.ShareGroup.id, share_groups.RESOURCE_NAME) def test_list(self): fake_share_group = fake.ShareGroup() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_share_group])) result = self.manager.list() self.assertEqual([fake_share_group], result) mock_list.assert_called_once_with( share_groups.RESOURCES_PATH + '/detail', share_groups.RESOURCES_NAME) def test_list_no_detail(self): fake_share_group = fake.ShareGroup() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_share_group])) result = self.manager.list(detailed=False) self.assertEqual([fake_share_group], result) mock_list.assert_called_once_with( share_groups.RESOURCES_PATH, share_groups.RESOURCES_NAME) def test_list_with_filters(self): fake_share_group = fake.ShareGroup() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_share_group])) filters = {'all_tenants': 1} result = self.manager.list(detailed=False, search_opts=filters) self.assertEqual([fake_share_group], result) expected_path = (share_groups.RESOURCES_PATH + '?all_tenants=1') mock_list.assert_called_once_with( expected_path, share_groups.RESOURCES_NAME) @ddt.data( ('name', 'name'), ('share_group_type', 'share_group_type_id'), ('share_network', 'share_network_id'), ) @ddt.unpack def test_list_with_sorting(self, key, expected_key): fake_share_group = fake.ShareGroup() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_share_group])) result = self.manager.list( detailed=False, sort_dir='asc', sort_key=key) self.assertEqual([fake_share_group], result) expected_path = ( share_groups.RESOURCES_PATH + '?sort_dir=asc&sort_key=' + expected_key) mock_list.assert_called_once_with( expected_path, share_groups.RESOURCES_NAME) @ddt.data( ('name', 'invalid'), ('invalid', 'asc'), ) @ddt.unpack def test_list_with_invalid_sorting(self, sort_key, sort_dir): self.assertRaises( ValueError, self.manager.list, sort_dir=sort_dir, sort_key=sort_key) def test_update(self): fake_share_group = fake.ShareGroup() mock_get = self.mock_object( self.manager, '_get', mock.Mock(return_value=fake_share_group)) mock_update = self.mock_object( self.manager, '_update', mock.Mock(return_value=fake_share_group)) update_args = { 'name': fake.ShareGroup.name, 'description': fake.ShareGroup.description, } result = self.manager.update(fake.ShareGroup(), **update_args) self.assertIs(fake_share_group, result) self.assertFalse(mock_get.called) mock_update.assert_called_once_with( share_groups.RESOURCE_PATH % fake.ShareGroup.id, {share_groups.RESOURCE_NAME: update_args}, share_groups.RESOURCE_NAME) def test_update_no_data(self): fake_share_group = fake.ShareGroup() mock_get = self.mock_object( self.manager, '_get', mock.Mock(return_value=fake_share_group)) mock_update = self.mock_object( self.manager, '_update', mock.Mock(return_value=fake_share_group)) update_args = {} result = self.manager.update(fake.ShareGroup(), **update_args) self.assertIs(fake_share_group, result) mock_get.assert_called_once_with( share_groups.RESOURCE_PATH % fake.ShareGroup.id, share_groups.RESOURCE_NAME) self.assertFalse(mock_update.called) def test_delete(self): mock_delete = self.mock_object(self.manager, '_delete') mock_post = self.mock_object(self.manager.api.client, 'post') self.manager.delete(fake.ShareGroup()) mock_delete.assert_called_once_with( share_groups.RESOURCE_PATH % fake.ShareGroup.id) self.assertFalse(mock_post.called) def test_delete_force(self): mock_delete = self.mock_object(self.manager, '_delete') mock_post = self.mock_object(self.manager.api.client, 'post') self.manager.delete(fake.ShareGroup.id, force=True) self.assertFalse(mock_delete.called) mock_post.assert_called_once_with( share_groups.RESOURCE_PATH_ACTION % fake.ShareGroup.id, body={'force_delete': None}) def test_reset_state(self): mock_post = self.mock_object(self.manager.api.client, 'post') self.manager.reset_state(fake.ShareGroup(), 'fake_state') mock_post.assert_called_once_with( share_groups.RESOURCE_PATH_ACTION % fake.ShareGroup.id, body={'reset_status': {'status': 'fake_state'}}) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_availability_zones.py0000664000175000017500000000410213644133413030123 0ustar zuulzuul00000000000000# 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 import mock from manilaclient import api_versions from manilaclient.tests.unit import utils from manilaclient.v2 import availability_zones @ddt.ddt class AvailabilityZoneTest(utils.TestCase): def _get_manager(self, microversion): version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) return availability_zones.AvailabilityZoneManager( api=mock_microversion) def _get_resource_path(self, microversion): if (api_versions.APIVersion(microversion) > api_versions.APIVersion("2.6")): return availability_zones.RESOURCE_PATH return availability_zones.RESOURCE_PATH_LEGACY @ddt.data("2.6", "2.7", api_versions.MIN_VERSION, api_versions.MAX_VERSION) def test_list(self, microversion): manager = self._get_manager(microversion) resource_path = self._get_resource_path(microversion) self.mock_object(manager, "_list") result = manager.list() manager._list.assert_called_once_with( resource_path, availability_zones.RESOURCE_NAME) self.assertEqual(manager._list.return_value, result) def test_representation(self): resource = availability_zones.AvailabilityZone(None, {}) resource.id = "9d21a755-b5ca-453e-a155-ee9c9066d909" expected = "" self.assertEqual(repr(resource), expected) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_group_snapshots.py0000664000175000017500000002334213644133413030662 0ustar zuulzuul00000000000000# Copyright 2016 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. import mock import ddt import six import manilaclient from manilaclient import exceptions from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes as fake from manilaclient.v2 import share_group_snapshots as snapshots class ShareGroupSnapshotTest(utils.TestCase): def setUp(self): super(ShareGroupSnapshotTest, self).setUp() self.manager = snapshots.ShareGroupSnapshotManager(fake.FakeClient()) self.share_group_snapshot = snapshots.ShareGroupSnapshot( self.manager, {'id': 'fake_id'}) self.fake_kwargs = {'key': 'value'} def test_repr(self): result = six.text_type(self.share_group_snapshot) self.assertEqual('', result) def test_update(self): mock_manager_update = self.mock_object(self.manager, 'update') self.share_group_snapshot.update(**self.fake_kwargs) mock_manager_update.assert_called_once_with( self.share_group_snapshot, **self.fake_kwargs) def test_delete(self): mock_manager_delete = self.mock_object(self.manager, 'delete') self.share_group_snapshot.delete() mock_manager_delete.assert_called_once_with(self.share_group_snapshot) def test_reset_state(self): mock_manager_reset_state = self.mock_object( self.manager, 'reset_state') self.share_group_snapshot.reset_state('fake_state') mock_manager_reset_state.assert_called_once_with( self.share_group_snapshot, 'fake_state') @ddt.ddt class ShareGroupSnapshotManagerTest(utils.TestCase): def setUp(self): super(ShareGroupSnapshotManagerTest, self).setUp() self.manager = snapshots.ShareGroupSnapshotManager(fake.FakeClient()) def test_create(self): fake_share_group_snapshot = fake.ShareGroupSnapshot() mock_create = self.mock_object( self.manager, '_create', mock.Mock(return_value=fake_share_group_snapshot)) create_args = { 'name': fake.ShareGroupSnapshot.name, 'description': fake.ShareGroupSnapshot.description, } result = self.manager.create(fake.ShareGroupSnapshot, **create_args) self.assertIs(fake_share_group_snapshot, result) expected_body = { snapshots.RESOURCE_NAME: { 'name': fake.ShareGroupSnapshot.name, 'description': fake.ShareGroupSnapshot.description, 'share_group_id': fake.ShareGroupSnapshot().id, }, } mock_create.assert_called_once_with( snapshots.RESOURCES_PATH, expected_body, snapshots.RESOURCE_NAME) def test_create_minimal_args(self): fake_share_group_snapshot = fake.ShareGroupSnapshot() mock_create = self.mock_object( self.manager, '_create', mock.Mock(return_value=fake_share_group_snapshot)) result = self.manager.create(fake.ShareGroupSnapshot) self.assertIs(fake_share_group_snapshot, result) expected_body = { snapshots.RESOURCE_NAME: { 'share_group_id': fake.ShareGroupSnapshot().id, }, } mock_create.assert_called_once_with( snapshots.RESOURCES_PATH, expected_body, snapshots.RESOURCE_NAME) def test_create_using_unsupported_microversion(self): self.manager.api.api_version = manilaclient.API_MIN_VERSION self.assertRaises( exceptions.UnsupportedVersion, self.manager.create, fake.ShareGroupSnapshot) def test_get(self): fake_share_group_snapshot = fake.ShareGroupSnapshot() mock_get = self.mock_object( self.manager, '_get', mock.Mock(return_value=fake_share_group_snapshot)) result = self.manager.get(fake.ShareGroupSnapshot.id) self.assertIs(fake_share_group_snapshot, result) mock_get.assert_called_once_with( snapshots.RESOURCE_PATH % fake.ShareGroupSnapshot.id, snapshots.RESOURCE_NAME) def test_list(self): fake_share_group_snapshot = fake.ShareGroupSnapshot() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_share_group_snapshot])) result = self.manager.list() self.assertEqual([fake_share_group_snapshot], result) mock_list.assert_called_once_with( snapshots.RESOURCES_PATH + '/detail', snapshots.RESOURCES_NAME) def test_list_no_detail(self): fake_share_group_snapshot = fake.ShareGroupSnapshot() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_share_group_snapshot])) result = self.manager.list(detailed=False) self.assertEqual([fake_share_group_snapshot], result) mock_list.assert_called_once_with( snapshots.RESOURCES_PATH, snapshots.RESOURCES_NAME) def test_list_with_filters(self): fake_share_group_snapshot = fake.ShareGroupSnapshot() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_share_group_snapshot])) filters = {'all_tenants': 1, 'status': 'ERROR'} result = self.manager.list(detailed=False, search_opts=filters) self.assertEqual([fake_share_group_snapshot], result) expected_path = (snapshots.RESOURCES_PATH + '?all_tenants=1&status=ERROR') mock_list.assert_called_once_with( expected_path, snapshots.RESOURCES_NAME) def test_list_with_sorting(self): fake_share_group_snapshot = fake.ShareGroupSnapshot() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_share_group_snapshot])) result = self.manager.list( detailed=False, sort_dir='asc', sort_key='name') self.assertEqual([fake_share_group_snapshot], result) expected_path = ( snapshots.RESOURCES_PATH + '?sort_dir=asc&sort_key=name') mock_list.assert_called_once_with( expected_path, snapshots.RESOURCES_NAME) @ddt.data({'sort_key': 'name', 'sort_dir': 'invalid'}, {'sort_key': 'invalid', 'sort_dir': 'asc'}) @ddt.unpack def test_list_with_invalid_sorting(self, sort_key, sort_dir): self.assertRaises( ValueError, self.manager.list, sort_dir=sort_dir, sort_key=sort_key) def test_update(self): fake_share_group_snapshot = fake.ShareGroupSnapshot() mock_get = self.mock_object( self.manager, '_get', mock.Mock(return_value=fake_share_group_snapshot)) mock_update = self.mock_object( self.manager, '_update', mock.Mock(return_value=fake_share_group_snapshot)) update_args = { 'name': fake.ShareGroupSnapshot.name, 'description': fake.ShareGroupSnapshot.description, } result = self.manager.update(fake.ShareGroupSnapshot(), **update_args) self.assertIs(fake_share_group_snapshot, result) self.assertFalse(mock_get.called) mock_update.assert_called_once_with( snapshots.RESOURCE_PATH % fake.ShareGroupSnapshot.id, {snapshots.RESOURCE_NAME: update_args}, snapshots.RESOURCE_NAME) def test_update_no_data(self): fake_share_group_snapshot = fake.ShareGroupSnapshot() mock_get = self.mock_object( self.manager, '_get', mock.Mock(return_value=fake_share_group_snapshot)) mock_update = self.mock_object( self.manager, '_update', mock.Mock(return_value=fake_share_group_snapshot)) update_args = {} result = self.manager.update(fake.ShareGroupSnapshot(), **update_args) self.assertIs(fake_share_group_snapshot, result) mock_get.assert_called_once_with( snapshots.RESOURCE_PATH % fake.ShareGroupSnapshot.id, snapshots.RESOURCE_NAME) self.assertFalse(mock_update.called) def test_delete(self): mock_delete = self.mock_object(self.manager, '_delete') mock_post = self.mock_object(self.manager.api.client, 'post') self.manager.delete(fake.ShareGroupSnapshot()) mock_delete.assert_called_once_with( snapshots.RESOURCE_PATH % fake.ShareGroupSnapshot.id) self.assertFalse(mock_post.called) def test_delete_force(self): mock_delete = self.mock_object(self.manager, '_delete') mock_post = self.mock_object(self.manager.api.client, 'post') self.manager.delete(fake.ShareGroupSnapshot.id, force=True) self.assertFalse(mock_delete.called) mock_post.assert_called_once_with( snapshots.RESOURCE_PATH_ACTION % fake.ShareGroupSnapshot.id, body={'force_delete': None}) def test_reset_state(self): mock_post = self.mock_object(self.manager.api.client, 'post') self.manager.reset_state(fake.ShareGroupSnapshot(), 'fake_state') mock_post.assert_called_once_with( snapshots.RESOURCE_PATH_ACTION % fake.ShareGroupSnapshot.id, body={'reset_status': {'status': 'fake_state'}}) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_quota_classes.py0000664000175000017500000000531513644133413027110 0ustar zuulzuul00000000000000# 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. import ddt import mock from manilaclient import api_versions from manilaclient.tests.unit import utils from manilaclient.v2 import quota_classes @ddt.ddt class QuotaClassSetsTest(utils.TestCase): def _get_manager(self, microversion): version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) return quota_classes.QuotaClassSetManager(api=mock_microversion) def _get_resource_path(self, microversion): if (api_versions.APIVersion(microversion) > api_versions.APIVersion("2.6")): return quota_classes.RESOURCE_PATH return quota_classes.RESOURCE_PATH_LEGACY @ddt.data("2.6", "2.7") def test_class_quotas_get(self, microversion): class_name = 'test' manager = self._get_manager(microversion) resource_path = self._get_resource_path(microversion) expected_url = "%s/test" % resource_path with mock.patch.object(manager, '_get', mock.Mock(return_value='fake_get')): manager.get(class_name) manager._get.assert_called_once_with( expected_url, "quota_class_set") @ddt.data("2.6", "2.7") def test_update_quota(self, microversion): class_name = 'test' manager = self._get_manager(microversion) resource_path = self._get_resource_path(microversion) expected_url = "%s/test" % resource_path expected_body = { 'quota_class_set': { 'class_name': class_name, 'shares': 1, 'snapshots': 2, 'gigabytes': 3, 'snapshot_gigabytes': 4, 'share_networks': 5, }, } with mock.patch.object(manager, '_update', mock.Mock(return_value='fake_update')): manager.update( class_name, shares=1, snapshots=2, gigabytes=3, snapshot_gigabytes=4, share_networks=5) manager._update.assert_called_once_with( expected_url, expected_body) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_instance_export_locations.py0000664000175000017500000000411713644133413032703 0ustar zuulzuul00000000000000# 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 mock from manilaclient import api_versions from manilaclient import extension from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import share_instance_export_locations extensions = [ extension.Extension('share_instance_export_locations', share_instance_export_locations), ] cs = fakes.FakeClient(extensions=extensions) @ddt.ddt class ShareInstanceExportLocationsTest(utils.TestCase): def _get_manager(self, microversion): version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) return ( share_instance_export_locations.ShareInstanceExportLocationManager( api=mock_microversion) ) def test_list_of_export_locations(self): share_instance_id = '1234' cs.share_instance_export_locations.list( share_instance_id, search_opts=None) cs.assert_called( 'GET', '/share_instances/%s/export_locations' % share_instance_id) def test_get_single_export_location(self): share_instance_id = '1234' el_uuid = 'fake_el_uuid' cs.share_instance_export_locations.get(share_instance_id, el_uuid) cs.assert_called( 'GET', ('/share_instances/%(share_instance_id)s/export_locations/' '%(el_uuid)s') % { 'share_instance_id': share_instance_id, 'el_uuid': el_uuid}) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_limits.py0000664000175000017500000001250113644133413025536 0ustar zuulzuul00000000000000# Copyright 2018 OpenStack Foundation # # 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 mock from manilaclient.tests.unit import utils from manilaclient.v2 import limits def _get_default_RateLimit(verb="verb1", uri="uri1", regex="regex1", value="value1", remain="remain1", unit="unit1", next_available="next1"): return limits.RateLimit(verb, uri, regex, value, remain, unit, next_available) class TestLimits(utils.TestCase): def test_repr(self): li = limits.Limits(None, {"foo": "bar"}) self.assertEqual("", repr(li)) def test_absolute(self): li = limits.Limits(None, {"absolute": {"name1": "value1", "name2": "value2"}}) l1 = limits.AbsoluteLimit("name1", "value1") l2 = limits.AbsoluteLimit("name2", "value2") for item in li.absolute: self.assertIn(item, [l1, l2]) def test_rate(self): limit_param = { "rate": [{ "uri": "uri1", "regex": "regex1", "limit": [{ "verb": "verb1", "value": "value1", "remaining": "remain1", "unit": "unit1", "next-available": "next1" }] }, { "uri": "uri2", "regex": "regex2", "limit": [{ "verb": "verb2", "value": "value2", "remaining": "remain2", "unit": "unit2", "next-available": "next2" }] }] } li = limits.Limits(None, limit_param) l1 = limits.RateLimit("verb1", "uri1", "regex1", "value1", "remain1", "unit1", "next1") l2 = limits.RateLimit("verb2", "uri2", "regex2", "value2", "remain2", "unit2", "next2") for item in li.rate: self.assertIn(item, [l1, l2]) class TestRateLimit(utils.TestCase): def test_equal(self): l1 = _get_default_RateLimit() l2 = _get_default_RateLimit() self.assertEqual(l1, l2) def test_not_equal_verbs(self): l1 = _get_default_RateLimit() l2 = _get_default_RateLimit(verb="verb2") self.assertNotEqual(l1, l2) def test_not_equal_uris(self): l1 = _get_default_RateLimit() l2 = _get_default_RateLimit(uri="uri2") self.assertNotEqual(l1, l2) def test_not_equal_regexps(self): l1 = _get_default_RateLimit() l2 = _get_default_RateLimit(regex="regex2") self.assertNotEqual(l1, l2) def test_not_equal_values(self): l1 = _get_default_RateLimit() l2 = _get_default_RateLimit(value="value2") self.assertNotEqual(l1, l2) def test_not_equal_remains(self): l1 = _get_default_RateLimit() l2 = _get_default_RateLimit(remain="remain2") self.assertNotEqual(l1, l2) def test_not_equal_units(self): l1 = _get_default_RateLimit() l2 = _get_default_RateLimit(unit="unit2") self.assertNotEqual(l1, l2) def test_not_equal_next_available(self): l1 = _get_default_RateLimit() l2 = _get_default_RateLimit(next_available="next2") self.assertNotEqual(l1, l2) def test_repr(self): l1 = _get_default_RateLimit() self.assertEqual("", repr(l1)) class TestAbsoluteLimit(utils.TestCase): def test_equal(self): l1 = limits.AbsoluteLimit("name1", "value1") l2 = limits.AbsoluteLimit("name1", "value1") self.assertEqual(l1, l2) def test_not_equal_values(self): l1 = limits.AbsoluteLimit("name1", "value1") l2 = limits.AbsoluteLimit("name1", "value2") self.assertNotEqual(l1, l2) def test_not_equal_names(self): l1 = limits.AbsoluteLimit("name1", "value1") l2 = limits.AbsoluteLimit("name2", "value1") self.assertNotEqual(l1, l2) def test_repr(self): l1 = limits.AbsoluteLimit("name1", "value1") self.assertEqual("", repr(l1)) @ddt.ddt class TestLimitsManager(utils.TestCase): def test_get(self): api = mock.Mock() api.client.get.return_value = ( None, {"limits": {"absolute": {"name1": "value1", }}, "no-limits": {"absolute": {"name2": "value2", }}}) l1 = limits.AbsoluteLimit("name1", "value1") limitsManager = limits.LimitsManager(api) lim = limitsManager.get() api.client.get.assert_called_once_with('/limits') self.assertIsInstance(lim, limits.Limits) for l in lim.absolute: self.assertEqual(l1, l) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_networks.py0000664000175000017500000002040013644133413027270 0ustar zuulzuul00000000000000# 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. import ddt import itertools import mock from manilaclient import api_versions from manilaclient import exceptions from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import share_networks @ddt.ddt class ShareNetworkTest(utils.TestCase): class _FakeShareNetwork(object): id = 'fake_share_network_id' class _FakeSecurityService(object): id = 'fake_security_service_id' def setUp(self): super(ShareNetworkTest, self).setUp() self.manager = share_networks.ShareNetworkManager(fakes.FakeClient()) self.values = { 'nova_net_id': 'fake_nova_net_id', 'neutron_net_id': 'fake net id', 'neutron_subnet_id': 'fake subnet id', 'name': 'fake name', 'description': 'new whatever', } @ddt.data("2.25", "2.26") def test_create(self, microversion): api_version = api_versions.APIVersion(microversion) values = self.values.copy() if (api_version >= api_versions.APIVersion("2.26")): del(values['nova_net_id']) body_expected = {share_networks.RESOURCE_NAME: values} manager = share_networks.ShareNetworkManager( fakes.FakeClient(api_version=api_version)) with mock.patch.object(manager, '_create', fakes.fake_create): result = manager.create(**values) self.assertEqual(result['url'], share_networks.RESOURCES_PATH) self.assertEqual(result['resp_key'], share_networks.RESOURCE_NAME) self.assertEqual( body_expected, result['body']) def test_delete_str(self): share_nw = 'fake share nw' with mock.patch.object(self.manager, '_delete', mock.Mock()): self.manager.delete(share_nw) self.manager._delete.assert_called_once_with( share_networks.RESOURCE_PATH % share_nw) def test_delete_obj(self): share_nw = self._FakeShareNetwork() with mock.patch.object(self.manager, '_delete', mock.Mock()): self.manager.delete(share_nw) self.manager._delete.assert_called_once_with( share_networks.RESOURCE_PATH % share_nw.id) def test_get(self): share_nw = 'fake share nw' with mock.patch.object(self.manager, '_get', mock.Mock()): self.manager.get(share_nw) self.manager._get.assert_called_once_with( share_networks.RESOURCE_PATH % share_nw, share_networks.RESOURCE_NAME) def test_list_not_detailed(self): with mock.patch.object(self.manager, '_list', mock.Mock(return_value=None)): self.manager.list(detailed=False) self.manager._list.assert_called_once_with( share_networks.RESOURCES_PATH, share_networks.RESOURCES_NAME) def test_list(self): with mock.patch.object(self.manager, '_list', mock.Mock(return_value=None)): self.manager.list() self.manager._list.assert_called_once_with( share_networks.RESOURCES_PATH + '/detail', share_networks.RESOURCES_NAME) def test_list_with_filters(self): filters = {'all_tenants': 1, 'status': 'ERROR'} expected_path = ("%s/detail?all_tenants=1&status=" "ERROR" % share_networks.RESOURCES_PATH) with mock.patch.object(self.manager, '_list', mock.Mock(return_value=None)): self.manager.list(search_opts=filters) self.manager._list.assert_called_once_with( expected_path, share_networks.RESOURCES_NAME) @ddt.data(*itertools.product( ["2.25", "2.26"], ['fake share nw', _FakeShareNetwork()] )) @ddt.unpack def test_update_share_network(self, microversion, share_nw): api_version = api_versions.APIVersion(microversion) values = self.values.copy() if (api_version >= api_versions.APIVersion("2.26")): del(values['nova_net_id']) body_expected = {share_networks.RESOURCE_NAME: values} manager = share_networks.ShareNetworkManager( fakes.FakeClient(api_version=api_version)) with mock.patch.object(manager, '_update', fakes.fake_update): result = manager.update(share_nw, **values) id = share_nw.id if hasattr(share_nw, 'id') else share_nw self.assertEqual(result['url'], share_networks.RESOURCE_PATH % id) self.assertEqual(result['resp_key'], share_networks.RESOURCE_NAME) self.assertEqual(result['body'], body_expected) def test_update_with_exception(self): share_nw = 'fake share nw' self.assertRaises(exceptions.CommandError, self.manager.update, share_nw) def test_add_security_service(self): security_service = 'fake security service' share_nw = 'fake share nw' expected_path = (share_networks.RESOURCE_PATH + '/action') % share_nw expected_body = { 'add_security_service': { 'security_service_id': security_service, }, } with mock.patch.object(self.manager, '_create', mock.Mock()): self.manager.add_security_service(share_nw, security_service) self.manager._create.assert_called_once_with( expected_path, expected_body, share_networks.RESOURCE_NAME) def test_add_security_service_to_share_nw_object(self): security_service = self._FakeSecurityService() share_nw = self._FakeShareNetwork() expected_path = ((share_networks.RESOURCE_PATH + '/action') % share_nw.id) expected_body = { 'add_security_service': { 'security_service_id': security_service.id, }, } with mock.patch.object(self.manager, '_create', mock.Mock()): self.manager.add_security_service(share_nw, security_service) self.manager._create.assert_called_once_with( expected_path, expected_body, share_networks.RESOURCE_NAME) def test_remove_security_service(self): security_service = 'fake security service' share_nw = 'fake share nw' expected_path = (share_networks.RESOURCE_PATH + '/action') % share_nw expected_body = { 'remove_security_service': { 'security_service_id': security_service, }, } with mock.patch.object(self.manager, '_create', mock.Mock()): self.manager.remove_security_service(share_nw, security_service) self.manager._create.assert_called_once_with( expected_path, expected_body, share_networks.RESOURCE_NAME) def test_remove_security_service_from_share_nw_object(self): security_service = self._FakeSecurityService() share_nw = self._FakeShareNetwork() expected_path = ((share_networks.RESOURCE_PATH + '/action') % share_nw.id) expected_body = { 'remove_security_service': { 'security_service_id': security_service.id, }, } with mock.patch.object(self.manager, '_create', mock.Mock()): self.manager.remove_security_service(share_nw, security_service) self.manager._create.assert_called_once_with( expected_path, expected_body, share_networks.RESOURCE_NAME) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_servers.py0000664000175000017500000001766113644133413027124 0ustar zuulzuul00000000000000# Copyright 2014 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. import ddt import mock from manilaclient.common.apiclient import base as common_base from manilaclient.common import constants from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import share_servers class FakeShareServer(object): _info = { "backend_details": { "fake_key1": "fake_value1", "fake_key2": "fake_value2", } } class ShareServerTest(utils.TestCase): def setUp(self): super(ShareServerTest, self).setUp() self.share_server_id = 'foo' self.share_network = 'bar' info = { 'id': self.share_server_id, 'share_network_name': self.share_network, } self.resource_class = share_servers.ShareServer( manager=self, info=info) def test_get_repr_of_share_server(self): self.assertIn( 'ShareServer: %s' % self.share_server_id, repr(self.resource_class), ) def test_get_share_network_attr(self): # We did not set 'share_network' attr within instance, it is expected # that attr 'share_network_name' will be reused. self.assertEqual(self.resource_class.share_network, self.share_network) def test_get_nonexistent_share_network_name(self): resource_class = share_servers.ShareServer(manager=self, info={}) try: # We expect AttributeError instead of endless loop of getattr resource_class.share_network_name except AttributeError: pass else: raise Exception("Expected exception 'AttributeError' " "has not been raised.") @ddt.ddt class ShareServerManagerTest(utils.TestCase): def setUp(self): super(ShareServerManagerTest, self).setUp() self.manager = share_servers.ShareServerManager(api=fakes.FakeClient()) def test_list(self): with mock.patch.object(self.manager, '_list', mock.Mock(return_value=None)): self.manager.list() self.manager._list.assert_called_once_with( share_servers.RESOURCES_PATH, share_servers.RESOURCES_NAME) @ddt.data(None, {}, {'opt1': 'fake_opt1', 'opt12': 'fake_opt2'}) def test_manage(self, driver_options): host = 'fake_host' share_network_id = 'fake_share_net_id' share_network_subnet_id = 'fake_share_network_subnet_id' identifier = 'ff-aa-kk-ee-00' if driver_options is None: driver_options = {} expected_body = { 'host': host, 'share_network_id': share_network_id, 'identifier': identifier, 'driver_options': driver_options, 'share_network_subnet_id': share_network_subnet_id, } with mock.patch.object(self.manager, '_create', mock.Mock(return_value='fake')): result = self.manager.manage( host, share_network_id, identifier, driver_options=driver_options, share_network_subnet_id=share_network_subnet_id) self.manager._create.assert_called_once_with( share_servers.RESOURCES_PATH + '/manage', {'share_server': expected_body}, 'share_server' ) self.assertEqual('fake', result) @ddt.data(True, False) def test_unmanage(self, force): share_server = {'id': 'fake'} with mock.patch.object(self.manager, '_action', mock.Mock(return_value='fake')): result = self.manager.unmanage(share_server, force) self.manager._action.assert_called_once_with( "unmanage", share_server, {'force': force}) self.assertEqual('fake', result) def test_reset_state(self): share_server = {'id': 'fake'} state = constants.STATUS_AVAILABLE with mock.patch.object(self.manager, '_action', mock.Mock(return_value='fake')): result = self.manager.reset_state(share_server, state) self.manager._action.assert_called_once_with( "reset_status", share_server, {"status": state}) self.assertEqual('fake', result) @ddt.data(("reset_status", {"status": constants.STATUS_AVAILABLE}), ("unmanage", {"id": "fake_id"})) @ddt.unpack def test__action(self, action, info): action = "" share_server = {"id": 'fake_id'} expected_url = '/share-servers/%s/action' % share_server['id'] expected_body = {action: info} with mock.patch.object(self.manager.api.client, 'post', mock.Mock(return_value='fake')): self.mock_object(common_base, 'getid', mock.Mock(return_value=share_server['id'])) result = self.manager._action(action, share_server, info) self.manager.api.client.post.assert_called_once_with( expected_url, body=expected_body ) self.assertEqual('fake', result) def test_list_with_one_search_opt(self): host = 'fake_host' query_string = "?host=%s" % host with mock.patch.object(self.manager, '_list', mock.Mock(return_value=None)): self.manager.list({'host': host}) self.manager._list.assert_called_once_with( share_servers.RESOURCES_PATH + query_string, share_servers.RESOURCES_NAME, ) def test_list_with_two_search_opts(self): host = 'fake_host' status = 'fake_status' query_string = "?host=%s&status=%s" % (host, status) with mock.patch.object(self.manager, '_list', mock.Mock(return_value=None)): self.manager.list({'host': host, 'status': status}) self.manager._list.assert_called_once_with( share_servers.RESOURCES_PATH + query_string, share_servers.RESOURCES_NAME, ) def test_delete(self): share_server_id = 'fake_share_server_id' with mock.patch.object(self.manager, '_delete', mock.Mock()): self.manager.delete(share_server_id) self.manager._delete.assert_called_once_with( share_servers.RESOURCE_PATH % share_server_id) def test_get(self): server = FakeShareServer() with mock.patch.object(self.manager, '_get', mock.Mock(return_value=server)): share_server_id = 'fake_share_server_id' self.manager.get(share_server_id) self.manager._get.assert_called_once_with( "%s/%s" % (share_servers.RESOURCES_PATH, share_server_id), share_servers.RESOURCE_NAME) for key in ["details:fake_key1", "details:fake_key2"]: self.assertIn(key, list(server._info)) def test_details(self): with mock.patch.object(self.manager, '_get', mock.Mock(return_value=None)): share_server_id = 'fake_share_server_id' self.manager.details(share_server_id) self.manager._get.assert_called_once_with( "%s/%s/details" % (share_servers.RESOURCES_PATH, share_server_id), 'details') python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_replicas.py0000664000175000017500000001106313644133413027223 0ustar zuulzuul00000000000000# Copyright 2015 Chuck Fouts. # 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 mock from manilaclient import api_versions from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import share_replicas FAKE_REPLICA = 'fake_replica' @ddt.ddt class ShareReplicasTest(utils.TestCase): class _FakeShareReplica(object): id = 'fake_share_replica_id' def setUp(self): super(ShareReplicasTest, self).setUp() microversion = api_versions.APIVersion("2.11") self.manager = share_replicas.ShareReplicaManager( fakes.FakeClient(api_version=microversion)) def test_create(self): values = { 'availability_zone': 'az1', 'share': 's1', } self._create_common(values) def _create_common(self, values): with mock.patch.object(self.manager, '_create', fakes.fake_create): result = self.manager.create(**values) values['share_id'] = values.pop('share') body_expected = {share_replicas.RESOURCE_NAME: values} self.assertEqual(share_replicas.RESOURCES_PATH, result['url']) self.assertEqual(share_replicas.RESOURCE_NAME, result['resp_key']) self.assertEqual(body_expected, result['body']) def test_delete_str(self): with mock.patch.object(self.manager, '_delete', mock.Mock()): self.manager.delete(FAKE_REPLICA) self.manager._delete.assert_called_once_with( share_replicas.RESOURCE_PATH % FAKE_REPLICA) def test_delete_obj(self): replica = self._FakeShareReplica with mock.patch.object(self.manager, '_delete', mock.Mock()): self.manager.delete(replica) self.manager._delete.assert_called_once_with( share_replicas.RESOURCE_PATH % replica.id) def test_delete_with_force(self): with mock.patch.object(self.manager, '_action', mock.Mock()): self.manager.delete(FAKE_REPLICA, force=True) self.manager._action.assert_called_once_with( 'force_delete', FAKE_REPLICA) def test_get(self): with mock.patch.object(self.manager, '_get', mock.Mock()): self.manager.get(FAKE_REPLICA) self.manager._get.assert_called_once_with( share_replicas.RESOURCE_PATH % FAKE_REPLICA, share_replicas.RESOURCE_NAME) def test_promote(self): with mock.patch.object(self.manager, '_action', mock.Mock()): self.manager.promote(FAKE_REPLICA) self.manager._action.assert_called_once_with( 'promote', FAKE_REPLICA) def test_list(self): with mock.patch.object(self.manager, '_list', mock.Mock()): self.manager.list(search_opts=None) self.manager._list.assert_called_once_with( share_replicas.RESOURCES_PATH + '/detail', share_replicas.RESOURCES_NAME) def test_list_with_share(self): with mock.patch.object(self.manager, '_list', mock.Mock()): self.manager.list('share_id') share_uri = '?share_id=share_id' self.manager._list.assert_called_once_with( (share_replicas.RESOURCES_PATH + '/detail' + share_uri), share_replicas.RESOURCES_NAME) def test_resync(self): with mock.patch.object(self.manager, '_action', mock.Mock()): self.manager.resync(FAKE_REPLICA) self.manager._action.assert_called_once_with( 'resync', FAKE_REPLICA) @ddt.data('reset_status', 'reset_replica_state') def test_reset_state_actions(self, action): attr = 'status' if action == 'reset_status' else 'replica_state' method = getattr(self.manager, action.replace('status', 'state')) with mock.patch.object(self.manager, '_action', mock.Mock()): method(FAKE_REPLICA, 'some_status') self.manager._action.assert_called_once_with( action, FAKE_REPLICA, {attr: 'some_status'}) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_snapshot_instances.py0000664000175000017500000000701513644133413031331 0ustar zuulzuul00000000000000# 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 import mock from manilaclient import api_versions from manilaclient import exceptions from manilaclient import extension from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import share_snapshot_instances extensions = [ extension.Extension('share_snapshot_instances', share_snapshot_instances), ] cs = fakes.FakeClient(extensions=extensions) @ddt.ddt class SnapshotInstancesTest(utils.TestCase): def setUp(self): super(SnapshotInstancesTest, self).setUp() microversion = api_versions.APIVersion("2.19") mock_microversion = mock.Mock(api_version=microversion) self.manager = share_snapshot_instances.ShareSnapshotInstanceManager( api=mock_microversion) @ddt.data(True, False) def test_list(self, detailed): if detailed: url = '/snapshot-instances/detail' else: url = '/snapshot-instances' self.mock_object(self.manager, '_list', mock.Mock()) self.manager.list(detailed=detailed, search_opts=None) self.manager._list.assert_called_once_with(url, 'snapshot_instances') @ddt.data(True, False) def test_list_with_snapshot(self, detailed): if detailed: url = '/snapshot-instances/detail' else: url = '/snapshot-instances' self.mock_object(self.manager, '_list', mock.Mock()) self.manager.list(detailed=detailed, snapshot='snapshot_id') self.manager._list.assert_called_once_with( (url + '?snapshot_id=snapshot_id'), 'snapshot_instances',) def test_get(self): self.mock_object(self.manager, '_get', mock.Mock()) self.manager.get('fake_snapshot_instance') self.manager._get.assert_called_once_with( '/snapshot-instances/' + 'fake_snapshot_instance', 'snapshot_instance') def test_reset_instance_state(self): state = 'available' self.mock_object(self.manager, '_action', mock.Mock()) self.manager.reset_state('fake_instance', state) self.manager._action.assert_called_once_with( "reset_status", 'fake_instance', {"status": state}) @ddt.data('get', 'list', 'reset_state') def test_upsupported_microversion(self, method_name): unsupported_microversions = ('1.0', '2.18') arguments = { 'instance': 'FAKE_INSTANCE', } if method_name in ('list'): arguments.clear() for microversion in unsupported_microversions: microversion = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=microversion) manager = share_snapshot_instances.ShareSnapshotInstanceManager( api=mock_microversion) method = getattr(manager, method_name) self.assertRaises(exceptions.UnsupportedVersion, method, **arguments) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_client.py0000664000175000017500000003245313644133413025523 0ustar zuulzuul00000000000000# 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 mock import manilaclient from manilaclient import exceptions from manilaclient.tests.unit import utils from manilaclient.v2 import client from oslo_utils import uuidutils @ddt.ddt class ClientTest(utils.TestCase): def setUp(self): super(self.__class__, self).setUp() self.catalog = { 'share': [ {'region': 'TestRegion', 'publicURL': 'http://1.2.3.4'}, ], } def test_adapter_properties(self): # sample of properties, there are many more retries = 3 base_url = uuidutils.generate_uuid(dashed=False) s = client.session.Session() c = client.Client(session=s, api_version=manilaclient.API_MAX_VERSION, service_catalog_url=base_url, retries=retries, input_auth_token='token') self.assertEqual(base_url, c.client.endpoint_url) self.assertEqual(retries, c.client.retries) def test_auth_via_token_invalid(self): self.assertRaises(exceptions.ClientException, client.Client, api_version=manilaclient.API_MAX_VERSION, input_auth_token="token") def test_auth_via_token_and_session(self): s = client.session.Session() base_url = uuidutils.generate_uuid(dashed=False) c = client.Client(input_auth_token='token', service_catalog_url=base_url, session=s, api_version=manilaclient.API_MAX_VERSION) self.assertIsNotNone(c.client) self.assertIsNone(c.keystone_client) def test_auth_via_token(self): base_url = uuidutils.generate_uuid(dashed=False) c = client.Client(input_auth_token='token', service_catalog_url=base_url, api_version=manilaclient.API_MAX_VERSION) self.assertIsNotNone(c.client) self.assertIsNone(c.keystone_client) @mock.patch.object(client.Client, '_get_keystone_client', mock.Mock()) def test_valid_region_name_v1(self): self.mock_object(client.httpclient, 'HTTPClient') kc = client.Client._get_keystone_client.return_value kc.service_catalog = mock.Mock() kc.service_catalog.get_endpoints = mock.Mock(return_value=self.catalog) c = client.Client(api_version=manilaclient.API_DEPRECATED_VERSION, service_type="share", region_name='TestRegion') self.assertTrue(client.Client._get_keystone_client.called) kc.service_catalog.get_endpoints.assert_called_with('share') client.httpclient.HTTPClient.assert_called_with( 'http://1.2.3.4', mock.ANY, 'python-manilaclient', insecure=False, cacert=None, timeout=None, retries=None, http_log_debug=False, api_version=manilaclient.API_DEPRECATED_VERSION) self.assertIsNotNone(c.client) @mock.patch.object(client.Client, '_get_keystone_client', mock.Mock()) def test_nonexistent_region_name(self): kc = client.Client._get_keystone_client.return_value kc.service_catalog = mock.Mock() kc.service_catalog.get_endpoints = mock.Mock(return_value=self.catalog) self.assertRaises(RuntimeError, client.Client, api_version=manilaclient.API_MAX_VERSION, region_name='FakeRegion') self.assertTrue(client.Client._get_keystone_client.called) kc.service_catalog.get_endpoints.assert_called_with('sharev2') @mock.patch.object(client.Client, '_get_keystone_client', mock.Mock()) def test_regions_with_same_name(self): self.mock_object(client.httpclient, 'HTTPClient') catalog = { 'sharev2': [ {'region': 'FirstRegion', 'publicURL': 'http://1.2.3.4'}, {'region': 'secondregion', 'publicURL': 'http://1.1.1.1'}, {'region': 'SecondRegion', 'publicURL': 'http://2.2.2.2'}, ], } kc = client.Client._get_keystone_client.return_value kc.service_catalog = mock.Mock() kc.service_catalog.get_endpoints = mock.Mock(return_value=catalog) c = client.Client(api_version=manilaclient.API_MIN_VERSION, service_type='sharev2', region_name='SecondRegion') self.assertTrue(client.Client._get_keystone_client.called) kc.service_catalog.get_endpoints.assert_called_with('sharev2') client.httpclient.HTTPClient.assert_called_with( 'http://2.2.2.2', mock.ANY, 'python-manilaclient', insecure=False, cacert=None, timeout=None, retries=None, http_log_debug=False, api_version=manilaclient.API_MIN_VERSION) self.assertIsNotNone(c.client) def _get_client_args(self, **kwargs): client_args = { 'auth_url': 'both', 'api_version': manilaclient.API_DEPRECATED_VERSION, 'username': 'fake_username', 'service_type': 'sharev2', 'region_name': 'SecondRegion', 'input_auth_token': None, 'session': None, 'service_catalog_url': None, 'user_id': 'foo_user_id', 'user_domain_name': 'foo_user_domain_name', 'user_domain_id': 'foo_user_domain_id', 'project_name': 'foo_project_name', 'project_domain_name': 'foo_project_domain_name', 'project_domain_id': 'foo_project_domain_id', 'endpoint_type': 'publicUrl', 'cert': 'foo_cert', } client_args.update(kwargs) return client_args @ddt.data( {'auth_url': 'only_v3', 'api_key': 'password_backward_compat', 'endpoint_type': 'publicURL', 'project_id': 'foo_tenant_project_id'}, {'password': 'renamed_api_key', 'endpoint_type': 'public', 'tenant_id': 'foo_tenant_project_id'}, ) def test_client_init_no_session_no_auth_token_v3(self, kwargs): def fake_url_for(version): if version == 'v3.0': return 'url_v3.0' elif version == 'v2.0' and self.auth_url == 'both': return 'url_v2.0' else: return None self.mock_object(client.httpclient, 'HTTPClient') self.mock_object(client.ks_client, 'Client') self.mock_object(client.session.discover, 'Discover') self.mock_object(client.session, 'Session') client_args = self._get_client_args(**kwargs) client_args['api_version'] = manilaclient.API_MIN_VERSION self.auth_url = client_args['auth_url'] catalog = { 'share': [ {'region': 'SecondRegion', 'region_id': 'SecondRegion', 'url': 'http://4.4.4.4', 'interface': 'public', }, ], 'sharev2': [ {'region': 'FirstRegion', 'interface': 'public', 'region_id': 'SecondRegion', 'url': 'http://1.1.1.1'}, {'region': 'secondregion', 'interface': 'public', 'region_id': 'SecondRegion', 'url': 'http://2.2.2.2'}, {'region': 'SecondRegion', 'interface': 'internal', 'region_id': 'SecondRegion', 'url': 'http://3.3.3.1'}, {'region': 'SecondRegion', 'interface': 'public', 'region_id': 'SecondRegion', 'url': 'http://3.3.3.3'}, {'region': 'SecondRegion', 'interface': 'admin', 'region_id': 'SecondRegion', 'url': 'http://3.3.3.2'}, ], } client.session.discover.Discover.return_value.url_for.side_effect = ( fake_url_for) client.ks_client.Client.return_value.auth_token.return_value = ( 'fake_token') mocked_ks_client = client.ks_client.Client.return_value mocked_ks_client.service_catalog.get_endpoints.return_value = catalog client.Client(**client_args) client.httpclient.HTTPClient.assert_called_with( 'http://3.3.3.3', mock.ANY, 'python-manilaclient', insecure=False, cacert=None, timeout=None, retries=None, http_log_debug=False, api_version=manilaclient.API_MIN_VERSION) client.ks_client.Client.assert_called_with( session=mock.ANY, version=(3, 0), auth_url='url_v3.0', username=client_args['username'], password=client_args.get('password', client_args.get('api_key')), user_id=client_args['user_id'], user_domain_name=client_args['user_domain_name'], user_domain_id=client_args['user_domain_id'], project_id=client_args.get('tenant_id', client_args.get('project_id')), project_name=client_args['project_name'], project_domain_name=client_args['project_domain_name'], project_domain_id=client_args['project_domain_id'], region_name=client_args['region_name'], ) mocked_ks_client.service_catalog.get_endpoints.assert_called_with( client_args['service_type']) mocked_ks_client.authenticate.assert_called_with() @ddt.data( {'auth_url': 'only_v2', 'api_key': 'foo', 'project_id': 'bar'}, {'password': 'foo', 'tenant_id': 'bar'}, ) def test_client_init_no_session_no_auth_token_v2(self, kwargs): self.mock_object(client.httpclient, 'HTTPClient') self.mock_object(client.ks_client, 'Client') self.mock_object(client.session.discover, 'Discover') self.mock_object(client.session, 'Session') client_args = self._get_client_args(**kwargs) client_args['api_version'] = manilaclient.API_MIN_VERSION self.auth_url = client_args['auth_url'] catalog = { 'share': [ {'region': 'SecondRegion', 'publicUrl': 'http://4.4.4.4'}, ], 'sharev2': [ {'region': 'FirstRegion', 'publicUrl': 'http://1.1.1.1'}, {'region': 'secondregion', 'publicUrl': 'http://2.2.2.2'}, {'region': 'SecondRegion', 'internalUrl': 'http://3.3.3.1', 'publicUrl': 'http://3.3.3.3', 'adminUrl': 'http://3.3.3.2'}, ], } client.session.discover.Discover.return_value.url_for.side_effect = ( lambda v: 'url_v2.0' if v == 'v2.0' else None) client.ks_client.Client.return_value.auth_token.return_value = ( 'fake_token') mocked_ks_client = client.ks_client.Client.return_value mocked_ks_client.service_catalog.get_endpoints.return_value = catalog client.Client(**client_args) client.httpclient.HTTPClient.assert_called_with( 'http://3.3.3.3', mock.ANY, 'python-manilaclient', insecure=False, cacert=None, timeout=None, retries=None, http_log_debug=False, api_version=manilaclient.API_MIN_VERSION) client.ks_client.Client.assert_called_with( session=mock.ANY, version=(2, 0), auth_url='url_v2.0', username=client_args['username'], password=client_args.get('password', client_args.get('api_key')), tenant_id=client_args.get('tenant_id', client_args.get('project_id')), tenant_name=client_args['project_name'], region_name=client_args['region_name'], cert=client_args['cert'], use_keyring=False, force_new_token=False, stale_duration=300) mocked_ks_client.service_catalog.get_endpoints.assert_called_with( client_args['service_type']) mocked_ks_client.authenticate.assert_called_with() @mock.patch.object(client.ks_client, 'Client', mock.Mock()) @mock.patch.object(client.session.discover, 'Discover', mock.Mock()) @mock.patch.object(client.session, 'Session', mock.Mock()) def test_client_init_no_session_no_auth_token_endpoint_not_found(self): self.mock_object(client.httpclient, 'HTTPClient') client_args = self._get_client_args( auth_urli='fake_url', password='foo_password', tenant_id='foo_tenant_id') discover = client.session.discover.Discover discover.return_value.url_for.return_value = None mocked_ks_client = client.ks_client.Client.return_value self.assertRaises( exceptions.CommandError, client.Client, **client_args) self.assertTrue(client.session.Session.called) self.assertTrue(client.session.discover.Discover.called) self.assertFalse(client.httpclient.HTTPClient.called) self.assertFalse(client.ks_client.Client.called) self.assertFalse(mocked_ks_client.service_catalog.get_endpoints.called) self.assertFalse(mocked_ks_client.authenticate.called) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_instances.py0000664000175000017500000001050513644133413027410 0ustar zuulzuul00000000000000# 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 mock from manilaclient import api_versions from manilaclient import extension from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import share_instances extensions = [ extension.Extension('share_instances', share_instances), ] cs = fakes.FakeClient(extensions=extensions) @ddt.ddt class ShareInstancesTest(utils.TestCase): def _get_manager(self, microversion): version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) return share_instances.ShareInstanceManager(api=mock_microversion) def test_list(self): cs.share_instances.list(search_opts=None) cs.assert_called('GET', '/share_instances') @ddt.data(('id', 'b4991315-eb7d-43ec-979e-5715d4399827'), ('path', '//0.0.0.0/fake_path')) @ddt.unpack def test_list_by_export_location(self, filter_type, value): cs.share_instances.list(export_location=value) cs.assert_called( 'GET', '/share_instances?export_location_' + filter_type + '=' + value) def test_get(self): instance = type('None', (object, ), {'id': '1234'}) cs.share_instances.get(instance) cs.assert_called('GET', '/share_instances/1234') @ddt.data( ("2.6", type("InstanceUUID", (object, ), {"uuid": "1234"})), ("2.7", type("InstanceUUID", (object, ), {"uuid": "1234"})), ("2.6", type("InstanceID", (object, ), {"id": "1234"})), ("2.7", type("InstanceID", (object, ), {"id": "1234"})), ("2.6", "1234"), ("2.7", "1234"), ) @ddt.unpack def test_reset_instance_state(self, microversion, instance): manager = self._get_manager(microversion) state = 'available' if (api_versions.APIVersion(microversion) > api_versions.APIVersion("2.6")): action_name = "reset_status" else: action_name = "os-reset_status" with mock.patch.object(manager, "_action", mock.Mock()): manager.reset_state(instance, state) manager._action.assert_called_once_with( action_name, instance, {"status": state}) @ddt.data( ("2.6", type('InstanceUUID', (object, ), {"uuid": "1234"})), ("2.6", "1234"), ("2.7", type('InstanceUUID', (object, ), {"uuid": "1234"})), ("2.7", "1234"), ) @ddt.unpack def test_force_delete_share_snapshot(self, microversion, instance): manager = self._get_manager(microversion) if (api_versions.APIVersion(microversion) > api_versions.APIVersion("2.6")): action_name = "force_delete" else: action_name = "os-force_delete" with mock.patch.object(manager, "_action", mock.Mock()): manager.force_delete(instance) manager._action.assert_called_once_with(action_name, "1234") @ddt.data( ("2.6", "1234", "migrating_to"), ("2.6", "1234", "error"), ("2.6", "1234", "available"), ("2.7", "1234", "error_deleting"), ("2.7", "1234", "deleting"), ("2.7", "1234", "migrating"), ) @ddt.unpack def test_valid_instance_state(self, microversion, instance, state): manager = self._get_manager(microversion) if (api_versions.APIVersion(microversion) > api_versions.APIVersion("2.6")): action_name = "reset_status" else: action_name = "os-reset_status" with mock.patch.object(manager, "_action", mock.Mock()): manager.reset_state(instance, state) manager._action.assert_called_once_with( action_name, instance, {"status": state}) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/fake_clients.py0000664000175000017500000001536313644133413025636 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation # # 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 six.moves.urllib import parse from manilaclient import api_versions from manilaclient.common import httpclient from manilaclient.tests.unit import fakes from manilaclient.tests.unit import utils from manilaclient.v2 import client class FakeClient(fakes.FakeClient, client.Client): def __init__(self, *args, **kwargs): api_version = kwargs.get('version') or api_versions.MAX_VERSION client.Client.__init__(self, 'username', 'password', 'project_id', 'auth_url', extensions=kwargs.get('extensions'), version=api_version) self.client = FakeHTTPClient(**kwargs) class FakeHTTPClient(httpclient.HTTPClient): def __init__(self, **kwargs): api_version = kwargs.get('version') or api_versions.MAX_VERSION self.username = 'username' self.password = 'password' self.auth_url = 'auth_url' self.callstack = [] self.base_url = 'localhost' self.default_headers = { 'X-Auth-Token': 'xabc123', 'X-Openstack-Manila-Api-Version': api_version, 'Accept': 'application/json', } def _cs_request(self, url, method, **kwargs): return self._cs_request_with_retries(url, method, **kwargs) def _cs_request_base_url(self, url, method, **kwargs): return self._cs_request_with_retries(url, method, **kwargs) def _cs_request_with_retries(self, url, method, **kwargs): # Check that certain things are called correctly if method in ['GET', 'DELETE']: assert 'body' not in kwargs elif method == 'PUT': assert 'body' in kwargs # Call the method args = parse.parse_qsl(parse.urlparse(url)[4]) kwargs.update(args) munged_url = url.rsplit('?', 1)[0] munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_') munged_url = munged_url.replace('-', '_') callback = "%s_%s" % (method.lower(), munged_url) if not hasattr(self, callback): raise AssertionError('Called unknown API method: %s %s, ' 'expected fakes method name: %s' % (method, url, callback)) # Note the call self.callstack.append((method, url, kwargs.get('body', None))) status, headers, body = getattr(self, callback)(**kwargs) r = utils.TestResponse({ "status_code": status, "text": body, "headers": headers, }) return r, body # # Quotas # def get_os_quota_sets_test(self, **kw): quota_set = { 'quota_set': { 'tenant_id': 'test', 'metadata_items': [], 'shares': 1, 'snapshots': 1, 'gigabytes': 1, 'snapshot_gigabytes': 1, 'share_networks': 1, } } return (200, {}, quota_set) get_quota_sets_test = get_os_quota_sets_test def get_os_quota_sets_test_defaults(self): quota_set = { 'quota_set': { 'tenant_id': 'test', 'metadata_items': [], 'shares': 1, 'snapshots': 1, 'gigabytes': 1, 'snapshot_gigabytes': 1, 'share_networks': 1, } } return (200, {}, quota_set) get_quota_sets_test_defaults = get_os_quota_sets_test_defaults def put_os_quota_sets_test(self, body, **kw): assert list(body) == ['quota_set'] fakes.assert_has_keys(body['quota_set'], required=['tenant_id']) quota_set = { 'quota_set': { 'tenant_id': 'test', 'metadata_items': [], 'shares': 2, 'snapshots': 2, 'gigabytes': 1, 'snapshot_gigabytes': 1, 'share_networks': 1, } } return (200, {}, quota_set) put_quota_sets_test = put_os_quota_sets_test # # Quota Classes # def get_os_quota_class_sets_test(self, **kw): quota_class_set = { 'quota_class_set': { 'class_name': 'test', 'metadata_items': [], 'shares': 1, 'snapshots': 1, 'gigabytes': 1, 'snapshot_gigabytes': 1, 'share_networks': 1, } } return (200, {}, quota_class_set) get_quota_class_sets_test = get_os_quota_class_sets_test def put_os_quota_class_sets_test(self, body, **kw): assert list(body) == ['quota_class_set'] fakes.assert_has_keys(body['quota_class_set'], required=['class_name']) quota_class_set = { 'quota_class_set': { 'class_name': 'test', 'metadata_items': [], 'shares': 2, 'snapshots': 2, 'gigabytes': 1, 'snapshot_gigabytes': 1, 'share_networks': 1, } } return (200, {}, quota_class_set) put_quota_class_sets_test = put_os_quota_class_sets_test def delete_os_quota_sets_test(self, **kw): return (202, {}, {}) delete_quota_sets_test = delete_os_quota_sets_test # # List all extensions # def get_extensions(self, **kw): exts = [ { "alias": "FAKE-1", "description": "Fake extension number 1", "links": [], "name": "Fake1", "namespace": ("http://docs.openstack.org/" "/ext/fake1/api/v1.1"), "updated": "2011-06-09T00:00:00+00:00" }, { "alias": "FAKE-2", "description": "Fake extension number 2", "links": [], "name": "Fake2", "namespace": ("http://docs.openstack.org/" "/ext/fake1/api/v1.1"), "updated": "2011-06-09T00:00:00+00:00" }, ] return (200, {}, {"extensions": exts, }) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_replica_export_locations.py0000664000175000017500000000352713644133413032522 0ustar zuulzuul00000000000000# 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 mock from manilaclient import api_versions from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import share_replica_export_locations cs = fakes.FakeClient() @ddt.ddt class ShareReplicaExportLocationsTest(utils.TestCase): def _get_manager(self, microversion): version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) return ( share_replica_export_locations.ShareReplicaExportLocationManager( api=mock_microversion) ) def test_list_share_replica_export_locations(self): share_replica_id = '1234' cs.share_replica_export_locations.list(share_replica_id) cs.assert_called( 'GET', '/share-replicas/%s/export-locations' % share_replica_id) def test_get_share_replica_export_location(self): share_replica_id = '1234' el_uuid = 'fake_el_uuid' cs.share_replica_export_locations.get(share_replica_id, el_uuid) url = ('/share-replicas/%(share_replica_id)s/export-locations/' '%(el_uuid)s') payload = {'share_replica_id': share_replica_id, 'el_uuid': el_uuid} cs.assert_called('GET', url % payload) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_messages.py0000664000175000017500000001043613644133413026051 0ustar zuulzuul00000000000000# Copyright 2017 Red Hat # 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 mock import ddt import six from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes as fake from manilaclient.v2 import messages class MessageTest(utils.TestCase): def setUp(self): super(MessageTest, self).setUp() self.manager = messages.MessageManager(fake.FakeClient()) self.message = messages.Message( self.manager, {'id': 'fake_id'}) self.fake_kwargs = {'key': 'value'} def test_repr(self): result = six.text_type(self.message) self.assertEqual('', result) def test_delete(self): mock_manager_delete = self.mock_object(self.manager, 'delete') self.message.delete() mock_manager_delete.assert_called_once_with(self.message) @ddt.ddt class MessageManagerTest(utils.TestCase): def setUp(self): super(MessageManagerTest, self).setUp() self.manager = messages.MessageManager(fake.FakeClient()) def test_get(self): fake_message = fake.Message() mock_get = self.mock_object( self.manager, '_get', mock.Mock(return_value=fake_message)) result = self.manager.get(fake.Message.id) self.assertIs(fake_message, result) mock_get.assert_called_once_with( messages.RESOURCE_PATH % fake.Message.id, messages.RESOURCE_NAME) def test_list(self): fake_message = fake.Message() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_message])) result = self.manager.list() self.assertEqual([fake_message], result) mock_list.assert_called_once_with( messages.RESOURCES_PATH, messages.RESOURCES_NAME) @ddt.data( ({'action_id': 1, 'resource_type': 'share'}, '?action_id=1&resource_type=share'), ({'action_id': 1}, '?action_id=1'), ) @ddt.unpack def test_list_with_filters(self, filters, filters_path): fake_message = fake.Message() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_message])) result = self.manager.list(search_opts=filters) self.assertEqual([fake_message], result) expected_path = (messages.RESOURCES_PATH + filters_path) mock_list.assert_called_once_with( expected_path, messages.RESOURCES_NAME) @ddt.data('id', 'project_id', 'request_id', 'resource_type', 'action_id', 'detail_id', 'resource_id', 'message_level', 'expires_at', 'request_id', 'created_at') def test_list_with_sorting(self, key): fake_message = fake.Message() mock_list = self.mock_object( self.manager, '_list', mock.Mock(return_value=[fake_message])) result = self.manager.list(sort_dir='asc', sort_key=key) self.assertEqual([fake_message], result) expected_path = ( messages.RESOURCES_PATH + '?sort_dir=asc&sort_key=' + key) mock_list.assert_called_once_with( expected_path, messages.RESOURCES_NAME) @ddt.data( ('name', 'invalid'), ('invalid', 'asc'), ) @ddt.unpack def test_list_with_invalid_sorting(self, sort_key, sort_dir): self.assertRaises( ValueError, self.manager.list, sort_dir=sort_dir, sort_key=sort_key) def test_delete(self): mock_delete = self.mock_object(self.manager, '_delete') mock_post = self.mock_object(self.manager.api.client, 'post') self.manager.delete(fake.Message()) mock_delete.assert_called_once_with( messages.RESOURCE_PATH % fake.Message.id) self.assertFalse(mock_post.called) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/__init__.py0000664000175000017500000000000013644133413024724 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_shares.py0000664000175000017500000006561613644133413025541 0ustar zuulzuul00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 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. import ddt import mock from manilaclient import api_versions from manilaclient.common.apiclient import exceptions as client_exceptions from manilaclient import exceptions from manilaclient import extension from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import shares extensions = [ extension.Extension('shares', shares), ] cs = fakes.FakeClient(extensions=extensions) @ddt.ddt class SharesTest(utils.TestCase): # Testcases for class Share def setUp(self): super(SharesTest, self).setUp() self.share = shares.Share(None, {'id': 1}) self.share.manager = mock.Mock() def test_share_allow_access(self): access_level = 'fake_level' access_to = 'fake_credential' access_type = 'fake_type' self.share.allow(access_type, access_to, access_level) self.share.manager.allow.assert_called_once_with( self.share, access_type, access_to, access_level, None) # Testcases for class ShareManager @ddt.data('nfs', 'cifs', 'cephfs', 'glusterfs', 'hdfs', 'maprfs') def test_create_share_with_protocol(self, protocol): expected = { 'size': 1, 'snapshot_id': None, 'name': None, 'description': None, 'metadata': dict(), 'share_proto': protocol, 'share_network_id': None, 'share_type': None, 'is_public': False, 'availability_zone': None, } cs.shares.create(protocol, 1) cs.assert_called('POST', '/shares', {'share': expected}) @ddt.data( type('ShareNetworkUUID', (object, ), {'uuid': 'fake_nw'}), type('ShareNetworkID', (object, ), {'id': 'fake_nw'}), 'fake_nw') def test_create_share_with_share_network(self, share_network): expected = { 'size': 1, 'snapshot_id': None, 'name': None, 'description': None, 'metadata': dict(), 'share_proto': 'nfs', 'share_network_id': 'fake_nw', 'share_type': None, 'is_public': False, 'availability_zone': None, } cs.shares.create('nfs', 1, share_network=share_network) cs.assert_called('POST', '/shares', {'share': expected}) @ddt.data( type('ShareTypeUUID', (object, ), {'uuid': 'fake_st'}), type('ShareTypeID', (object, ), {'id': 'fake_st'}), 'fake_st') def test_create_share_with_share_type(self, share_type): expected = { 'size': 1, 'snapshot_id': None, 'name': None, 'description': None, 'metadata': dict(), 'share_proto': 'nfs', 'share_network_id': None, 'share_type': 'fake_st', 'is_public': False, 'availability_zone': None, } cs.shares.create('nfs', 1, share_type=share_type) cs.assert_called('POST', '/shares', {'share': expected}) @ddt.data({'is_public': True, 'availability_zone': 'nova'}, {'is_public': False, 'availability_zone': 'fake_azzzzz'}) @ddt.unpack def test_create_share_with_all_params_defined(self, is_public, availability_zone): body = { 'share': { 'is_public': is_public, 'share_type': None, 'name': None, 'snapshot_id': None, 'description': None, 'metadata': {}, 'share_proto': 'nfs', 'share_network_id': None, 'size': 1, 'availability_zone': availability_zone, } } cs.shares.create('nfs', 1, is_public=is_public, availability_zone=availability_zone) cs.assert_called('POST', '/shares', body) @ddt.data( type('ShareUUID', (object, ), {'uuid': '1234'}), type('ShareID', (object, ), {'id': '1234'}), '1234') def test_get_share(self, share): share = cs.shares.get(share) cs.assert_called('GET', '/shares/1234') @ddt.data( type('ShareUUID', (object, ), {'uuid': '1234'}), type('ShareID', (object, ), {'id': '1234'}), '1234') def test_get_update(self, share): data = dict(foo='bar', quuz='foobar') share = cs.shares.update(share, **data) cs.assert_called('PUT', '/shares/1234', {'share': data}) def test_delete_share(self): share = cs.shares.get('1234') cs.shares.delete(share) cs.assert_called('DELETE', '/shares/1234') @ddt.data( ("2.6", "/os-share-manage", None), ("2.7", "/shares/manage", None), ("2.8", "/shares/manage", True), ("2.8", "/shares/manage", False), ("2.49", "/shares/manage", False, '1234'), ) @ddt.unpack def test_manage_share(self, microversion, resource_path, is_public=False, share_server_id=None): service_host = "fake_service_host" protocol = "fake_protocol" export_path = "fake_export_path" driver_options = "fake_driver_options" share_type = "fake_share_type" name = "foo_name" description = "bar_description" expected_body = { "service_host": service_host, "share_type": share_type, "protocol": protocol, "export_path": export_path, "driver_options": driver_options, "name": name, "description": description, "share_server_id": share_server_id, } version = api_versions.APIVersion(microversion) if version >= api_versions.APIVersion('2.8'): expected_body["is_public"] = is_public mock_microversion = mock.Mock(api_version=version) manager = shares.ShareManager(api=mock_microversion) with mock.patch.object(manager, "_create", mock.Mock(return_value="fake")): if version < api_versions.APIVersion('2.8'): result = manager.manage( service_host, protocol, export_path, driver_options, share_type, name, description) elif (api_versions.APIVersion('2.8') <= version < api_versions.APIVersion('2.49')): result = manager.manage( service_host, protocol, export_path, driver_options, share_type, name, description, is_public) else: result = manager.manage( service_host, protocol, export_path, driver_options, share_type, name, description, is_public, share_server_id) self.assertEqual(manager._create.return_value, result) manager._create.assert_called_once_with( resource_path, {"share": expected_body}, "share") @ddt.data( type("ShareUUID", (object, ), {"uuid": "1234"}), type("ShareID", (object, ), {"id": "1234"}), "1234") def test_unmanage_share_v2_6(self, share): version = api_versions.APIVersion("2.6") mock_microversion = mock.Mock(api_version=version) manager = shares.ShareManager(api=mock_microversion) with mock.patch.object(manager, "_action", mock.Mock(return_value="fake")): result = manager.unmanage(share) self.assertFalse(manager._action.called) self.assertNotEqual("fake", result) self.assertEqual(manager.api.client.post.return_value, result) manager.api.client.post.assert_called_once_with( "/os-share-unmanage/1234/unmanage") def test_unmanage_share_v2_7(self): share = "fake_share" version = api_versions.APIVersion("2.7") mock_microversion = mock.Mock(api_version=version) manager = shares.ShareManager(api=mock_microversion) with mock.patch.object(manager, "_action", mock.Mock(return_value="fake")): result = manager.unmanage(share) manager._action.assert_called_once_with("unmanage", share) self.assertEqual("fake", result) def test_revert_to_snapshot(self): share = 'fake_share' snapshot = 'fake_snapshot' version = api_versions.APIVersion("2.27") mock_microversion = mock.Mock(api_version=version) manager = shares.ShareManager(api=mock_microversion) mock_action = self.mock_object( manager, '_action', mock.Mock(return_value='fake')) result = manager.revert_to_snapshot(share, snapshot) self.assertEqual('fake', result) mock_action.assert_called_once_with( 'revert', 'fake_share', info={'snapshot_id': 'fake_snapshot'}) def test_revert_to_snapshot_not_supported(self): share = 'fake_share' snapshot = 'fake_snapshot' version = api_versions.APIVersion("2.26") mock_microversion = mock.Mock(api_version=version) manager = shares.ShareManager(api=mock_microversion) self.assertRaises(client_exceptions.UnsupportedVersion, manager.revert_to_snapshot, share, snapshot) @ddt.data( ("2.6", "os-force_delete"), ("2.7", "force_delete"), ) @ddt.unpack def test_force_delete_share(self, microversion, action_name): share = "fake_share" version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) manager = shares.ShareManager(api=mock_microversion) with mock.patch.object(manager, "_action", mock.Mock(return_value="fake")): result = manager.force_delete(share) manager._action.assert_called_once_with(action_name, share) self.assertEqual("fake", result) def test_list_shares_index(self): cs.shares.list(detailed=False) cs.assert_called('GET', '/shares?is_public=True') def test_list_shares_index_with_search_opts(self): search_opts = { 'fake_str': 'fake_str_value', 'fake_int': 1, 'name~': 'fake_name', 'description~': 'fake_description', } cs.shares.list(detailed=False, search_opts=search_opts) cs.assert_called( 'GET', '/shares?description~=fake_description&fake_int=1&' 'fake_str=fake_str_value&is_public=True&name~=fake_name') @ddt.data(('id', 'b4991315-eb7d-43ec-979e-5715d4399827', True), ('id', 'b4991315-eb7d-43ec-979e-5715d4399827', False), ('path', 'fake_path', False), ('path', 'fake_path', True)) @ddt.unpack def test_list_shares_index_with_export_location(self, filter_type, value, detailed): search_opts = { 'export_location': value, } cs.shares.list(detailed=detailed, search_opts=search_opts) if detailed: cs.assert_called( 'GET', ('/shares/detail?export_location_' + filter_type + '=' + value + '&is_public=True')) else: cs.assert_called( 'GET', ('/shares?export_location_' + filter_type + '=' + value + '&is_public=True')) def test_list_shares_detailed(self): search_opts = { 'with_count': 'True', } shares, count = cs.shares.list(detailed=True, search_opts=search_opts) cs.assert_called( 'GET', '/shares/detail?is_public=True&with_count=True') self.assertEqual(2, count) self.assertEqual(1, len(shares)) def test_list_shares_detailed_with_count(self): cs.shares.list(detailed=True) cs.assert_called('GET', '/shares/detail?is_public=True') def test_list_shares_detailed_with_search_opts(self): search_opts = { 'fake_str': 'fake_str_value', 'fake_int': 1, } cs.shares.list(detailed=True, search_opts=search_opts) cs.assert_called( 'GET', '/shares/detail?fake_int=1&fake_str=fake_str_value&is_public=True') def test_list_shares_sort_by_asc_and_host_key(self): cs.shares.list(detailed=False, sort_key='host', sort_dir='asc') cs.assert_called('GET', '/shares?is_public=True&sort_dir=asc&sort_key=host') def test_list_shares_sort_by_desc_and_size_key(self): cs.shares.list(detailed=False, sort_key='size', sort_dir='desc') cs.assert_called('GET', '/shares?is_public=True&sort_dir=desc&sort_key=size') def test_list_shares_filter_by_share_network_alias(self): cs.shares.list(detailed=False, sort_key='share_network') cs.assert_called('GET', '/shares?is_public=True&sort_key=share_network_id') def test_list_shares_filter_by_snapshot_alias(self): cs.shares.list(detailed=False, sort_key='snapshot') cs.assert_called('GET', '/shares?is_public=True&sort_key=snapshot_id') def test_list_shares_filter_by_share_type_alias(self): cs.shares.list(detailed=False, sort_key='share_type') cs.assert_called('GET', '/shares?is_public=True&sort_key=share_type_id') def test_list_shares_by_improper_direction(self): self.assertRaises(ValueError, cs.shares.list, sort_dir='fake') def test_list_shares_by_improper_key(self): self.assertRaises(ValueError, cs.shares.list, sort_key='fake') @ddt.data( {'access_to': '127.0.0.1', 'access_type': 'ip', 'action_name': 'os-allow_access', 'microversion': '2.0'}, {'access_to': '1' * 4, 'access_type': 'user', 'action_name': 'os-allow_access', 'microversion': '2.0'}, {'access_to': '1' * 255, 'access_type': 'user', 'action_name': 'os-allow_access', 'microversion': '2.0'}, {'access_to': 'fake${.-_\'`}', 'access_type': 'user', 'action_name': 'os-allow_access', 'microversion': '2.0'}, {'access_to': 'MYDOMAIN-Administrator', 'access_type': 'user', 'action_name': 'os-allow_access', 'microversion': '2.0'}, {'access_to': 'test group name', 'access_type': 'user', 'action_name': 'os-allow_access', 'microversion': '2.0'}, {'access_to': 'x', 'access_type': 'cert', 'action_name': 'os-allow_access', 'microversion': '2.0'}, {'access_to': 'x' * 64, 'access_type': 'cert', 'action_name': 'os-allow_access', 'microversion': '2.0'}, {'access_to': 'tenant.example.com', 'access_type': 'cert', 'action_name': 'os-allow_access', 'microversion': '2.0'}, {'access_to': '127.0.0.1', 'access_type': 'ip', 'action_name': 'allow_access', 'microversion': '2.7'}, {'access_to': 'test group name', 'access_type': 'user', 'action_name': 'allow_access', 'microversion': '2.7'}, {'access_to': 'alice', 'access_type': 'cephx', 'action_name': 'allow_access', 'microversion': '2.13'}, {'access_to': 'alice_bob', 'access_type': 'cephx', 'action_name': 'allow_access', 'microversion': '2.13'}, {'access_to': 'alice bob', 'access_type': 'cephx', 'action_name': 'allow_access', 'microversion': '2.13'}, {'access_to': 'test group name', 'access_type': 'user', 'action_name': 'allow_access', 'microversion': '2.13'}, {'access_to': 'AD80:0000:0000:0000:ABAA:0000:00C2:0002', 'access_type': 'ip', 'action_name': 'allow_access', 'microversion': '2.38'}, {'access_to': 'AD80::/36', 'access_type': 'ip', 'action_name': 'allow_access', 'microversion': '2.38'}, {'access_to': 'AD80:ABAA::/128', 'access_type': 'ip', 'action_name': 'allow_access', 'microversion': '2.38'}, {'access_to': 'ad80::abaa:0:c2:2', 'access_type': 'ip', 'action_name': 'allow_access', 'microversion': '2.38'}, {'access_to': 'test group name', 'access_type': 'user', 'action_name': 'allow_access', 'microversion': '2.38'}, ) @ddt.unpack def test_allow_access_to_share(self, access_to, access_type, action_name, microversion): access = ('foo', {'access': 'bar'}) access_level = 'fake_access_level' share = 'fake_share' version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) manager = shares.ShareManager(api=mock_microversion) with mock.patch.object(manager, '_action', mock.Mock(return_value=access)): result = manager.allow(share, access_type, access_to, access_level) manager._action.assert_called_once_with( action_name, share, {'access_level': access_level, 'access_type': access_type, 'access_to': access_to}) self.assertEqual('bar', result) @ddt.data( {'access_to': 'localhost', 'access_type': 'ip', 'microversion': '2.0'}, {'access_to': '127.0.0.*', 'access_type': 'ip', 'microversion': '2.0'}, {'access_to': '127.0.0.0/33', 'access_type': 'ip', 'microversion': '2.0'}, {'access_to': '127.0.0.256', 'access_type': 'ip', 'microversion': '2.0'}, {'access_to': '1', 'access_type': 'user', 'microversion': '2.0'}, {'access_to': '1' * 3, 'access_type': 'user', 'microversion': '2.0'}, {'access_to': '1' * 256, 'access_type': 'user', 'microversion': '2.0'}, {'access_to': 'root+=', 'access_type': 'user', 'microversion': '2.0'}, {'access_to': '', 'access_type': 'cert', 'microversion': '2.0'}, {'access_to': ' ', 'access_type': 'cert', 'microversion': '2.0'}, {'access_to': 'x' * 65, 'access_type': 'cert', 'microversion': '2.0'}, {'access_to': 'alice', 'access_type': 'cephx', 'microversion': '2.0'}, {'access_to': '', 'access_type': 'cephx', 'microversion': '2.13'}, {'access_to': u"bj\u00F6rn", 'access_type': 'cephx', 'microversion': '2.13'}, {'access_to': "AD80:0000:0000:0000:ABAA:0000:00C2:0002/65", 'access_type': 'ip', 'microversion': '2.38'}, {'access_to': "AD80:0000:0000:0000:ABAA:0000:00C2:0002*32", 'access_type': 'ip', 'microversion': '2.38'}, {'access_to': "AD80:0000:0000:0000:ABAA:0000:00C2:0002", 'access_type': 'ip', 'microversion': '2.37'}, ) @ddt.unpack def test_allow_access_to_share_error_invalid_access(self, access_to, access_type, microversion): access = ('foo', {'access': 'bar'}) access_level = 'fake_access_level' share = 'fake_share' version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) manager = shares.ShareManager(api=mock_microversion) with mock.patch.object(manager, '_action', mock.Mock(return_value=access)): self.assertRaises(exceptions.CommandError, manager.allow, share, access_type, access_to, access_level) manager._action.assert_not_called() @ddt.data( ("2.6", "os-deny_access"), ("2.7", "deny_access"), ) @ddt.unpack def test_deny_access_to_share(self, microversion, action_name): access_id = "fake_access_id" share = "fake_share" version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) manager = shares.ShareManager(api=mock_microversion) with mock.patch.object(manager, "_action", mock.Mock(return_value="fake")): result = manager.deny(share, access_id) manager._action.assert_called_once_with( action_name, share, {"access_id": access_id}) self.assertEqual("fake", result) def test_get_metadata(self): cs.shares.get_metadata(1234) cs.assert_called('GET', '/shares/1234/metadata') def test_set_metadata(self): cs.shares.set_metadata(1234, {'k1': 'v2'}) cs.assert_called('POST', '/shares/1234/metadata', {'metadata': {'k1': 'v2'}}) @ddt.data( type('ShareUUID', (object, ), {'uuid': '1234'}), type('ShareID', (object, ), {'id': '1234'}), '1234') def test_delete_metadata(self, share): keys = ['key1'] cs.shares.delete_metadata(share, keys) cs.assert_called('DELETE', '/shares/1234/metadata/key1') @ddt.data( type('ShareUUID', (object, ), {'uuid': '1234'}), type('ShareID', (object, ), {'id': '1234'}), '1234') def test_metadata_update_all(self, share): cs.shares.update_all_metadata(share, {'k1': 'v1'}) cs.assert_called('PUT', '/shares/1234/metadata', {'metadata': {'k1': 'v1'}}) @ddt.data( ("2.6", "os-reset_status"), ("2.7", "reset_status"), ) @ddt.unpack def test_reset_share_state(self, microversion, action_name): state = "available" share = "fake_share" version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) manager = shares.ShareManager(api=mock_microversion) with mock.patch.object(manager, "_action", mock.Mock(return_value="fake")): result = manager.reset_state(share, state) manager._action.assert_called_once_with( action_name, share, {"status": state}) self.assertEqual("fake", result) @ddt.data( ("2.6", "os-extend"), ("2.7", "extend"), ) @ddt.unpack def test_extend_share(self, microversion, action_name): size = 123 share = "fake_share" version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) manager = shares.ShareManager(api=mock_microversion) with mock.patch.object(manager, "_action", mock.Mock(return_value="fake")): result = manager.extend(share, size) manager._action.assert_called_once_with( action_name, share, {"new_size": size}) self.assertEqual("fake", result) @ddt.data( ("2.6", "os-shrink"), ("2.7", "shrink"), ) @ddt.unpack def test_shrink_share(self, microversion, action_name): size = 123 share = "fake_share" version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) manager = shares.ShareManager(api=mock_microversion) with mock.patch.object(manager, "_action", mock.Mock(return_value="fake")): result = manager.shrink(share, size) manager._action.assert_called_once_with( action_name, share, {"new_size": size}) self.assertEqual("fake", result) def test_list_share_instances(self): share = type('ShareID', (object, ), {'id': '1234'}) cs.shares.list_instances(share) cs.assert_called('GET', '/shares/1234/instances') def test_migration_start(self): share = "fake_share" host = "fake_host" version = api_versions.APIVersion('2.29') manager = shares.ShareManager( api=fakes.FakeClient(api_version=version)) with mock.patch.object(manager, "_action", mock.Mock(return_value="fake")): result = manager.migration_start( share, host, force_host_assisted_migration=True, preserve_metadata=True, writable=True, nondisruptive=True, preserve_snapshots=True) manager._action.assert_called_once_with( 'migration_start', share, { "host": host, "force_host_assisted_migration": True, "preserve_metadata": True, "writable": True, "nondisruptive": True, "preserve_snapshots": True, "new_share_network_id": None, "new_share_type_id": None, }) self.assertEqual("fake", result) def test_migration_complete(self): share = "fake_share" manager = shares.ShareManager(api=fakes.FakeClient()) with mock.patch.object(manager, "_action", mock.Mock(return_value="fake")): result = manager.migration_complete(share) manager._action.assert_called_once_with( "migration_complete", share) self.assertEqual("fake", result) def test_migration_get_progress(self): share = "fake_share" manager = shares.ShareManager(api=fakes.FakeClient()) with mock.patch.object(manager, "_action", mock.Mock(return_value="fake")): result = manager.migration_get_progress(share) manager._action.assert_called_once_with( "migration_get_progress", share) self.assertEqual("fake", result) def test_reset_task_state(self): share = "fake_share" state = "fake_state" manager = shares.ShareManager(api=fakes.FakeClient()) with mock.patch.object(manager, "_action", mock.Mock(return_value="fake")): result = manager.reset_task_state(share, state) manager._action.assert_called_once_with( "reset_task_state", share, {'task_state': state}) self.assertEqual("fake", result) def test_migration_cancel(self): share = "fake_share" manager = shares.ShareManager(api=fakes.FakeClient()) with mock.patch.object(manager, "_action", mock.Mock(return_value="fake")): result = manager.migration_cancel(share) manager._action.assert_called_once_with( "migration_cancel", share) self.assertEqual("fake", result) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_services.py0000664000175000017500000000723713644133413026072 0ustar zuulzuul00000000000000# 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. import ddt import mock from manilaclient import api_versions from manilaclient.tests.unit import utils from manilaclient.v2 import services @ddt.ddt class ServicesTest(utils.TestCase): def _get_manager(self, microversion): version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) return services.ServiceManager(api=mock_microversion) def _get_resource_path(self, microversion): if (api_versions.APIVersion(microversion) > api_versions.APIVersion("2.6")): return services.RESOURCE_PATH return services.RESOURCE_PATH_LEGACY @ddt.data("2.6", "2.7") def test_list(self, microversion): manager = self._get_manager(microversion) resource_path = self._get_resource_path(microversion) with mock.patch.object(manager, '_list', mock.Mock(return_value='fake')): result = manager.list() manager._list.assert_called_once_with( resource_path, services.RESOURCE_NAME) self.assertEqual("fake", result) def test_list_services_with_one_search_opt(self): manager = self._get_manager("2.7") host = 'fake_host' query_string = "?host=%s" % host with mock.patch.object(manager, '_list', mock.Mock(return_value=None)): manager.list({'host': host}) manager._list.assert_called_once_with( services.RESOURCE_PATH + query_string, services.RESOURCE_NAME, ) def test_list_services_with_two_search_opts(self): manager = self._get_manager("2.7") host = 'fake_host' binary = 'fake_binary' query_string = "?binary=%s&host=%s" % (binary, host) with mock.patch.object(manager, '_list', mock.Mock(return_value=None)): manager.list({'binary': binary, 'host': host}) manager._list.assert_called_once_with( services.RESOURCE_PATH + query_string, services.RESOURCE_NAME, ) @ddt.data("2.6", "2.7") def test_enable_service(self, microversion): manager = self._get_manager(microversion) host = 'fake_host' binary = 'fake_binary' with mock.patch.object(manager, '_update'): manager.enable(binary=binary, host=host) manager._update.assert_called_once_with( self._get_resource_path(microversion) + '/enable', {"host": host, "binary": binary}, ) @ddt.data("2.6", "2.7") def test_disable_service(self, microversion): manager = self._get_manager(microversion) host = 'fake_host' binary = 'fake_binary' with mock.patch.object(manager, '_update'): manager.disable(binary=binary, host=host) manager._update.assert_called_once_with( self._get_resource_path(microversion) + '/disable', {"host": host, "binary": binary}, ) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_network_subnets.py0000664000175000017500000000570613644133413030664 0ustar zuulzuul00000000000000# 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 import mock from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import share_network_subnets @ddt.ddt class ShareNetworkSubnetTest(utils.TestCase): def setUp(self): super(ShareNetworkSubnetTest, self).setUp() self.manager = share_network_subnets.ShareNetworkSubnetManager( fakes.FakeClient()) def test_create(self): share_network_id = 'fake_share_net_id' expected_url = share_network_subnets.RESOURCES_PATH % { 'share_network_id': share_network_id } expected_values = { 'neutron_net_id': 'fake_net_id', 'neutron_subnet_id': 'fake_subnet_id', 'availability_zone': 'fake_availability_zone', } expected_body = {'share-network-subnet': expected_values} payload = expected_values.copy() payload.update({'share_network_id': share_network_id}) with mock.patch.object(self.manager, '_create', fakes.fake_create): result = self.manager.create(**payload) self.assertEqual(expected_url, result['url']) self.assertEqual( share_network_subnets.RESOURCE_NAME, result['resp_key']) self.assertEqual( expected_body, result['body']) def test_get(self): share_network = 'fake_share_network' share_subnet = 'fake_share_subnet' with mock.patch.object(self.manager, '_get', mock.Mock()): self.manager.get(share_network, share_subnet) self.manager._get.assert_called_once_with( share_network_subnets.RESOURCE_PATH % { 'share_network_id': share_network, 'share_network_subnet_id': share_subnet }, share_network_subnets.RESOURCE_NAME) def test_delete(self): share_network = 'fake_share_network' share_subnet = 'fake_share_subnet' with mock.patch.object(self.manager, '_delete', mock.Mock()): self.manager.delete(share_network, share_subnet) self.manager._delete.assert_called_once_with( share_network_subnets.RESOURCE_PATH % { 'share_network_id': share_network, 'share_network_subnet_id': share_subnet }) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_security_services.py0000664000175000017500000001646013644133413030017 0ustar zuulzuul00000000000000# 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. import mock from manilaclient import exceptions from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import security_services class SecurityServiceTest(utils.TestCase): class _FakeSecurityService(object): id = 'fake_security_service_id' def setUp(self): super(SecurityServiceTest, self).setUp() self.manager = security_services.SecurityServiceManager( fakes.FakeClient()) def test_create_all_fields(self): values = { 'type': 'ldap', 'dns_ip': 'fake dns ip', 'ou': 'fake ou', 'server': 'fake.ldap.server', 'domain': 'fake.ldap.domain', 'user': 'fake user', 'password': 'fake password', 'name': 'fake name', 'description': 'fake description', } with mock.patch.object(self.manager, '_create', fakes.fake_create): result = self.manager.create(**values) self.assertEqual(result['url'], security_services.RESOURCES_PATH) self.assertEqual(result['resp_key'], security_services.RESOURCE_NAME) self.assertIn(security_services.RESOURCE_NAME, result['body']) self.assertEqual(result['body'][security_services.RESOURCE_NAME], values) def test_create_some_fields(self): values = { 'type': 'ldap', 'dns_ip': 'fake dns ip', 'server': 'fake.ldap.server', 'domain': 'fake.ldap.domain', 'user': 'fake user', } with mock.patch.object(self.manager, '_create', fakes.fake_create): result = self.manager.create(**values) self.assertEqual(result['url'], security_services.RESOURCES_PATH) self.assertEqual(result['resp_key'], security_services.RESOURCE_NAME) self.assertIn(security_services.RESOURCE_NAME, result['body']) self.assertEqual(result['body'][security_services.RESOURCE_NAME], values) def test_delete(self): security_service = 'fake service' with mock.patch.object(self.manager, '_delete', mock.Mock()): self.manager.delete(security_service) self.manager._delete.assert_called_once_with( security_services.RESOURCE_PATH % security_service) def test_delete_by_object(self): security_service = self._FakeSecurityService() with mock.patch.object(self.manager, '_delete', mock.Mock()): self.manager.delete(security_service) self.manager._delete.assert_called_once_with( security_services.RESOURCE_PATH % security_service.id) def test_get(self): security_service = 'fake service' with mock.patch.object(self.manager, '_get', mock.Mock()): self.manager.get(security_service) self.manager._get.assert_called_once_with( security_services.RESOURCE_PATH % security_service, security_services.RESOURCE_NAME) def test_get_by_object(self): security_service = self._FakeSecurityService() with mock.patch.object(self.manager, '_get', mock.Mock()): self.manager.get(security_service) self.manager._get.assert_called_once_with( security_services.RESOURCE_PATH % security_service.id, security_services.RESOURCE_NAME) def test_list_summary(self): with mock.patch.object(self.manager, '_list', mock.Mock(return_value=None)): self.manager.list(detailed=False) self.manager._list.assert_called_once_with( security_services.RESOURCES_PATH, security_services.RESOURCES_NAME) def test_list_detail(self): with mock.patch.object(self.manager, '_list', mock.Mock(return_value=None)): self.manager.list(detailed=True) self.manager._list.assert_called_once_with( security_services.RESOURCES_PATH + '/detail', security_services.RESOURCES_NAME) def test_list_no_filters(self): with mock.patch.object(self.manager, '_list', mock.Mock(return_value=None)): self.manager.list() self.manager._list.assert_called_once_with( security_services.RESOURCES_PATH + '/detail', security_services.RESOURCES_NAME) def test_list_with_filters(self): filters = {'all_tenants': 1, 'network': 'fake', 'status': 'ERROR'} expected_postfix = ('/detail?all_tenants=1&network=fake&status=ERROR') with mock.patch.object(self.manager, '_list', mock.Mock(return_value=None)): self.manager.list(search_opts=filters) self.manager._list.assert_called_once_with( security_services.RESOURCES_PATH + expected_postfix, security_services.RESOURCES_NAME) def test_update(self): security_service = 'fake service' values = { 'dns_ip': 'new dns ip', 'ou': 'new ou', 'server': 'new.ldap.server', 'domain': 'new.ldap.domain', 'user': 'new user', 'password': 'fake password', } with mock.patch.object(self.manager, '_update', fakes.fake_update): result = self.manager.update(security_service, **values) self.assertEqual( result['url'], security_services.RESOURCE_PATH % security_service) self.assertEqual( result['resp_key'], security_services.RESOURCE_NAME) self.assertEqual( result['body'][security_services.RESOURCE_NAME], values) def test_update_by_object(self): security_service = self._FakeSecurityService() values = {'user': 'fake_user'} with mock.patch.object(self.manager, '_update', fakes.fake_update): result = self.manager.update(security_service, **values) self.assertEqual( result['url'], security_services.RESOURCE_PATH % security_service.id) self.assertEqual( result['resp_key'], security_services.RESOURCE_NAME) self.assertEqual( result['body'][security_services.RESOURCE_NAME], values) def test_update_no_fields_specified(self): security_service = 'fake service' self.assertRaises(exceptions.CommandError, self.manager.update, security_service) python-manilaclient-2.1.0/manilaclient/tests/unit/v2/test_share_snapshots.py0000664000175000017500000002040713644133413027445 0ustar zuulzuul00000000000000# Copyright 2010 Jacob Kaplan-Moss # 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. import ddt import mock from manilaclient import api_versions from manilaclient import extension from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import share_snapshots extensions = [ extension.Extension('share_snapshots', share_snapshots), ] cs = fakes.FakeClient(extensions=extensions) @ddt.ddt class ShareSnapshotsTest(utils.TestCase): def _get_manager(self, microversion): version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) return share_snapshots.ShareSnapshotManager(api=mock_microversion) def test_create_share_snapshot(self): cs.share_snapshots.create(1234) cs.assert_called('POST', '/snapshots') @ddt.data( type('SnapshotUUID', (object, ), {'uuid': '1234'}), type('SnapshotID', (object, ), {'id': '1234'}), '1234') def test_get_share_snapshot(self, snapshot): snapshot = cs.share_snapshots.get(snapshot) cs.assert_called('GET', '/snapshots/1234') @ddt.data( type('SnapshotUUID', (object, ), {'uuid': '1234'}), type('SnapshotID', (object, ), {'id': '1234'}), '1234') def test_update_share_snapshot(self, snapshot): data = dict(foo='bar', quuz='foobar') snapshot = cs.share_snapshots.update(snapshot, **data) cs.assert_called('PUT', '/snapshots/1234', {'snapshot': data}) @ddt.data( ("2.6", type('SnapshotUUID', (object, ), {'uuid': '1234'})), ("2.7", type('SnapshotUUID', (object, ), {'uuid': '1234'})), ("2.6", type('SnapshotID', (object, ), {'id': '1234'})), ("2.7", type('SnapshotID', (object, ), {'id': '1234'})), ("2.6", "1234"), ("2.7", "1234"), ) @ddt.unpack def test_reset_snapshot_state(self, microversion, snapshot): manager = self._get_manager(microversion) state = 'available' if (api_versions.APIVersion(microversion) > api_versions.APIVersion("2.6")): action_name = "reset_status" else: action_name = "os-reset_status" with mock.patch.object(manager, "_action", mock.Mock()): manager.reset_state(snapshot, state) manager._action.assert_called_once_with( action_name, snapshot, {"status": state}) def test_delete_share_snapshot(self): snapshot = cs.share_snapshots.get(1234) cs.share_snapshots.delete(snapshot) cs.assert_called('DELETE', '/snapshots/1234') @ddt.data( ("2.6", type('SnapshotUUID', (object, ), {"uuid": "1234"})), ("2.6", "1234"), ("2.7", type('SnapshotUUID', (object, ), {"uuid": "1234"})), ("2.7", "1234"), ) @ddt.unpack def test_force_delete_share_snapshot(self, microversion, snapshot): manager = self._get_manager(microversion) if (api_versions.APIVersion(microversion) > api_versions.APIVersion("2.6")): action_name = "force_delete" else: action_name = "os-force_delete" with mock.patch.object(manager, "_action", mock.Mock()): manager.force_delete(snapshot) manager._action.assert_called_once_with(action_name, "1234") def test_list_share_snapshots_index(self): cs.share_snapshots.list(detailed=False) cs.assert_called('GET', '/snapshots') def test_list_share_snapshots_index_with_search_opts(self): search_opts = {'fake_str': 'fake_str_value', 'fake_int': 1} cs.share_snapshots.list(detailed=False, search_opts=search_opts) cs.assert_called( 'GET', '/snapshots?fake_int=1&fake_str=fake_str_value') def test_list_share_snapshots_sort_by_asc_and_share_id(self): cs.share_snapshots.list( detailed=False, sort_key='share_id', sort_dir='asc') cs.assert_called('GET', '/snapshots?sort_dir=asc&sort_key=share_id') def test_list_share_snapshots_sort_by_desc_and_status(self): cs.share_snapshots.list( detailed=False, sort_key='status', sort_dir='desc') cs.assert_called('GET', '/snapshots?sort_dir=desc&sort_key=status') def test_list_share_snapshots_by_improper_direction(self): self.assertRaises(ValueError, cs.share_snapshots.list, sort_dir='fake') def test_list_share_snapshots_by_improper_key(self): self.assertRaises(ValueError, cs.share_snapshots.list, sort_key='fake') def test_list_share_snapshots_detail(self): cs.share_snapshots.list(detailed=True) cs.assert_called('GET', '/snapshots/detail') def test_manage_snapshot(self): share_id = "1234" provider_location = "fake_location" driver_options = {} name = "foo_name" description = "bar_description" expected_body = { "share_id": share_id, "provider_location": provider_location, "driver_options": driver_options, "name": name, "description": description, } version = api_versions.APIVersion("2.12") mock_microversion = mock.Mock(api_version=version) manager = share_snapshots.ShareSnapshotManager(api=mock_microversion) with mock.patch.object(manager, "_create", mock.Mock(return_value="fake")): result = manager.manage(share_id, provider_location, driver_options=driver_options, name=name, description=description) self.assertEqual(manager._create.return_value, result) manager._create.assert_called_once_with( "/snapshots/manage", {"snapshot": expected_body}, "snapshot") def test_unmanage_snapshot(self): snapshot = "fake_snapshot" version = api_versions.APIVersion("2.12") mock_microversion = mock.Mock(api_version=version) manager = share_snapshots.ShareSnapshotManager(api=mock_microversion) with mock.patch.object(manager, "_action", mock.Mock(return_value="fake")): result = manager.unmanage(snapshot) manager._action.assert_called_once_with("unmanage", snapshot) self.assertEqual("fake", result) def test_allow_access(self): snapshot = "fake_snapshot" access_type = "fake_type" access_to = "fake_to" access = ("foo", {"snapshot_access": "fake"}) version = api_versions.APIVersion("2.32") mock_microversion = mock.Mock(api_version=version) manager = share_snapshots.ShareSnapshotManager(api=mock_microversion) with mock.patch.object(manager, "_action", mock.Mock(return_value=access)): result = manager.allow(snapshot, access_type, access_to) self.assertEqual("fake", result) manager._action.assert_called_once_with( "allow_access", snapshot, {'access_type': access_type, 'access_to': access_to}) def test_deny_access(self): snapshot = "fake_snapshot" access_id = "fake_id" version = api_versions.APIVersion("2.32") mock_microversion = mock.Mock(api_version=version) manager = share_snapshots.ShareSnapshotManager(api=mock_microversion) with mock.patch.object(manager, "_action"): manager.deny(snapshot, access_id) manager._action.assert_called_once_with( "deny_access", snapshot, {'access_id': access_id}) def test_access_list(self): cs.share_snapshots.access_list(1234) cs.assert_called('GET', '/snapshots/1234/access-list') python-manilaclient-2.1.0/manilaclient/tests/unit/utils.py0000664000175000017500000000531113644133413024010 0ustar zuulzuul00000000000000# 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 os import fixtures import mock import requests import testtools class TestCase(testtools.TestCase): TEST_REQUEST_BASE = { 'verify': True, } def setUp(self): super(TestCase, self).setUp() if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or os.environ.get('OS_STDOUT_CAPTURE') == '1'): stdout = self.useFixture(fixtures.StringStream('stdout')).stream self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or os.environ.get('OS_STDERR_CAPTURE') == '1'): stderr = self.useFixture(fixtures.StringStream('stderr')).stream self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) def mock_object(self, obj, attr_name, new_attr=None, **kwargs): """Mock an object attribute. Use python mock to mock an object attribute Mocks the specified objects attribute with the given value. Automatically performs 'addCleanup' for the mock. """ if not new_attr: new_attr = mock.Mock() patcher = mock.patch.object(obj, attr_name, new_attr, **kwargs) patcher.start() self.addCleanup(patcher.stop) return new_attr class TestResponse(requests.Response): """Class used to wrap requests.Response. Class used to wrap requests.Response and provide some convenience to initialize with a dict. """ def __init__(self, data): self._text = None super(TestResponse, self) if isinstance(data, dict): self.status_code = data.get('status_code', None) self.headers = data.get('headers', {}) self.headers['x-openstack-request-id'] = data.get( 'x-openstack-request-id', 'fake-request-id') # Fake the text attribute to streamline Response creation self._text = data.get('text', None) else: self.status_code = data self.headers = {'x-openstack-request-id': 'fake-request-id'} def __eq__(self, other): return self.__dict__ == other.__dict__ @property def text(self): return self._text python-manilaclient-2.1.0/manilaclient/tests/unit/common/0000775000175000017500000000000013644133466023576 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/unit/common/test_httpclient.py0000664000175000017500000001770213644133413027364 0ustar zuulzuul00000000000000# 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 mock import re import requests import manilaclient from manilaclient.common import httpclient from manilaclient import exceptions from manilaclient.tests.unit import utils fake_user_agent = "fake" fake_response = utils.TestResponse({ "status_code": 200, "text": '{"hi": "there"}', }) mock_request = mock.Mock(return_value=(fake_response)) bad_400_response = utils.TestResponse({ "status_code": 400, "text": '{"error": {"message": "n/a", "details": "Terrible!"}}', }) bad_400_request = mock.Mock(return_value=(bad_400_response)) bad_401_response = utils.TestResponse({ "status_code": 401, "text": '{"error": {"message": "FAILED!", "details": "DETAILS!"}}', }) bad_401_request = mock.Mock(return_value=(bad_401_response)) bad_500_response = utils.TestResponse({ "status_code": 500, "text": '{"error": {"message": "FAILED!", "details": "DETAILS!"}}', }) bad_500_request = mock.Mock(return_value=(bad_500_response)) retry_after_response = utils.TestResponse({ "status_code": 413, "text": '', "headers": { "retry-after": "5" }, }) retry_after_mock_request = mock.Mock(return_value=retry_after_response) retry_after_no_headers_response = utils.TestResponse({ "status_code": 413, "text": '', }) retry_after_no_headers_mock_request = mock.Mock( return_value=retry_after_no_headers_response) retry_after_non_supporting_response = utils.TestResponse({ "status_code": 403, "text": '', "headers": { "retry-after": "5" }, }) retry_after_non_supporting_mock_request = mock.Mock( return_value=retry_after_non_supporting_response) def get_authed_client(endpoint_url="http://example.com", retries=0): cl = httpclient.HTTPClient(endpoint_url, "token", fake_user_agent, retries=retries, http_log_debug=True, api_version=manilaclient.API_MAX_VERSION) return cl @ddt.ddt class ClientTest(utils.TestCase): def setUp(self): super(ClientTest, self).setUp() self.max_version = manilaclient.API_MAX_VERSION self.max_version_str = self.max_version.get_string() @ddt.data( "http://manila.example.com/v2/b2d18606-2673-4965-885a-4f5a8b955b9b", "http://manila.example.com/v1", "http://manila.example.com/share/v2.22/", "http://manila.example.com/share/v1/" "b2d18606-2673-4965-885a-4f5a8b955b9b", "http://10.10.10.10:3366/v1", "http://10.10.10.10:3366/v2/b2d18606-2673-4965-885a-4f5a8b955b9b", "http://manila.example.com:3366/v1.1/", "http://manila.example.com:3366/v2/" "b2d18606-2673-4965-885a-4f5a8b955b9b") def test_get(self, endpoint_url): cl = get_authed_client(endpoint_url) @mock.patch.object(requests, "request", mock_request) @mock.patch('time.time', mock.Mock(return_value=1234)) def test_get_call(): resp, body = cl.get("/hi") headers = { "X-Auth-Token": "token", "User-Agent": fake_user_agent, cl.API_VERSION_HEADER: self.max_version_str, 'Accept': 'application/json', } mock_request.assert_called_with( "GET", endpoint_url + "/hi", headers=headers, **self.TEST_REQUEST_BASE) # Automatic JSON parsing self.assertEqual(body, {"hi": "there"}) self.assertEqual(re.split(r'/v[0-9]+[\.0-9]*', endpoint_url)[0] + "/", cl.base_url) test_get_call() def test_get_retry_500(self): cl = get_authed_client(retries=1) self.requests = [bad_500_request, mock_request] def request(*args, **kwargs): next_request = self.requests.pop(0) return next_request(*args, **kwargs) @mock.patch.object(requests, "request", request) @mock.patch('time.time', mock.Mock(return_value=1234)) def test_get_call(): resp, body = cl.get("/hi") test_get_call() self.assertEqual(self.requests, []) def test_retry_limit(self): cl = get_authed_client(retries=1) self.requests = [bad_500_request, bad_500_request, mock_request] def request(*args, **kwargs): next_request = self.requests.pop(0) return next_request(*args, **kwargs) @mock.patch.object(requests, "request", request) @mock.patch('time.time', mock.Mock(return_value=1234)) def test_get_call(): resp, body = cl.get("/hi") self.assertRaises(exceptions.ClientException, test_get_call) self.assertEqual(self.requests, [mock_request]) def test_get_no_retry_400(self): cl = get_authed_client(retries=0) self.requests = [bad_400_request, mock_request] def request(*args, **kwargs): next_request = self.requests.pop(0) return next_request(*args, **kwargs) @mock.patch.object(requests, "request", request) @mock.patch('time.time', mock.Mock(return_value=1234)) def test_get_call(): resp, body = cl.get("/hi") self.assertRaises(exceptions.BadRequest, test_get_call) self.assertEqual(self.requests, [mock_request]) def test_get_retry_400_socket(self): cl = get_authed_client(retries=1) self.requests = [bad_400_request, mock_request] def request(*args, **kwargs): next_request = self.requests.pop(0) return next_request(*args, **kwargs) @mock.patch.object(requests, "request", request) @mock.patch('time.time', mock.Mock(return_value=1234)) def test_get_call(): resp, body = cl.get("/hi") test_get_call() self.assertEqual(self.requests, []) def test_get_with_retries_none(self): cl = get_authed_client(retries=None) @mock.patch.object(requests, "request", bad_401_request) def test_get_call(): resp, body = cl.get("/hi") self.assertRaises(exceptions.Unauthorized, test_get_call) @ddt.data( "http://manila.example.com/v1/b2d18606-2673-4965-885a-4f5a8b955b9b", "http://manila.example.com/v1", "http://manila.example.com/share/v2.1/", "http://manila.example.com/share/v1/" "b2d18606-2673-4965-885a-4f5a8b955b9b", "http://10.10.10.10:3366/v1.1", "http://10.10.10.10:3366/v2/b2d18606-2673-4965-885a-4f5a8b955b9b", "http://manila.example.com:3366/v2.22/", "http://manila.example.com:3366/v1/" "b2d18606-2673-4965-885a-4f5a8b955b9b") def test_post(self, endpoint_url): cl = get_authed_client(endpoint_url) @mock.patch.object(requests, "request", mock_request) def test_post_call(): cl.post("/hi", body=[1, 2, 3]) headers = { "X-Auth-Token": "token", "Content-Type": "application/json", 'Accept': 'application/json', "X-Openstack-Manila-Api-Version": self.max_version_str, "User-Agent": fake_user_agent } mock_request.assert_called_with( "POST", endpoint_url + "/hi", headers=headers, data='[1, 2, 3]', **self.TEST_REQUEST_BASE) self.assertEqual(re.split(r'/v[0-9]+[\.0-9]*', endpoint_url)[0] + "/", cl.base_url) test_post_call() python-manilaclient-2.1.0/manilaclient/tests/unit/common/__init__.py0000664000175000017500000000000013644133413025665 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/unit/common/apiclient/0000775000175000017500000000000013644133466025546 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/unit/common/apiclient/__init__.py0000664000175000017500000000000013644133413027635 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/unit/test_base.py0000664000175000017500000000532213644133413024623 0ustar zuulzuul00000000000000# 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 mock import mock from manilaclient.common.apiclient import base as common_base from manilaclient import exceptions from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import shares cs = fakes.FakeClient() class BaseTest(utils.TestCase): def test_resource_repr(self): r = common_base.Resource(None, dict(foo="bar", baz="spam")) self.assertEqual(repr(r), "") def test_eq(self): # Two resources of the same type with the same id: equal # The truth of r1==r2 does not imply that r1!=r2 is false in PY2. # Test that inequality operator is defined and that comparing equal # items returns False. r1 = common_base.Resource(None, {'id': 1, 'name': 'hi'}) r2 = common_base.Resource(None, {'id': 1, 'name': 'hello'}) self.assertTrue(r1 == r2) self.assertFalse(r1 != r2) # Two resources of different types: never equal r1 = common_base.Resource(None, {'id': 1}) r2 = shares.Share(None, {'id': 1}) self.assertNotEqual(r1, r2) self.assertTrue(r1 != r2) # Two resources with no ID: equal if their info is equal # The truth of r1==r2 does not imply that r1!=r2 is false in PY2. # Test that inequality operator is defined and that comparing equal # items returns False. r1 = common_base.Resource(None, {'name': 'joe', 'age': 12}) r2 = common_base.Resource(None, {'name': 'joe', 'age': 12}) self.assertTrue(r1 == r2) self.assertFalse(r1 != r2) def test_findall_invalid_attribute(self): # Make sure findall with an invalid attribute doesn't cause errors. # The following should not raise an exception. cs.shares.findall(vegetable='carrot') # However, find() should raise an error self.assertRaises(exceptions.NotFound, cs.shares.find, vegetable='carrot') def test_findall_with_all_tenants(self): cs.shares.list = mock.Mock(return_value=[]) cs.shares.findall() cs.shares.list.assert_called_once_with(search_opts={'all_tenants': 1}) python-manilaclient-2.1.0/manilaclient/tests/unit/test_api_versions.py0000664000175000017500000003510113644133413026410 0ustar zuulzuul00000000000000# Copyright 2015 Chuck Fouts # # 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 mock import six import manilaclient from manilaclient import api_versions from manilaclient.common import cliutils from manilaclient import exceptions from manilaclient.tests.unit import utils @ddt.ddt class APIVersionTestCase(utils.TestCase): def test_valid_version_strings(self): def _test_string(version, exp_major, exp_minor): v = api_versions.APIVersion(version) self.assertEqual(v.ver_major, exp_major) self.assertEqual(v.ver_minor, exp_minor) _test_string("1.1", 1, 1) _test_string("2.10", 2, 10) _test_string("5.234", 5, 234) _test_string("12.5", 12, 5) _test_string("2.0", 2, 0) _test_string("2.200", 2, 200) def test_null_version(self): v = api_versions.APIVersion() self.assertTrue(v.is_null()) self.assertEqual(repr(v), "") @ddt.data( "2", "200", "2.1.4", "200.23.66.3", "5 .3", "5. 3", "5.03", "02.1", "2.001", "", " 2.1", "2.1 ", "2.", ) def test_invalid_version_strings(self, version): self.assertRaises(exceptions.UnsupportedVersion, api_versions.APIVersion, version) def test_version_comparisons(self): v1 = api_versions.APIVersion("2.0") v2 = api_versions.APIVersion("2.5") v3 = api_versions.APIVersion("5.23") v4 = api_versions.APIVersion("2.0") v5 = api_versions.APIVersion("1.0") v_null = api_versions.APIVersion() self.assertLess(v1, v2) self.assertGreater(v3, v2) self.assertNotEqual(v1, v2) self.assertEqual(v1, v4) self.assertNotEqual(v1, v_null) self.assertLess(v5, v1) self.assertLess(v5, v2) self.assertEqual(v_null, v_null) self.assertRaises(TypeError, v1.__le__, "2.1") self.assertRaises(TypeError, v1.__eq__, "2.1") self.assertRaises(TypeError, v1.__gt__, "2.1") def test_version_matches(self): v1 = api_versions.APIVersion("2.0") v2 = api_versions.APIVersion("2.5") v3 = api_versions.APIVersion("2.45") v4 = api_versions.APIVersion("3.3") v5 = api_versions.APIVersion("3.23") v6 = api_versions.APIVersion("2.0") v7 = api_versions.APIVersion("3.3") v8 = api_versions.APIVersion("4.0") v_null = api_versions.APIVersion() v1_25 = api_versions.APIVersion("2.5") v1_32 = api_versions.APIVersion("3.32") v1_33 = api_versions.APIVersion("3.33") self.assertTrue(v2.matches(v1, v3)) self.assertTrue(v2.matches(v1, v_null)) self.assertTrue(v1_32.matches(v1_25, v1_33)) self.assertTrue(v1.matches(v6, v2)) self.assertTrue(v4.matches(v2, v7)) self.assertTrue(v4.matches(v_null, v7)) self.assertTrue(v4.matches(v_null, v8)) self.assertFalse(v1.matches(v2, v3)) self.assertFalse(v5.matches(v2, v4)) self.assertFalse(v2.matches(v3, v1)) self.assertRaises(ValueError, v_null.matches, v1, v3) def test_get_string(self): v1_string = "3.23" v1 = api_versions.APIVersion(v1_string) self.assertEqual(v1_string, v1.get_string()) self.assertRaises(ValueError, api_versions.APIVersion().get_string) @ddt.data("2.0", "2.5", "2.45", "3.3", "3.23", "2.0", "3.3", "4.0") def test_representation(self, version): version_major, version_minor = version.split('.') api_version = api_versions.APIVersion(version) self.assertEqual(six.text_type(api_version), ("API Version Major: %s, Minor: %s" % (version_major, version_minor))) self.assertEqual(repr(api_version), "" % version) def test_is_latest(self): v1 = api_versions.APIVersion("1.0") self.assertFalse(v1.is_latest()) v_latest = api_versions.APIVersion(api_versions.MAX_VERSION) self.assertTrue(v_latest.is_latest()) class GetAPIVersionTestCase(utils.TestCase): def test_wrong_format(self): self.assertRaises(exceptions.UnsupportedVersion, api_versions.get_api_version, "something_wrong") def test_wrong_major_version(self): self.assertRaises(exceptions.UnsupportedVersion, api_versions.get_api_version, "1") @mock.patch("manilaclient.api_versions.APIVersion") def test_major_and_minor_parts_is_presented(self, mock_apiversion): version = "2.7" self.assertEqual(mock_apiversion.return_value, api_versions.get_api_version(version)) mock_apiversion.assert_called_once_with(version) class WrapsTestCase(utils.TestCase): def _get_obj_with_vers(self, vers): return mock.MagicMock(api_version=api_versions.APIVersion(vers)) def _side_effect_of_vers_method(self, *args, **kwargs): m = mock.MagicMock(start_version=args[1], end_version=args[2]) m.name = args[0] return m @mock.patch("manilaclient.utils.get_function_name") @mock.patch("manilaclient.api_versions.VersionedMethod") def test_end_version_is_none(self, mock_versioned_method, mock_name): func_name = 'foo' mock_name.return_value = func_name mock_versioned_method.side_effect = self._side_effect_of_vers_method @api_versions.wraps('2.2') def foo(*args, **kwargs): pass foo(self._get_obj_with_vers('2.4')) mock_versioned_method.assert_called_once_with( func_name, api_versions.APIVersion('2.2'), api_versions.APIVersion(api_versions.MAX_VERSION), mock.ANY) @mock.patch("manilaclient.utils.get_function_name") @mock.patch("manilaclient.api_versions.VersionedMethod") def test_start_and_end_version_are_presented(self, mock_versioned_method, mock_name): func_name = "foo" mock_name.return_value = func_name mock_versioned_method.side_effect = self._side_effect_of_vers_method @api_versions.wraps("2.2", "2.6") def foo(*args, **kwargs): pass foo(self._get_obj_with_vers("2.4")) mock_versioned_method.assert_called_once_with( func_name, api_versions.APIVersion("2.2"), api_versions.APIVersion("2.6"), mock.ANY) @mock.patch("manilaclient.utils.get_function_name") @mock.patch("manilaclient.api_versions.VersionedMethod") def test_api_version_doesnt_match(self, mock_versioned_method, mock_name): func_name = "foo" mock_name.return_value = func_name mock_versioned_method.side_effect = self._side_effect_of_vers_method @api_versions.wraps("2.2", "2.6") def foo(*args, **kwargs): pass self.assertRaises(exceptions.UnsupportedVersion, foo, self._get_obj_with_vers("2.1")) mock_versioned_method.assert_called_once_with( func_name, api_versions.APIVersion("2.2"), api_versions.APIVersion("2.6"), mock.ANY) def test_define_method_is_actually_called(self): checker = mock.MagicMock() @api_versions.wraps("2.2", "2.6") def some_func(*args, **kwargs): checker(*args, **kwargs) obj = self._get_obj_with_vers("2.4") some_args = ("arg_1", "arg_2") some_kwargs = {"key1": "value1", "key2": "value2"} some_func(obj, *some_args, **some_kwargs) checker.assert_called_once_with(*((obj,) + some_args), **some_kwargs) def test_cli_args_are_copied(self): @api_versions.wraps("2.2", "2.6") @cliutils.arg("name_1", help="Name of the something") @cliutils.arg("action_1", help="Some action") def some_func_1(cs, args): pass @cliutils.arg("name_2", help="Name of the something") @cliutils.arg("action_2", help="Some action") @api_versions.wraps("2.2", "2.6") def some_func_2(cs, args): pass args_1 = [(('name_1',), {'help': 'Name of the something'}), (('action_1',), {'help': 'Some action'})] self.assertEqual(args_1, some_func_1.arguments) args_2 = [(('name_2',), {'help': 'Name of the something'}), (('action_2',), {'help': 'Some action'})] self.assertEqual(args_2, some_func_2.arguments) class DiscoverVersionTestCase(utils.TestCase): def setUp(self): super(DiscoverVersionTestCase, self).setUp() self.orig_max = manilaclient.API_MAX_VERSION self.orig_min = manilaclient.API_MIN_VERSION self.addCleanup(self._clear_fake_version) self.fake_client = mock.MagicMock() def _clear_fake_version(self): manilaclient.API_MAX_VERSION = self.orig_max manilaclient.API_MIN_VERSION = self.orig_min def _mock_returned_server_version(self, server_version, server_min_version): version_mock = mock.MagicMock(version=server_version, min_version=server_min_version, status='CURRENT') val = [version_mock] self.fake_client.services.server_api_version.return_value = val def test_server_is_too_new(self): self._mock_returned_server_version('2.7', '2.4') manilaclient.API_MAX_VERSION = api_versions.APIVersion("2.3") manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.1") self.assertRaisesRegex(exceptions.UnsupportedVersion, ".*range is '2.4' to '2.7'.*", api_versions.discover_version, self.fake_client, api_versions.APIVersion("2.3")) self.assertTrue(self.fake_client.services.server_api_version.called) def test_server_is_too_old(self): self._mock_returned_server_version('2.2', '2.0') manilaclient.API_MAX_VERSION = api_versions.APIVersion("2.10") manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.9") self.assertRaises(exceptions.UnsupportedVersion, api_versions.discover_version, self.fake_client, api_versions.APIVersion("2.10")) self.assertTrue(self.fake_client.services.server_api_version.called) def test_requested_version_is_less_than_server_max(self): self._mock_returned_server_version('2.17', '2.14') max_version = api_versions.APIVersion('2.15') manilaclient.API_MAX_VERSION = max_version manilaclient.API_MIN_VERSION = api_versions.APIVersion('2.12') version = api_versions.discover_version(self.fake_client, max_version) self.assertEqual(api_versions.APIVersion('2.15'), version) def test_requested_version_is_downgraded(self): server_end_version = '2.7' self._mock_returned_server_version(server_end_version, '2.0') max_version = api_versions.APIVersion("2.8") manilaclient.API_MAX_VERSION = max_version manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.5") version = api_versions.discover_version(self.fake_client, max_version) self.assertEqual(api_versions.APIVersion(server_end_version), version) def test_server_and_client_max_are_same(self): self._mock_returned_server_version('2.5', '2.0') manilaclient.API_MAX_VERSION = api_versions.APIVersion("2.5") manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.5") discovered_version = api_versions.discover_version( self.fake_client, manilaclient.API_MAX_VERSION) self.assertEqual("2.5", discovered_version.get_string()) self.assertTrue(self.fake_client.services.server_api_version.called) def test_pre_microversion_server(self): self.fake_client.services.server_api_version.return_value = [] manilaclient.API_MAX_VERSION = api_versions.APIVersion("2.5") manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.5") discovered_version = api_versions.discover_version( self.fake_client, manilaclient.API_MAX_VERSION) self.assertEqual("1.0", discovered_version.get_string()) self.assertTrue(self.fake_client.services.server_api_version.called) def test_requested_version_in_range(self): self._mock_returned_server_version('2.7', '2.4') manilaclient.API_MAX_VERSION = api_versions.APIVersion("2.11") manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.1") discovered_version = api_versions.discover_version( self.fake_client, api_versions.APIVersion('2.7')) self.assertEqual('2.7', discovered_version.get_string()) self.assertTrue(self.fake_client.services.server_api_version.called) def test_server_without_microversion(self): self._mock_returned_server_version(None, None) manilaclient.API_MAX_VERSION = api_versions.APIVersion("2.11") manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.1") discovered_version = api_versions.discover_version( self.fake_client, api_versions.APIVersion('2.7')) self.assertEqual(api_versions.DEPRECATED_VERSION, discovered_version.get_string()) self.assertTrue(self.fake_client.services.server_api_version.called) def test_requested_version_is_too_old(self): self._mock_returned_server_version('2.5', '2.0') manilaclient.API_MAX_VERSION = api_versions.APIVersion("2.5") manilaclient.API_MIN_VERSION = api_versions.APIVersion("2.5") self.assertRaisesRegex(exceptions.UnsupportedVersion, ".*range is '2.0' to '2.5'.*", api_versions.discover_version, self.fake_client, api_versions.APIVersion("1.0")) python-manilaclient-2.1.0/manilaclient/tests/unit/test_client.py0000664000175000017500000001225213644133413025167 0ustar zuulzuul00000000000000# 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 mock from manilaclient import api_versions from manilaclient import client from manilaclient import exceptions from manilaclient.tests.unit import utils import manilaclient.v1.client import manilaclient.v2.client @ddt.ddt class ClientTest(utils.TestCase): def test_get_client_class_v2(self): output = manilaclient.client.get_client_class('2') self.assertEqual(output, manilaclient.v2.client.Client) def test_get_client_class_unknown(self): self.assertRaises(manilaclient.exceptions.UnsupportedVersion, manilaclient.client.get_client_class, '0') @ddt.data('1', '1.0') def test_init_client_with_string_v1_version(self, version): with mock.patch.object(manilaclient.v1.client, 'Client'): with mock.patch.object(api_versions, 'APIVersion'): api_instance = api_versions.APIVersion.return_value api_instance.get_major_version.return_value = '1' manilaclient.client.Client(version, 'foo', bar='quuz') manilaclient.v1.client.Client.assert_called_once_with( 'foo', api_version=api_instance, bar='quuz') api_versions.APIVersion.assert_called_once_with('1.0') @ddt.data( ('2', '2.0'), ('2.0', '2.0'), ('2.6', '2.6'), ) @ddt.unpack def test_init_client_with_string_v2_version(self, provided, expected): with mock.patch.object(manilaclient.v2.client, 'Client'): with mock.patch.object(api_versions, 'APIVersion'): api_instance = api_versions.APIVersion.return_value api_instance.get_major_version.return_value = '2' manilaclient.client.Client(provided, 'foo', bar='quuz') manilaclient.v2.client.Client.assert_called_once_with( 'foo', api_version=api_instance, bar='quuz') api_versions.APIVersion.assert_called_once_with(expected) def test_init_client_with_api_version_instance(self): version = manilaclient.API_MAX_VERSION with mock.patch.object(manilaclient.v2.client, 'Client'): manilaclient.client.Client(version, 'foo', bar='quuz') manilaclient.v2.client.Client.assert_called_once_with( 'foo', api_version=version, bar='quuz') @ddt.data(None, '', '3', 'v1', 'v2', 'v1.0', 'v2.0') def test_init_client_with_unsupported_version(self, v): self.assertRaises(exceptions.UnsupportedVersion, client.Client, v) @ddt.data( ('1', '1.0'), ('1', '2.0'), ('1', '2.7'), ('1', None), ('1.0', '1.0'), ('1.0', '2.0'), ('1.0', '2.7'), ('1.0', None), ('2', '1.0'), ('2', '2.0'), ('2', '2.7'), ('2', None), ) @ddt.unpack def test_init_client_with_version_parms(self, pos, kw): major = int(float(pos)) pos_av = mock.Mock() kw_av = mock.Mock() with mock.patch.object(manilaclient.v1.client, 'Client'): with mock.patch.object(manilaclient.v2.client, 'Client'): with mock.patch.object(api_versions, 'APIVersion'): api_versions.APIVersion.side_effect = [pos_av, kw_av] pos_av.get_major_version.return_value = str(major) if kw is None: manilaclient.client.Client(pos, 'foo') expected_av = pos_av else: manilaclient.client.Client(pos, 'foo', api_version=kw) expected_av = kw_av if int(float(pos)) == 1: expected_client_ver = api_versions.DEPRECATED_VERSION self.assertFalse(manilaclient.v2.client.Client.called) manilaclient.v1.client.Client.assert_has_calls([ mock.call('foo', api_version=expected_av) ]) else: expected_client_ver = api_versions.MIN_VERSION self.assertFalse(manilaclient.v1.client.Client.called) manilaclient.v2.client.Client.assert_has_calls([ mock.call('foo', api_version=expected_av) ]) if kw is None: api_versions.APIVersion.assert_called_once_with( expected_client_ver) else: api_versions.APIVersion.assert_has_calls([ mock.call(expected_client_ver), mock.call(kw), ]) python-manilaclient-2.1.0/manilaclient/tests/unit/v1/0000775000175000017500000000000013644133466022634 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/unit/v1/test_scheduler_stats.py0000664000175000017500000000231413644133413027431 0ustar zuulzuul00000000000000# 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 six from manilaclient.tests.unit import utils class SchedulerStatsV1Test(utils.TestCase): def test_import_v1_scheduler_stats_module(self): try: from manilaclient.v1 import scheduler_stats except Exception as e: msg = ("module 'manilaclient.v1.scheduler_stats' cannot be " "imported with error: %s") % six.text_type(e) assert False, msg for cls in ('Pool', 'PoolManager'): msg = "Module 'scheduler_stats' has no '%s' attr." % cls self.assertTrue(hasattr(scheduler_stats, cls), msg) python-manilaclient-2.1.0/manilaclient/tests/unit/v1/test_quotas.py0000664000175000017500000000223713644133413025555 0ustar zuulzuul00000000000000# 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 six from manilaclient.tests.unit import utils class QuotasV1Test(utils.TestCase): def test_import_v1_quotas_module(self): try: from manilaclient.v1 import quotas except Exception as e: msg = ("module 'manilaclient.v1.quotas' cannot be imported " "with error: %s") % six.text_type(e) assert False, msg for cls in ('QuotaSet', 'QuotaSetManager'): msg = "Module 'quotas' has no '%s' attr." % cls self.assertTrue(hasattr(quotas, cls), msg) python-manilaclient-2.1.0/manilaclient/tests/unit/v1/test_share_type_access.py0000664000175000017500000000235513644133413027726 0ustar zuulzuul00000000000000# 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 six from manilaclient.tests.unit import utils class ShareTypeAccessV1Test(utils.TestCase): def test_import_v1_share_type_access_module(self): try: from manilaclient.v1 import share_type_access except Exception as e: msg = ("module 'manilaclient.v1.share_type_access' cannot be " "imported with error: %s") % six.text_type(e) assert False, msg for cls in ('ShareTypeAccess', 'ShareTypeAccessManager'): msg = "Module 'share_type_access' has no '%s' attr." % cls self.assertTrue(hasattr(share_type_access, cls), msg) python-manilaclient-2.1.0/manilaclient/tests/unit/v1/test_share_types.py0000664000175000017500000000227613644133413026572 0ustar zuulzuul00000000000000# 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 six from manilaclient.tests.unit import utils class ShareTypesV1Test(utils.TestCase): def test_import_v1_share_types_module(self): try: from manilaclient.v1 import share_types except Exception as e: msg = ("module 'manilaclient.v1.share_types' cannot be imported " "with error: %s") % six.text_type(e) assert False, msg for cls in ('ShareType', 'ShareTypeManager'): msg = "Module 'share_types' has no '%s' attr." % cls self.assertTrue(hasattr(share_types, cls), msg) python-manilaclient-2.1.0/manilaclient/tests/unit/v1/test_quota_classes.py0000664000175000017500000000232213644133413027102 0ustar zuulzuul00000000000000# 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 six from manilaclient.tests.unit import utils class QuotaClassesV1Test(utils.TestCase): def test_import_v1_quota_classes_module(self): try: from manilaclient.v1 import quota_classes except Exception as e: msg = ("module 'manilaclient.v1.quota_classes' cannot be imported " "with error: %s") % six.text_type(e) assert False, msg for cls in ('QuotaClassSet', 'QuotaClassSetManager'): msg = "Module 'quota_classes' has no '%s' attr." % cls self.assertTrue(hasattr(quota_classes, cls), msg) python-manilaclient-2.1.0/manilaclient/tests/unit/v1/test_limits.py0000664000175000017500000000227113644133413025540 0ustar zuulzuul00000000000000# 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 six from manilaclient.tests.unit import utils class LimitsV1Test(utils.TestCase): def test_import_v1_limits_module(self): try: from manilaclient.v1 import limits except Exception as e: msg = ("module 'manilaclient.v1.limits' cannot be imported " "with error: %s") % six.text_type(e) assert False, msg for cls in ('Limits', 'RateLimit', 'AbsoluteLimit', 'LimitsManager'): msg = "Module 'limits' has no '%s' attr." % cls self.assertTrue(hasattr(limits, cls), msg) python-manilaclient-2.1.0/manilaclient/tests/unit/v1/test_share_networks.py0000664000175000017500000000232613644133413027276 0ustar zuulzuul00000000000000# 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 six from manilaclient.tests.unit import utils class ShareNetworksV1Test(utils.TestCase): def test_import_v1_share_networks_module(self): try: from manilaclient.v1 import share_networks except Exception as e: msg = ("module 'manilaclient.v1.share_networks' cannot be " "imported with error: %s") % six.text_type(e) assert False, msg for cls in ('ShareNetwork', 'ShareNetworkManager'): msg = "Module 'share_networks' has no '%s' attr." % cls self.assertTrue(hasattr(share_networks, cls), msg) python-manilaclient-2.1.0/manilaclient/tests/unit/v1/test_share_servers.py0000664000175000017500000000231613644133413027112 0ustar zuulzuul00000000000000# 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 six from manilaclient.tests.unit import utils class ShareServersV1Test(utils.TestCase): def test_import_v1_share_servers_module(self): try: from manilaclient.v1 import share_servers except Exception as e: msg = ("module 'manilaclient.v1.share_servers' cannot be imported " "with error: %s") % six.text_type(e) assert False, msg for cls in ('ShareServer', 'ShareServerManager'): msg = "Module 'share_servers' has no '%s' attr." % cls self.assertTrue(hasattr(share_servers, cls), msg) python-manilaclient-2.1.0/manilaclient/tests/unit/v1/test_shares.py0000664000175000017500000000223113644133413025520 0ustar zuulzuul00000000000000# 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 six from manilaclient.tests.unit import utils class SharesV1Test(utils.TestCase): def test_import_v1_shares_module(self): try: from manilaclient.v1 import shares except Exception as e: msg = ("module 'manilaclient.v1.shares' cannot be imported " "with error: %s") % six.text_type(e) assert False, msg for cls in ('Share', 'ShareManager'): msg = "Module 'shares' has no '%s' attr." % cls self.assertTrue(hasattr(shares, cls), msg) python-manilaclient-2.1.0/manilaclient/tests/unit/v1/test_services.py0000664000175000017500000000225113644133413026060 0ustar zuulzuul00000000000000# 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 six from manilaclient.tests.unit import utils class ServicesV1Test(utils.TestCase): def test_import_v1_services_module(self): try: from manilaclient.v1 import services except Exception as e: msg = ("module 'manilaclient.v1.services' cannot be imported " "with error: %s") % six.text_type(e) assert False, msg for cls in ('Service', 'ServiceManager'): msg = "Module 'services' has no '%s' attr." % cls self.assertTrue(hasattr(services, cls), msg) python-manilaclient-2.1.0/manilaclient/tests/unit/v1/test_security_services.py0000664000175000017500000000235613644133413030015 0ustar zuulzuul00000000000000# 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 six from manilaclient.tests.unit import utils class SecurityServicesV1Test(utils.TestCase): def test_import_v1_security_services_module(self): try: from manilaclient.v1 import security_services except Exception as e: msg = ("module 'manilaclient.v1.security_services' cannot be " "imported with error: %s") % six.text_type(e) assert False, msg for cls in ('SecurityService', 'SecurityServiceManager'): msg = "Module 'security_services' has no '%s' attr." % cls self.assertTrue(hasattr(security_services, cls), msg) python-manilaclient-2.1.0/manilaclient/tests/unit/v1/test_share_snapshots.py0000664000175000017500000000233613644133413027445 0ustar zuulzuul00000000000000# 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 six from manilaclient.tests.unit import utils class ShareSnapshotsV1Test(utils.TestCase): def test_import_v1_share_snapshots_module(self): try: from manilaclient.v1 import share_snapshots except Exception as e: msg = ("module 'manilaclient.v1.share_snapshots' cannot be " "imported with error: %s") % six.text_type(e) assert False, msg for cls in ('ShareSnapshot', 'ShareSnapshotManager'): msg = "Module 'share_snapshots' has no '%s' attr." % cls self.assertTrue(hasattr(share_snapshots, cls), msg) python-manilaclient-2.1.0/manilaclient/tests/unit/__init__.py0000664000175000017500000000000013644133413024375 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/manilaclient/tests/__init__.py0000664000175000017500000000000013644133413023416 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/setup.py0000664000175000017500000000127113644133413017230 0ustar zuulzuul00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # 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 setuptools setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) python-manilaclient-2.1.0/requirements.txt0000664000175000017500000000120313644133413020775 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. # pbr should be first pbr!=2.1.0,>=2.0.0 # Apache-2.0 oslo.config>=5.2.0 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 PrettyTable<0.8,>=0.7.1 # BSD requests>=2.14.2 # Apache-2.0 simplejson>=3.5.1 # MIT Babel!=2.4.0,>=2.3.4 # BSD six>=1.10.0 # MIT osc-lib>=1.10.0 # Apache-2.0 python-keystoneclient>=3.8.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 python-manilaclient-2.1.0/contrib/0000775000175000017500000000000013644133466017165 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/contrib/ci/0000775000175000017500000000000013644133466017560 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/contrib/ci/post_test_hook.sh0000775000175000017500000001024413644133413023154 0ustar zuulzuul00000000000000#!/bin/bash -xe # # 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. # This script is executed inside post_test_hook function in devstack gate. export MANILACLIENT_DIR="$BASE/new/python-manilaclient" export MANILACLIENT_CONF="$MANILACLIENT_DIR/etc/manilaclient/manilaclient.conf" # Go to the manilaclient dir cd $MANILACLIENT_DIR # Give permissions sudo chown -R $USER:stack . # Create manilaclient config file touch $MANILACLIENT_CONF # Import functions from devstack source $BASE/new/devstack/functions env | grep OS_ # Set options to config client. source $BASE/new/devstack/openrc demo demo env | grep OS_ export OS_TENANT_NAME=${OS_PROJECT_NAME:-$OS_TENANT_NAME} iniset $MANILACLIENT_CONF DEFAULT username $OS_USERNAME iniset $MANILACLIENT_CONF DEFAULT tenant_name $OS_TENANT_NAME iniset $MANILACLIENT_CONF DEFAULT password $OS_PASSWORD iniset $MANILACLIENT_CONF DEFAULT auth_url $OS_AUTH_URL iniset $MANILACLIENT_CONF DEFAULT project_domain_name $OS_PROJECT_DOMAIN_NAME iniset $MANILACLIENT_CONF DEFAULT user_domain_name $OS_USER_DOMAIN_NAME iniset $MANILACLIENT_CONF DEFAULT project_domain_id $OS_PROJECT_DOMAIN_ID iniset $MANILACLIENT_CONF DEFAULT user_domain_id $OS_USER_DOMAIN_ID source $BASE/new/devstack/openrc admin demo env | grep OS_ export OS_TENANT_NAME=${OS_PROJECT_NAME:-$OS_TENANT_NAME} iniset $MANILACLIENT_CONF DEFAULT admin_username $OS_USERNAME iniset $MANILACLIENT_CONF DEFAULT admin_tenant_name $OS_TENANT_NAME iniset $MANILACLIENT_CONF DEFAULT admin_password $OS_PASSWORD iniset $MANILACLIENT_CONF DEFAULT admin_auth_url $OS_AUTH_URL iniset $MANILACLIENT_CONF DEFAULT admin_project_domain_name $OS_PROJECT_DOMAIN_NAME iniset $MANILACLIENT_CONF DEFAULT admin_user_domain_name $OS_USER_DOMAIN_NAME iniset $MANILACLIENT_CONF DEFAULT admin_project_domain_id $OS_PROJECT_DOMAIN_ID iniset $MANILACLIENT_CONF DEFAULT admin_user_domain_id $OS_USER_DOMAIN_ID # Suppress errors in cleanup of resources SUPPRESS_ERRORS=${SUPPRESS_ERRORS_IN_CLEANUP:-True} iniset $MANILACLIENT_CONF DEFAULT suppress_errors_in_cleanup $SUPPRESS_ERRORS # Set access type usage specific to dummy driver that we are using in CI iniset $MANILACLIENT_CONF DEFAULT access_types_mapping "nfs:ip,cifs:user" # Dummy driver is capable of running share migration tests iniset $MANILACLIENT_CONF DEFAULT run_migration_tests "True" # Dummy driver is capable of running share manage tests iniset $MANILACLIENT_CONF DEFAULT run_manage_tests "True" # Running mountable snapshot tests in dummy driver iniset $MANILACLIENT_CONF DEFAULT run_mount_snapshot_tests "True" # Create share network and use it for functional tests if required USE_SHARE_NETWORK=$(trueorfalse True USE_SHARE_NETWORK) if [[ ${USE_SHARE_NETWORK} = True ]]; then SHARE_NETWORK_NAME=${SHARE_NETWORK_NAME:-ci} DEFAULT_NEUTRON_NET=$(openstack network show private -c id -f value) DEFAULT_NEUTRON_SUBNET=$(openstack subnet show private-subnet -c id -f value) NEUTRON_NET=${NEUTRON_NET:-$DEFAULT_NEUTRON_NET} NEUTRON_SUBNET=${NEUTRON_SUBNET:-$DEFAULT_NEUTRON_SUBNET} manila share-network-create \ --name $SHARE_NETWORK_NAME \ --neutron-net $NEUTRON_NET \ --neutron-subnet $NEUTRON_SUBNET iniset $MANILACLIENT_CONF DEFAULT share_network $SHARE_NETWORK_NAME iniset $MANILACLIENT_CONF DEFAULT admin_share_network $SHARE_NETWORK_NAME fi # Set share type if required if [[ "$SHARE_TYPE" ]]; then iniset $MANILACLIENT_CONF DEFAULT share_type $SHARE_TYPE fi # let us control if we die or not set +o errexit CONCURRENCY=${CONCURRENCY:-8} # Run functional tests sudo -H -u $USER tox -e functional -v -- --concurrency=$CONCURRENCY EXIT_CODE=$? # Copy artifacts into ZUUL's workspace sudo -H -u $USER cp -r $MANILACLIENT_DIR $WORKSPACE return $EXIT_CODEpython-manilaclient-2.1.0/contrib/ci/pre_test_hook.sh0000664000175000017500000000241613644133413022754 0ustar zuulzuul00000000000000#!/bin/bash -xe # # 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. # This script is executed inside pre_test_hook function in devstack gate. # Run manila's pre_test_hook.sh script first source $BASE/new/manila/contrib/ci/pre_test_hook.sh True dummy multibackend sudo -H pip install virtualenv virtualenv /tmp/devstack-tools /tmp/devstack-tools/bin/pip install -U devstack-tools==0.4.0 localconf=$BASE/new/devstack/local.conf DSCONF=/tmp/devstack-tools/bin/dsconf # Set big quota for share networks to avoid limit exceedances $DSCONF setlc $localconf MANILA_OPTGROUP_DEFAULT_quota_share_networks 50 $DSCONF setlc $localconf MANILA_CONFIGURE_DEFAULT_TYPES True # Enable and use only v3 of Identity API $DSCONF setlc $localconf IDENTITY_API_VERSION 3 $DSCONF setlc $localconf ENABLE_IDENTITY_V2 False python-manilaclient-2.1.0/LICENSE0000664000175000017500000002707513644133413016535 0ustar zuulzuul00000000000000Copyright (c) 2009 Jacob Kaplan-Moss - initial codebase (< v2.1) Copyright (c) 2011 Rackspace - OpenStack extensions (>= v2.1) All rights reserved. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. --- License for python-manilaclient versions prior to 2.1 --- All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. python-manilaclient-2.1.0/etc/0000775000175000017500000000000013644133466016300 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/etc/oslo-config-generator/0000775000175000017500000000000013644133466022503 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/etc/oslo-config-generator/manilaclient.conf0000664000175000017500000000014213644133413025777 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/manilaclient/manilaclient.conf.sample namespace = manilaclient.config python-manilaclient-2.1.0/etc/manilaclient/0000775000175000017500000000000013644133466020740 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/etc/manilaclient/README.manilaclient.conf0000664000175000017500000000021413644133413025170 0ustar zuulzuul00000000000000To generate the sample manilaclient.conf file, run the following command from the top level of the manilaclient directory: tox -egenconfig python-manilaclient-2.1.0/doc/0000775000175000017500000000000013644133466016272 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/doc/source/0000775000175000017500000000000013644133466017572 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/doc/source/conf.py0000664000175000017500000000701413644133413021063 0ustar zuulzuul00000000000000# 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. # python-manilaclient documentation build configuration file import os import sys import openstackdocstheme # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "..")) sys.path.insert(0, ROOT) # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'openstackdocstheme', 'sphinxcontrib.programoutput'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'python-manilaclient' copyright = u'Rackspace, based on work by Jacob Kaplan-Moss' # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output -------------------------------------------------- # Output file base name for HTML help builder. htmlhelp_basename = 'python-manilaclientdoc' html_theme = 'openstackdocs' html_theme_path = [openstackdocstheme.get_html_theme_path()] # openstackdocstheme options repository_name = 'openstack/python-manilaclient' bug_project = 'python-manilaclient' bug_tag = 'docs' # -- Options for LaTeX output ------------------------------------------------- # Docs job does not install xindy latex_use_xindy = False # The paper size ('letter' or 'a4'). # latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). # latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', 'Manila-client.tex', u'Manila Python Client Documentation', u'Manila contributors', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # Additional stuff for the LaTeX preamble. # latex_preamble = '' # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_use_modindex = True python-manilaclient-2.1.0/doc/source/contributor/0000775000175000017500000000000013644133466022144 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/doc/source/contributor/functional-tests.rst0000664000175000017500000001751213644133413026176 0ustar zuulzuul00000000000000Functional tests ================ Manila contains a suite of functional tests under the python-manilaclient/tests/functional directory. Adding functional tests to your changes is not mandatory but it's certainly a good practice and it's encouraged. Prerequisite ------------ You need to have manila running somewhere. If you are using devstack, you can enable manila by enabling the manila plugin and selecting the backend you want to use. As an example, you can use the following local.conf file .. code-block:: console [[local|localrc]] # auth ADMIN_PASSWORD=nomoresecret DATABASE_PASSWORD=stackdb RABBIT_PASSWORD=stackqueue SERVICE_PASSWORD=$ADMIN_PASSWORD # enable logging LOGFILE=/opt/stack/logs/stack.sh.log VERBOSE=True LOG_COLOR=True LOGDIR=/opt/stack/logs # manila enable_plugin manila https://opendev.org/openstack/manila # python-manilaclient LIBS_FROM_GIT=python-manilaclient # versioning PYTHON3_VERSION=3.6 IDENTITY_API_VERSION=3 # share driver SHARE_DRIVER=manila.tests.share.drivers.dummy.DummyDriver # share types MANILA_DEFAULT_SHARE_TYPE_EXTRA_SPECS='snapshot_support=True create_share_from_snapshot_support=True revert_to_snapshot_support=True mount_snapshot_support=True' MANILA_CONFIGURE_DEFAULT_TYPES=True # backends and groups MANILA_ENABLED_BACKENDS=alpha,beta,gamma,delta MANILA_CONFIGURE_GROUPS=alpha,beta,gamma,delta,membernet,adminnet # alpha MANILA_OPTGROUP_alpha_share_driver=manila.tests.share.drivers.dummy.DummyDriver MANILA_OPTGROUP_alpha_driver_handles_share_servers=True MANILA_OPTGROUP_alpha_share_backend_name=ALPHA MANILA_OPTGROUP_alpha_network_config_group=membernet MANILA_OPTGROUP_alpha_admin_network_config_group=adminnet # beta MANILA_OPTGROUP_beta_share_driver=manila.tests.share.drivers.dummy.DummyDriver MANILA_OPTGROUP_beta_driver_handles_share_servers=True MANILA_OPTGROUP_beta_share_backend_name=BETA MANILA_OPTGROUP_beta_network_config_group=membernet MANILA_OPTGROUP_beta_admin_network_config_group=adminnet # gamma MANILA_OPTGROUP_gamma_share_driver=manila.tests.share.drivers.dummy.DummyDriver MANILA_OPTGROUP_gamma_driver_handles_share_servers=False MANILA_OPTGROUP_gamma_share_backend_name=GAMMA MANILA_OPTGROUP_gamma_replication_domain=DUMMY_DOMAIN # delta MANILA_OPTGROUP_delta_share_driver=manila.tests.share.drivers.dummy.DummyDriver MANILA_OPTGROUP_delta_driver_handles_share_servers=False MANILA_OPTGROUP_delta_share_backend_name=DELTA MANILA_OPTGROUP_delta_replication_domain=DUMMY_DOMAIN # membernet MANILA_OPTGROUP_membernet_network_api_class=manila.network.standalone_network_plugin.StandaloneNetworkPlugin MANILA_OPTGROUP_membernet_standalone_network_plugin_gateway=10.0.0.1 MANILA_OPTGROUP_membernet_standalone_network_plugin_mask=24 MANILA_OPTGROUP_membernet_standalone_network_plugin_network_type=vlan MANILA_OPTGROUP_membernet_standalone_network_plugin_segmentation_id=1010 MANILA_OPTGROUP_membernet_standalone_network_plugin_allowed_ip_ranges=10.0.0.10-10.0.0.209 MANILA_OPTGROUP_membernet_network_plugin_ipv4_enabled=True # adminnet MANILA_OPTGROUP_adminnet_network_api_class=manila.network.standalone_network_plugin.StandaloneNetworkPlugin MANILA_OPTGROUP_adminnet_standalone_network_plugin_gateway=11.0.0.1 MANILA_OPTGROUP_adminnet_standalone_network_plugin_mask=24 MANILA_OPTGROUP_adminnet_standalone_network_plugin_network_type=vlan MANILA_OPTGROUP_adminnet_standalone_network_plugin_segmentation_id=1011 MANILA_OPTGROUP_adminnet_standalone_network_plugin_allowed_ip_ranges=11.0.0.10-11.0.0.19,11.0.0.30-11.0.0.39,11.0.0.50-11.0.0.199 MANILA_OPTGROUP_adminnet_network_plugin_ipv4_enabled=True In this example we enable manila with the DummyDriver. Configuration ------------- The functional tests require a couple of configuration files, so you will need to generate them yourself. For devstack ^^^^^^^^^^^^ If you are using devstack, you can run the following script .. code-block:: console export HOME=${LOCAL:-/home/} export DEST=${DEST:-/opt/stack} export MANILACLIENT_DIR=${MANILACLIENT_DIR:-$DEST/python-manilaclient} export MANILACLIENT_CONF="$MANILACLIENT_DIR/etc/manilaclient/manilaclient.conf" # Go to the manilaclient dir cd $MANILACLIENT_DIR # Give permissions sudo chown -R $USER:stack . # Create manilaclient config file touch $MANILACLIENT_CONF # Import functions from devstack source $HOME/devstack/functions # Set options to config client. source $HOME/devstack/openrc demo demo export OS_TENANT_NAME=${OS_PROJECT_NAME:-$OS_TENANT_NAME} iniset $MANILACLIENT_CONF DEFAULT username $OS_USERNAME iniset $MANILACLIENT_CONF DEFAULT tenant_name $OS_TENANT_NAME iniset $MANILACLIENT_CONF DEFAULT password $OS_PASSWORD iniset $MANILACLIENT_CONF DEFAULT auth_url $OS_AUTH_URL iniset $MANILACLIENT_CONF DEFAULT project_domain_name $OS_PROJECT_DOMAIN_NAME iniset $MANILACLIENT_CONF DEFAULT user_domain_name $OS_USER_DOMAIN_NAME iniset $MANILACLIENT_CONF DEFAULT project_domain_id $OS_PROJECT_DOMAIN_ID iniset $MANILACLIENT_CONF DEFAULT user_domain_id $OS_USER_DOMAIN_ID source $HOME/devstack/openrc admin demo export OS_TENANT_NAME=${OS_PROJECT_NAME:-$OS_TENANT_NAME} iniset $MANILACLIENT_CONF DEFAULT admin_username $OS_USERNAME iniset $MANILACLIENT_CONF DEFAULT admin_tenant_name $OS_TENANT_NAME iniset $MANILACLIENT_CONF DEFAULT admin_password $OS_PASSWORD iniset $MANILACLIENT_CONF DEFAULT admin_auth_url $OS_AUTH_URL iniset $MANILACLIENT_CONF DEFAULT admin_project_domain_name $OS_PROJECT_DOMAIN_NAME iniset $MANILACLIENT_CONF DEFAULT admin_user_domain_name $OS_USER_DOMAIN_NAME iniset $MANILACLIENT_CONF DEFAULT admin_project_domain_id $OS_PROJECT_DOMAIN_ID iniset $MANILACLIENT_CONF DEFAULT admin_user_domain_id $OS_USER_DOMAIN_ID # Suppress errors in cleanup of resources SUPPRESS_ERRORS=${SUPPRESS_ERRORS_IN_CLEANUP:-False} iniset $MANILACLIENT_CONF DEFAULT suppress_errors_in_cleanup $SUPPRESS_ERRORS # Set access type usage specific to dummy driver that we are using in CI iniset $MANILACLIENT_CONF DEFAULT access_types_mapping "nfs:ip,cifs:user" # Dummy driver is capable of running share migration tests iniset $MANILACLIENT_CONF DEFAULT run_migration_tests "True" # Running mountable snapshot tests in dummy driver iniset $MANILACLIENT_CONF DEFAULT run_mount_snapshot_tests "True" # Create share network and use it for functional tests if required USE_SHARE_NETWORK=$(trueorfalse True USE_SHARE_NETWORK) .. code-block:: console if [[ ${USE_SHARE_NETWORK} = True ]]; then SHARE_NETWORK_NAME=${SHARE_NETWORK_NAME:-ci} DEFAULT_NEUTRON_NET=$(openstack network show private -c id -f value) DEFAULT_NEUTRON_SUBNET=$(openstack subnet show private-subnet -c id -f value) NEUTRON_NET=${NEUTRON_NET:-$DEFAULT_NEUTRON_NET} NEUTRON_SUBNET=${NEUTRON_SUBNET:-$DEFAULT_NEUTRON_SUBNET} manila share-network-create --name $SHARE_NETWORK_NAME --neutron-net $NEUTRON_NET --neutron-subnet $NEUTRON_SUBNET iniset $MANILACLIENT_CONF DEFAULT share_network $SHARE_NETWORK_NAME iniset $MANILACLIENT_CONF DEFAULT admin_share_network $SHARE_NETWORK_NAME fi .. code-block:: console # Set share type if required if [[ "$SHARE_TYPE" ]]; then iniset $MANILACLIENT_CONF DEFAULT share_type $SHARE_TYPE fi Change to the correct user value. Running the tests ----------------- To run all functional tests make sure you are in the top level of your python-manilaclient module (e.g. /opt/stack/python-manilaclient/) and simply run:: tox -e functional This will create a virtual environment, load all the packages from test-requirements.txt and run all functional tests.python-manilaclient-2.1.0/doc/source/contributor/index.rst0000664000175000017500000000142113644133413023773 0ustar zuulzuul00000000000000Contributing ============ Code is hosted at `opendev.org`_. Submit bugs to the python-manilaclient project on `Launchpad`_. Submit code to the openstack/python-manilaclient project using `Gerrit`_. .. _opendev.org: https://opendev.org/openstack/python-manilaclient .. _Launchpad: https://launchpad.net/python-manilaclient .. _Gerrit: https://docs.openstack.org/infra/manual/developers.html#development-workflow Testing ------- Manilaclient has two types of tests - 'unit' and 'functional'. The preferred way to run tests is using ``tox``. See `Consistent Testing Interface`_ for more details. .. toctree:: :maxdepth: 3 functional-tests .. _Consistent Testing Interface: https://opendev.org/openstack/governance/src/branch/master/reference/project-testing-interface.rstpython-manilaclient-2.1.0/doc/source/cli/0000775000175000017500000000000013644133466020341 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/doc/source/cli/.gitkeep0000664000175000017500000000000013644133413021750 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/doc/source/index.rst0000664000175000017500000000140513644133413021423 0ustar zuulzuul00000000000000Python 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. You'll need credentials for an OpenStack cloud that implements the Manila API in order to use the manila client. Command-Line Reference ====================== .. toctree:: :maxdepth: 1 user/shell API === .. toctree:: :maxdepth: 1 user/api Contributing ============ .. toctree:: :maxdepth: 1 contributor/index .. only:: html Indices and tables ================== * :ref:`genindex` * :ref:`search` python-manilaclient-2.1.0/doc/source/user/0000775000175000017500000000000013644133466020550 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/doc/source/user/shell.rst0000664000175000017500000000255513644133413022410 0ustar zuulzuul00000000000000The :program:`manila` shell utility ========================================= .. 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 python-manilaclient-2.1.0/doc/source/user/api.rst0000664000175000017500000000410513644133413022043 0ustar zuulzuul00000000000000The :program:`manilaclient` Python API ====================================== .. module:: manilaclient :synopsis: A client for the OpenStack Manila API. .. currentmodule:: manilaclient Usage ----- In order to use the Python API directly, you must first obtain an auth token and identify which endpoint you wish to speak to. Once you have done so, you can use the API like so:: >>> from manilaclient import client >>> manila = client.Client('1', $OS_USER_NAME, $OS_PASSWORD, $OS_TENANT_NAME, $OS_AUTH_URL) >>> manila.shares.list() [] >>> share = manila.shares.create(share_proto="nfs", size=1, share_network_id="some_share_network_id") >>> share.id ce06d0a8-5c1b-4e2c-81d2-39eca6bbfb70 >>> manila.shares.list() [] >>>share.delete In addition to creating and deleting shares, the manilaclient can manage share-types, access controls, and more! Using CephFS with Ganesha for NFS support as an example (assuumes this continues from the above initialization):: >>> share_type = client.share_types.create( >>> name="cephfsnfstype", spec_driver_handles_share_servers=False, >>> extra_specs={ >>> 'vendor_name': 'Ceph', >>> 'storage_protocol': 'NFS', >>> 'snapshot_support': False, >>> }) >>> share_type >>> share = client.shares.create( >>> share_type='cephfsnfstype', name='cephnfsshare1', >>> share_proto="nfs", size=1) >>> share.allow(access_type='ip', access="192.168.0.0/24", access_level='rw') {'id': '29bc4b66-d55d-424d-8107-aee96d1c562b', 'share_id': '0ac95dd2-afba-4ba3-8934-721b29492f04', 'access_level': 'rw', 'access_to': '192.168.0.0/24', 'access_type': 'ip', 'state': 'new'} >>> share.export_locations ['10.5.0.22:/volumes/_nogroup/cf0451b6-0a95-4982-a801-2e212e9c9b96'] In the above example, Manila will be setup with an NFS share type, backed by CephFS. A share is then created, and then access controls are added giving the 192.168.0/24 subnet read/write access to the share. python-manilaclient-2.1.0/doc/Makefile0000664000175000017500000000616413644133413017731 0ustar zuulzuul00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXSOURCE = source PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SPHINXSOURCE) .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/python-manilaclient.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python-manilaclient.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." python-manilaclient-2.1.0/doc/.gitignore0000664000175000017500000000000713644133413020247 0ustar zuulzuul00000000000000build/ python-manilaclient-2.1.0/doc/requirements.txt0000664000175000017500000000055113644133413021547 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. openstackdocstheme>=1.32.1 # Apache-2.0 sphinx!=1.6.6,!=1.6.7,>=1.6.2,!=2.1.0 # BSD reno>=2.5.0 # Apache-2.0 sphinxcontrib-programoutput>=0.11 # BSD python-manilaclient-2.1.0/rally-jobs/0000775000175000017500000000000013644133466017603 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/rally-jobs/rally-manila.yaml0000664000175000017500000001022713644133413023043 0ustar zuulzuul00000000000000--- Dummy.openstack: - description: "Check quotas context" runner: type: "constant" times: 1 concurrency: 1 context: users: tenants: 1 users_per_tenant: 1 quotas: manila: shares: -1 gigabytes: -1 snapshots: -1 snapshot_gigabytes: -1 share_networks: -1 ManilaShares.list_shares: - args: detailed: True runner: type: "constant" times: 10 concurrency: 10 context: users: tenants: 3 users_per_tenant: 4 user_choice_method: "round_robin" sla: failure_rate: max: 0 {% for s in ("create_and_delete_share", "create_and_list_share") %} ManilaShares.{{s}}: - args: share_proto: "nfs" size: 1 share_type: "dhss_true" min_sleep: 1 max_sleep: 2 runner: type: "constant" times: 10 concurrency: 10 context: quotas: manila: shares: -1 gigabytes: -1 share_networks: -1 users: tenants: 2 users_per_tenant: 1 user_choice_method: "round_robin" manila_share_networks: use_share_networks: True sla: failure_rate: max: 0 {% endfor %} ManilaShares.create_share_network_and_delete: - args: name: "rally" runner: type: "constant" times: 10 concurrency: 10 context: quotas: manila: share_networks: -1 users: tenants: 2 users_per_tenant: 1 sla: failure_rate: max: 0 ManilaShares.create_share_network_and_list: - args: name: "rally" detailed: True search_opts: name: "rally" runner: type: "constant" times: 10 concurrency: 10 context: quotas: manila: share_networks: -1 users: tenants: 2 users_per_tenant: 1 sla: failure_rate: max: 0 ManilaShares.list_share_servers: - args: search_opts: {} runner: type: "constant" times: 10 concurrency: 10 sla: failure_rate: max: 0 ManilaShares.create_security_service_and_delete: {% for s in ("ldap", "kerberos", "active_directory") %} - args: security_service_type: {{s}} dns_ip: "fake_dns_ip" server: "fake-server" domain: "fake_domain" user: "fake_user" password: "fake_password" name: "fake_name" description: "fake_description" runner: type: "constant" times: 10 concurrency: 10 context: users: tenants: 1 users_per_tenant: 1 sla: failure_rate: max: 0 {% endfor %} ManilaShares.attach_security_service_to_share_network: {% for s in ("ldap", "kerberos", "active_directory") %} - args: security_service_type: {{s}} runner: type: "constant" times: 10 concurrency: 10 context: users: tenants: 1 users_per_tenant: 1 quotas: manila: share_networks: -1 sla: failure_rate: max: 0 {% endfor %} ManilaShares.set_and_delete_metadata: - args: sets: 1 set_size: 3 delete_size: 3 key_min_length: 1 key_max_length: 256 value_min_length: 1 value_max_length: 1024 runner: type: "constant" times: 10 concurrency: 10 context: quotas: manila: shares: -1 gigabytes: -1 share_networks: -1 users: tenants: 1 users_per_tenant: 1 manila_share_networks: use_share_networks: True manila_shares: shares_per_tenant: 1 share_proto: "NFS" size: 1 share_type: "dhss_true" sla: failure_rate: max: 0 python-manilaclient-2.1.0/rally-jobs/rally-manila-no-ss.yaml0000664000175000017500000000354213644133413024102 0ustar zuulzuul00000000000000--- Dummy.openstack: - description: "Check quotas context" runner: type: "constant" times: 1 concurrency: 1 context: users: tenants: 1 users_per_tenant: 1 quotas: manila: shares: -1 gigabytes: -1 snapshots: -1 snapshot_gigabytes: -1 share_networks: -1 ManilaShares.list_shares: - args: detailed: True runner: type: "constant" times: 10 concurrency: 10 context: users: tenants: 1 users_per_tenant: 1 sla: failure_rate: max: 0 {% for s in ("create_and_delete_share", "create_and_list_share") %} ManilaShares.{{s}}: - args: share_proto: "nfs" size: 1 share_type: "dhss_false" min_sleep: 1 max_sleep: 2 runner: type: "constant" times: 10 concurrency: 10 context: quotas: manila: shares: -1 gigabytes: -1 users: tenants: 2 users_per_tenant: 1 sla: failure_rate: max: 0 {% endfor %} ManilaShares.set_and_delete_metadata: - args: sets: 1 set_size: 3 delete_size: 3 key_min_length: 1 key_max_length: 256 value_min_length: 1 value_max_length: 1024 runner: type: "constant" times: 10 concurrency: 10 context: quotas: manila: shares: -1 gigabytes: -1 users: tenants: 1 users_per_tenant: 1 manila_shares: shares_per_tenant: 1 share_proto: "NFS" size: 1 share_type: "dhss_false" sla: failure_rate: max: 0 python-manilaclient-2.1.0/PKG-INFO0000664000175000017500000001325313644133466016626 0ustar zuulzuul00000000000000Metadata-Version: 1.2 Name: python-manilaclient Version: 2.1.0 Summary: Client library for OpenStack Manila API. Home-page: https://docs.openstack.org/python-manilaclient/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/python-manilaclient.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on Python bindings to the OpenStack Manila API =========================================== .. only: html .. image:: https://img.shields.io/pypi/v/python-manilaclient.svg :target: https://pypi.org/project/python-manilaclient/ :alt: Latest Version This is a client for the OpenStack Manila API. There's a Python API (the ``manilaclient`` module), and a command-line script (``manila``). Each implements 100% of the OpenStack Manila API. See the `OpenStack CLI guide`_ for information on how to use the ``manila`` command-line tool. You may also want to look at the `OpenStack API documentation`_. .. _OpenStack CLI Guide: https://docs.openstack.org/python-openstackclient/latest/cli/ .. _OpenStack API documentation: https://docs.openstack.org/api/ The project is hosted on `Launchpad`_, where bugs can be filed. The code is hosted on `Github`_. Patches must be submitted using `Gerrit`_, *not* Github pull requests. .. _Github: https://github.com/openstack/python-manilaclient .. _Launchpad: https://launchpad.net/python-manilaclient .. _Gerrit: https://docs.openstack.org/infra/manual/developers.html#development-workflow This code is a fork of `Cinderclient`_ of Grizzly release and then it was developed separately. Cinderclient code is a fork of `Jacobian's python-cloudservers`__ If you need API support for the Rackspace API solely or the BSD license, you should use that repository. python-manilaclient is licensed under the Apache License like the rest of OpenStack. .. _Cinderclient: https://github.com/openstack/python-cinderclient __ https://github.com/jacobian/python-cloudservers .. contents:: Contents: :local: Command-line API ---------------- Installing this package gets you a shell command, ``manila``, that you can use to interact with any Rackspace compatible API (including OpenStack). You'll need to provide your OpenStack username and password. You can do this with the ``--os-username``, ``--os-password`` and ``--os-tenant-name`` params, but it's easier to just set them as environment variables:: export OS_USERNAME=foouser export OS_PASSWORD=barpass export OS_TENANT_NAME=fooproject You will also need to define the authentication url either with param ``--os-auth-url`` or as an environment variable:: export OS_AUTH_URL=http://example.com:5000/v2.0/ Since Keystone can return multiple regions in the Service Catalog, you can specify the one you want with ``--os-region-name`` (or ``export OS_REGION_NAME``). It defaults to the first in the list returned. You'll find complete documentation on the shell by running ``manila help``, see ``manila help COMMAND`` for help on a specific command. Python API ---------- There's also a complete Python API, but it has not yet been documented. Quick-start using keystone:: # use v2.0 auth with http://example.com:5000/v2.0/ >>> from manilaclient.v1 import client >>> nt = client.Client(USER, PASS, TENANT, AUTH_URL, service_type="share") >>> nt.shares.list() [...] * License: Apache License, Version 2.0 * `PyPi`_ - package installation * `Online Documentation`_ * `Launchpad project`_ - release management * `Blueprints`_ - feature specifications * `Bugs`_ - issue tracking * `Source`_ * `How to Contribute`_ * `Release Notes`_ .. _PyPi: https://pypi.org/project/python-manilaclient .. _Online Documentation: https://docs.openstack.org/python-manilaclient/latest/ .. _Launchpad project: https://launchpad.net/python-manilaclient .. _Blueprints: https://blueprints.launchpad.net/python-manilaclient .. _Bugs: https://bugs.launchpad.net/python-manilaclient .. _Source: https://opendev.org/openstack/python-manilaclient .. _How to Contribute: https://docs.openstack.org/infra/manual/developers.html .. _Release Notes: https://docs.openstack.org/releasenotes/python-manilaclient Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Environment :: OpenStack Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=3.6 python-manilaclient-2.1.0/run_tests.sh0000775000175000017500000001270213644133413020104 0ustar zuulzuul00000000000000#!/bin/bash set -eu function usage { echo "Usage: $0 [OPTION]..." echo "Run python-manilaclient test suite" echo "" echo " -V, --virtual-env Always use virtualenv. Install automatically if not present" echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment" echo " -s, --no-site-packages Isolate the virtualenv from the global Python environment" echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added." echo " -p, --pep8 Just run pep8" echo " -P, --no-pep8 Don't run pep8" echo " -c, --coverage Generate coverage report" echo " -h, --help Print this usage message" echo " --hide-elapsed Don't print the elapsed time for each test along with slow test list" echo "" echo "Note: with no options specified, the script will try to run the tests in a virtual environment," echo " If no virtualenv is found, the script will ask if you would like to create one. If you " echo " prefer to run tests NOT in a virtual environment, simply pass the -N option." exit } function process_option { case "$1" in -h|--help) usage;; -V|--virtual-env) always_venv=1; never_venv=0;; -N|--no-virtual-env) always_venv=0; never_venv=1;; -s|--no-site-packages) no_site_packages=1;; -f|--force) force=1;; -p|--pep8) just_pep8=1;; -P|--no-pep8) no_pep8=1;; -c|--coverage) coverage=1;; -d|--debug) debug=1;; -*) testropts="$testropts $1";; *) testrargs="$testrargs $1" esac } venv=.venv with_venv=tools/with_venv.sh always_venv=0 never_venv=0 force=0 no_site_packages=0 installvenvopts= testrargs= testropts= wrapper="" just_pep8=0 no_pep8=0 coverage=0 debug=0 LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=C for arg in "$@"; do process_option $arg done if [ $no_site_packages -eq 1 ]; then installvenvopts="--no-site-packages" fi function init_testr { if [ ! -d .testrepository ]; then ${wrapper} testr init fi } function run_tests { # Cleanup *pyc ${wrapper} find . -type f -name "*.pyc" -delete if [ $debug -eq 1 ]; then if [ "$testropts" = "" ] && [ "$testrargs" = "" ]; then # Default to running all tests if specific test is not # provided. testrargs="discover ./tests" fi ${wrapper} python -m testtools.run $testropts $testrargs # Short circuit because all of the testr and coverage stuff # below does not make sense when running testtools.run for # debugging purposes. return $? fi if [ $coverage -eq 1 ]; then # Do not test test_coverage_ext when gathering coverage. if [ "x$testrargs" = "x" ]; then testrargs="^(?!.*test_coverage_ext).*$" fi export PYTHON="${wrapper} coverage run --source manilaclient --parallel-mode" fi # Just run the test suites in current environment set +e TESTRTESTS="$TESTRTESTS $testrargs" echo "Running \`${wrapper} $TESTRTESTS\`" ${wrapper} $TESTRTESTS RESULT=$? set -e copy_subunit_log return $RESULT } function copy_subunit_log { LOGNAME=`cat .testrepository/next-stream` LOGNAME=$(($LOGNAME - 1)) LOGNAME=".testrepository/${LOGNAME}" cp $LOGNAME subunit.log } function run_pep8 { echo "Running pep8 ..." srcfiles="manilaclient" # Just run PEP8 in current environment # # NOTE(sirp): W602 (deprecated 3-arg raise) is being ignored for the # following reasons: # # 1. It's needed to preserve traceback information when re-raising # exceptions; this is needed b/c Eventlet will clear exceptions when # switching contexts. # # 2. There doesn't appear to be an alternative, "pep8-tool" compatible way of doing this # in Python 2 (in Python 3 `with_traceback` could be used). # # 3. Can find no corroborating evidence that this is deprecated in Python 2 # other than what the PEP8 tool claims. It is deprecated in Python 3, so, # perhaps the mistake was thinking that the deprecation applied to Python 2 # as well. pep8_opts="--ignore=E202,W602 --repeat" ${wrapper} pep8 ${pep8_opts} ${srcfiles} } TESTRTESTS="testr run --parallel $testropts" if [ $never_venv -eq 0 ] then # Remove the virtual environment if --force used if [ $force -eq 1 ]; then echo "Cleaning virtualenv..." rm -rf ${venv} fi if [ -e ${venv} ]; then wrapper="${with_venv}" else if [ $always_venv -eq 1 ]; then # Automatically install the virtualenv python tools/install_venv.py $installvenvopts wrapper="${with_venv}" else echo -e "No virtual environment found...create one? (Y/n) \c" read use_ve if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then # Install the virtualenv and run the test suite in it python tools/install_venv.py $installvenvopts wrapper=${with_venv} fi fi fi fi # Delete old coverage data from previous runs if [ $coverage -eq 1 ]; then ${wrapper} coverage erase fi if [ $just_pep8 -eq 1 ]; then run_pep8 exit fi init_testr run_tests # NOTE(sirp): we only want to run pep8 when we're running the full-test suite, # not when we're running tests individually. if [ -z "$testrargs" ]; then if [ $no_pep8 -eq 0 ]; then run_pep8 fi fi if [ $coverage -eq 1 ]; then echo "Generating coverage report in covhtml/" ${wrapper} coverage combine ${wrapper} coverage html --include='manilaclient/*' --omit='manilaclient/openstack/common/*' -d covhtml -i fi python-manilaclient-2.1.0/releasenotes/0000775000175000017500000000000013644133466020216 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/releasenotes/source/0000775000175000017500000000000013644133466021516 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/releasenotes/source/unreleased.rst0000664000175000017500000000015313644133413024366 0ustar zuulzuul00000000000000============================ Current Series Release Notes ============================ .. release-notes:: python-manilaclient-2.1.0/releasenotes/source/newton.rst0000664000175000017500000000023213644133413023547 0ustar zuulzuul00000000000000=================================== Newton Series Release Notes =================================== .. release-notes:: :branch: origin/stable/newton python-manilaclient-2.1.0/releasenotes/source/ocata.rst0000664000175000017500000000023013644133413023322 0ustar zuulzuul00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata python-manilaclient-2.1.0/releasenotes/source/conf.py0000664000175000017500000002177213644133413023016 0ustar zuulzuul00000000000000# 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. # Python-manilaclient Release Notes documentation build configuration file, # created by sphinx-quickstart on Tue May 24 17:40:50 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ import openstackdocstheme # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'openstackdocstheme', 'reno.sphinxext', ] # openstackdocstheme options repository_name = 'openstack/python-manilaclient' bug_project = 'python-manilaclient' bug_tag = 'release notes' html_last_updated_fmt = '%Y-%m-%d %H:%M' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'python-manilaclient Release Notes' copyright = u'2016, Manila Developers' # Release notes are version independent. release = '' # The short X.Y version. version = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'openstackdocs' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. html_theme_path = [openstackdocstheme.get_html_theme_path()] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'PythonManilaclientReleaseNotesdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'PythonManilaclientReleaseNotes.tex', u'python-manilaclient Release Notes Documentation', u'Manila Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'python-manilaclientreleasenotes', u'python-manilaclient Release Notes Documentation', [u'Manila Developers'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'PythonManilaclientReleaseNotes', u'python-manilaclient Release Notes Documentation', u'Manila Developers', 'PythonManilaclientReleaseNotes', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] python-manilaclient-2.1.0/releasenotes/source/_static/0000775000175000017500000000000013644133466023144 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/releasenotes/source/_static/.placeholder0000664000175000017500000000000013644133413025405 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/releasenotes/source/rocky.rst0000664000175000017500000000022113644133413023362 0ustar zuulzuul00000000000000=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky python-manilaclient-2.1.0/releasenotes/source/train.rst0000664000175000017500000000017613644133413023361 0ustar zuulzuul00000000000000========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train python-manilaclient-2.1.0/releasenotes/source/_templates/0000775000175000017500000000000013644133466023653 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/releasenotes/source/_templates/.placeholder0000664000175000017500000000000013644133413026114 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/releasenotes/source/stein.rst0000664000175000017500000000022113644133413023355 0ustar zuulzuul00000000000000=================================== Stein Series Release Notes =================================== .. release-notes:: :branch: stable/stein python-manilaclient-2.1.0/releasenotes/source/pike.rst0000664000175000017500000000021713644133413023170 0ustar zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike python-manilaclient-2.1.0/releasenotes/source/queens.rst0000664000175000017500000000022313644133413023535 0ustar zuulzuul00000000000000=================================== Queens Series Release Notes =================================== .. release-notes:: :branch: stable/queens python-manilaclient-2.1.0/releasenotes/source/index.rst0000664000175000017500000000032413644133413023346 0ustar zuulzuul00000000000000 ================================= python-manilaclient Release Notes ================================= .. toctree:: :maxdepth: 1 unreleased train stein rocky queens pike ocata newton python-manilaclient-2.1.0/releasenotes/notes/0000775000175000017500000000000013644133466021346 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/releasenotes/notes/newton-migration-improvements-166a03472948bdef.yaml0000664000175000017500000000146713644133413032422 0ustar zuulzuul00000000000000--- prelude: > - Added new parameters to Share Migration experimental API. features: - Share Migration now has parameters to force share migration procedure to maintain the share writable, preserve its metadata and be non-disruptive when migrating. - Added parameter to change share network when performing migration. deprecations: - Renamed Share Migration 'force_host_copy' parameter to 'force_host_assisted_migration', to better represent the parameter's functionality. - API version 2.22 is now required for all Share Migration APIs. upgrades: - Removed Share Migration 'notify' parameter, it is no longer possible to perform a 1-phase migration. - Removed 'migrate_share' API support. - Added 'None' to 'reset_task_state' API possible values so it can unset the task_state. ././@LongLink0000000000000000000000000000015100000000000011212 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bug-1811516-bug-1811627-python3-fixes-4b26130027b2c076.yamlpython-manilaclient-2.1.0/releasenotes/notes/bug-1811516-bug-1811627-python3-fixes-4b26130027b2c076.0000664000175000017500000000015613644133413031036 0ustar zuulzuul00000000000000--- fixes: - | The shell utility has been fixed to report errors correctly on python3 environments. python-manilaclient-2.1.0/releasenotes/notes/.placeholder0000664000175000017500000000000013644133413023607 0ustar zuulzuul00000000000000python-manilaclient-2.1.0/releasenotes/notes/add-share-group-quotas-support-b6563cec58209a1d.yaml0000664000175000017500000000040313644133413032441 0ustar zuulzuul00000000000000--- features: - Added support for share group and share group snapshot quotas. upgrade: - After addition of share group and share group snapshot quotas, it is now possible to get 'over limit' error creating share groups and share group snapshots. python-manilaclient-2.1.0/releasenotes/notes/mountable_snapshot-ced01da7dffc6d7e.yaml0000664000175000017500000000012613644133413030725 0ustar zuulzuul00000000000000--- features: - Added support for the mountable snapshots feature to manila client. python-manilaclient-2.1.0/releasenotes/notes/bug_1715769_fix-3ec701b0fb9d7910.yaml0000664000175000017500000000020613644133413026776 0ustar zuulzuul00000000000000--- fixes: - Usernames can contain the $ symbol to allow for windows computer authentication in an Active Directory environment python-manilaclient-2.1.0/releasenotes/notes/manage-unmanage-share-servers-8c7b27a1fe80e5fa.yaml0000664000175000017500000000024513644133413032413 0ustar zuulzuul00000000000000--- features: - Added CLI commands to manage and unmanage share servers. - Updated CLI command for managing shares to accept ``share_server_id`` parameter. python-manilaclient-2.1.0/releasenotes/notes/bug-1802059-fix-is_default-a8d3d95ffa0aede9.yaml0000664000175000017500000000014613644133413031253 0ustar zuulzuul00000000000000--- fixes: - Fix share_group_type_create returning is_default as a function object instead of value python-manilaclient-2.1.0/releasenotes/notes/fix-is-default-empty-557844001e0401e2.yaml0000664000175000017500000000037113644133413030100 0ustar zuulzuul00000000000000--- fixes: - The share type and share group type shell commands retrieve the 'is_default' value from the manila API where supported. This fix also addresses the blank 'is_default' field when creating share types and share group types. python-manilaclient-2.1.0/releasenotes/notes/add-support-to-check-quota-usage-scfdg14dod40k71a.yaml0000664000175000017500000000011713644133413033157 0ustar zuulzuul00000000000000--- features: - Add support to check quota usage by '--detail' in quota-show ././@LongLink0000000000000000000000000000016600000000000011220 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/remove-experimental-flag-from-share-groups-feature-dcf2b0b67fe4cac4.yamlpython-manilaclient-2.1.0/releasenotes/notes/remove-experimental-flag-from-share-groups-feature-dcf20000664000175000017500000000110613644133413034001 0ustar zuulzuul00000000000000--- prelude: > Since the share group APIs have graduated from their `experimental feature state `_ in API version ``2.55``, the client was updated to fit into those changes, and the share group commands will no longer include the `X-OpenStack-Manila-API-Experimental`` header in the request. Share group types can be created to encompass one or more share types, share groups can be created, updated, snapshotted and deleted, and shares can be created within share groups. python-manilaclient-2.1.0/releasenotes/notes/bug_1777849_1779935_fix-344cb8f09b7df502.yaml0000664000175000017500000000015413644133413027745 0ustar zuulzuul00000000000000--- fixes: - Fixed bugs 1777849 and 1779935. Name can be used for the sort option in the list command.python-manilaclient-2.1.0/releasenotes/notes/bug-1799934-add-ou-parameter-260f9aaf939d1919.yaml0000664000175000017500000000012313644133413031140 0ustar zuulzuul00000000000000--- features: - organizational unit (--ou) parameter support added in manila cli ././@LongLink0000000000000000000000000000017700000000000011222 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bug-1738917-and-1738918-fix-access-share-group-type-by-name-3a8760522c147f28.yamlpython-manilaclient-2.1.0/releasenotes/notes/bug-1738917-and-1738918-fix-access-share-group-type-by-0000775000175000017500000000016613644133413032230 0ustar zuulzuul00000000000000--- fixes: - Fixed bugs 1738917 and 1738918. Names can now be used in commands pertaining to share group types. python-manilaclient-2.1.0/releasenotes/notes/add-count-info-in-share-21a6b36c0f4c87b2.yaml0000664000175000017500000000012313644133413030736 0ustar zuulzuul00000000000000--- features: - Added ``with_count`` option in share's list commands since 2.42. python-manilaclient-2.1.0/releasenotes/notes/bug-1664877-c462bfad92ce03e5.yaml0000664000175000017500000000057413644133413026222 0ustar zuulzuul00000000000000--- fixes: - | If using python3.5, a TypeError will raise if server returns a 4xx response. Below is the error message: TypeError: unorderable types: int() > NoneType() This error occurred because python-manilaclient tried to compare an integer with None. This is not a valid comparison starting from python3.5. See bug #1664877 for more details. ././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bug-1650774-share-instance-reset-state-9c4b26f44b5da0e9.yamlpython-manilaclient-2.1.0/releasenotes/notes/bug-1650774-share-instance-reset-state-9c4b26f44b5da0e90000664000175000017500000000026313644133413032331 0ustar zuulzuul00000000000000--- fixes: - Manila now allows modifying the share instance statuses to "migrating" or "migrating_to". Clarified the help text in the client to include these statuses. ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bug-1622540-add-missing-az-list-command-f90265de5c2f261b.yamlpython-manilaclient-2.1.0/releasenotes/notes/bug-1622540-add-missing-az-list-command-f90265de5c2f2610000664000175000017500000000014013644133413032117 0ustar zuulzuul00000000000000--- fixes: - Added support for missing command to list manila service availability zones. python-manilaclient-2.1.0/releasenotes/notes/start-using-reno-b744cd0259c7a88c.yaml0000664000175000017500000000012613644133413027665 0ustar zuulzuul00000000000000--- features: - Started using release notes to track changes to python-manilaclient.python-manilaclient-2.1.0/releasenotes/notes/add-message-list-and-delete-41b3323edd63d894.yaml0000664000175000017500000000013213644133413031477 0ustar zuulzuul00000000000000--- features: - Added new subcommands message-list, message-show and message-delete. python-manilaclient-2.1.0/releasenotes/notes/add-share-group-support-a3166f6ca4d06a81.yaml0000664000175000017500000000033013644133413031120 0ustar zuulzuul00000000000000--- features: - Added share group support (replacing CG support). upgrade: - By adding share group support, the earlier support for CGs was removed. Preexisting CGs should be managed as share groups instead. python-manilaclient-2.1.0/releasenotes/notes/add-export-location-filter-4cf3114doe40k598.yaml0000664000175000017500000000015613644133413031624 0ustar zuulzuul00000000000000--- features: - Shares and share instances can now be filtered with their export location IDs or paths. ././@LongLink0000000000000000000000000000016100000000000011213 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/support-create-share-from-snapshot-extra-spec-cdba92f179c1c5c6.yamlpython-manilaclient-2.1.0/releasenotes/notes/support-create-share-from-snapshot-extra-spec-cdba92f170000664000175000017500000000023113644133413033561 0ustar zuulzuul00000000000000--- features: - Support the new optional create_share_from_snapshot_support extra spec, and handle the newly-optional snapshot_support extra spec. ././@LongLink0000000000000000000000000000017300000000000011216 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bug-1707303-fix-list-command-when-not-given-search-opts-c06af7b344e9cb91.yamlpython-manilaclient-2.1.0/releasenotes/notes/bug-1707303-fix-list-command-when-not-given-search-opts0000664000175000017500000000023513644133413033122 0ustar zuulzuul00000000000000--- fixes: - The list command for --os-share-api-version <=2.34 has been fixed to not error out in case there are no search options/filters specified. ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/add-support-filter-search-for-share-type-fdbaaa9510cc59dd.yamlpython-manilaclient-2.1.0/releasenotes/notes/add-support-filter-search-for-share-type-fdbaaa9510cc590000664000175000017500000000011013644133413033303 0ustar zuulzuul00000000000000--- features: - Share types can now be filtered with its extra_specs. python-manilaclient-2.1.0/releasenotes/notes/add-quota-per-share-type-support-3b2708ea232e69bc.yaml0000664000175000017500000000007313644133413032667 0ustar zuulzuul00000000000000--- features: - Added support for per-share-type quotas. python-manilaclient-2.1.0/releasenotes/notes/bug_1570085_fix-905786b797379309.yaml0000664000175000017500000000025713644133413026452 0ustar zuulzuul00000000000000--- fixes: - Both 'manila list --all-tenants' and 'manila list --public' will show column Project ID. Same is the change in behavior of snapshot-list --all-tenants. ././@LongLink0000000000000000000000000000020100000000000011206 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bug-1717940-dash-dash-column-reports-nothing-for-capabilities-db8c1234fae91af5.yamlpython-manilaclient-2.1.0/releasenotes/notes/bug-1717940-dash-dash-column-reports-nothing-for-capabi0000664000175000017500000000017013644133413033154 0ustar zuulzuul00000000000000--- fixes: - Fix Manila pool-list --column reports nothing for capabilities, And also format the detail contents. python-manilaclient-2.1.0/releasenotes/notes/add-snapshot-instances-admin-api-3cf3114doe40k598.yaml0000664000175000017500000000013213644133413032664 0ustar zuulzuul00000000000000--- features: - Add list, show, and reset-status admin commands for snapshot instances. ././@LongLink0000000000000000000000000000020200000000000011207 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bug-1709746-add-group-specs-in-share-group-type-create-command-f91265de5c2f251b.yamlpython-manilaclient-2.1.0/releasenotes/notes/bug-1709746-add-group-specs-in-share-group-type-create-0000664000175000017500000000010113644133413033013 0ustar zuulzuul00000000000000--- fixes: - Added group specs in share group type create CLI. python-manilaclient-2.1.0/releasenotes/notes/bug_1782672-1954059b373f03de.yaml0000664000175000017500000000012313644133413025772 0ustar zuulzuul00000000000000--- fixes: - | Fixed bug 1782672. Name can now be used in type-show command. python-manilaclient-2.1.0/releasenotes/notes/handle-missing-api-minor-version-5a6d242f28883442.yaml0000664000175000017500000000012113644133413032474 0ustar zuulzuul00000000000000--- fixes: - Added proper error handling for missing API minor version number. ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bp-update-share-type-name-or-description-32d98b5a42cd8090.yamlpython-manilaclient-2.1.0/releasenotes/notes/bp-update-share-type-name-or-description-32d98b5a42cd800000664000175000017500000000026413644133413033064 0ustar zuulzuul00000000000000--- features: - | The ``name``, ``description`` and/or ``share_type_access:is_public`` attributes of share types can be updated with API version ``2.50`` and beyond. python-manilaclient-2.1.0/releasenotes/notes/add-ipv6-access-type-4dko90r1a9a1e0b8.yaml0000664000175000017500000000022313644133413030455 0ustar zuulzuul00000000000000--- features: - IPv6 ACL support has been added in manila. access-allow command now validates and allows access control to IPv6 clients. python-manilaclient-2.1.0/releasenotes/notes/bug-1674915-allow-user-access-fix-495b3e42bdc985ec.yaml0000664000175000017500000000024313644133413032255 0ustar zuulzuul00000000000000--- fixes: - Changed user access name limit from 32 to 255 characters since there are security services that allow user names longer than 32 characters. python-manilaclient-2.1.0/releasenotes/notes/fix-and-improve-access-rules-a118a7f8e22f65bb.yaml0000664000175000017500000000036613644133413032117 0ustar zuulzuul00000000000000--- features: - Manila exposes new transitional states for access rules and the collective "access_rules_status" fields for shares and share instances. The API request version in the client has been bumped to support these changes. python-manilaclient-2.1.0/releasenotes/notes/bug-1611506-allow-deletion-multiple-resources0000664000175000017500000000034713644133413031441 0ustar zuulzuul00000000000000--- fixes: - Fixed 6 delete commands namely security-service-delete, share-network-delete, share-server-delete, snapshot-delete, snapshot-force-delete, and type-delete to allow deletion of multiple resources together.././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bp-export-locations-replica-az-commands-03aa32c08f59c42d.yamlpython-manilaclient-2.1.0/releasenotes/notes/bp-export-locations-replica-az-commands-03aa32c08f59c420000664000175000017500000000030513644133413033053 0ustar zuulzuul00000000000000--- features: - | Share replica export locations APIs are now supported within the v2 client and the manilaclient shell. These commands are available with API version 2.47 and beyond.././@LongLink0000000000000000000000000000016600000000000011220 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bug-1608664-add-support-columns-share-replica-list-885bd8c8b4bfa8f1.yamlpython-manilaclient-2.1.0/releasenotes/notes/bug-1608664-add-support-columns-share-replica-list-885b0000664000175000017500000000011113644133413032753 0ustar zuulzuul00000000000000--- fixes: - Added "--columns" support for share-replica-list command. python-manilaclient-2.1.0/releasenotes/notes/bug-share-access-list-3cf3114doe40k599.yaml0000664000175000017500000000024413644133413030547 0ustar zuulzuul00000000000000--- features: - Beginning in version 2.33, share access list API returns "created_at" and "updated_at" for each access rule as part of the JSON response. ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/expose-access-key-in-access-list-API-b57c386c9048ae55.yamlpython-manilaclient-2.1.0/releasenotes/notes/expose-access-key-in-access-list-API-b57c386c9048ae55.y0000664000175000017500000000016613644133413032455 0ustar zuulzuul00000000000000--- features: - Returns ``access_key`` as part of ``access_list`` API response for API microversions >= '2.21'. python-manilaclient-2.1.0/releasenotes/notes/add-query-params-pools-list-api-12cf1s14ddf40kdd.yaml0000664000175000017500000000017613644133413032723 0ustar zuulzuul00000000000000--- features: - Added ``--detail`` argument to pool list command. - Added ``--share_type`` argument to pool list command. python-manilaclient-2.1.0/releasenotes/notes/bug-1830677-fix-13b30d6a89f43246.yaml0000664000175000017500000000032313644133413026471 0ustar zuulzuul00000000000000--- fixes: - | `Launchpad bug 1830677 `_ has been fixed and the client now correctly get description of share type instead of ``None``. ././@LongLink0000000000000000000000000000017100000000000011214 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bug-1682726-remove-sort-key-export-location-from-list-885des26bd5ea2de.yamlpython-manilaclient-2.1.0/releasenotes/notes/bug-1682726-remove-sort-key-export-location-from-list-80000664000175000017500000000010413644133413033140 0ustar zuulzuul00000000000000--- fixes: - Remove unused sort key export_location from list CLI.././@LongLink0000000000000000000000000000021100000000000011207 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/update-api-version-create-share-from-snapshot-another-pool-or-backend-694cfda84a41c4ff.yamlpython-manilaclient-2.1.0/releasenotes/notes/update-api-version-create-share-from-snapshot-another-p0000664000175000017500000000040613644133413034027 0ustar zuulzuul00000000000000--- features: - Added ``progress`` field for shares and share replicas. This field allows the user to check the progress of the create share from snapshot operation. The field will show a percentage of completion between ``0`` and ``100`` percent. python-manilaclient-2.1.0/releasenotes/notes/bug_1603387_fix_env_variable_8ed5450aab41aa5f.yaml0000664000175000017500000000020413644133413031715 0ustar zuulzuul00000000000000--- fixes: - Use consistent environment variable naming. All old variables are still supported due to compatibility reasons. python-manilaclient-2.1.0/releasenotes/notes/drop-python2-support-71c7b9e1dcf8c890.yaml0000664000175000017500000000044513644133413030615 0ustar zuulzuul00000000000000--- upgrade: - | Python 2.7 support has been dropped. The last release of openstack/python-manilaclient to support python 2.7 is OpenStack Train (python-manilaclient version 1.29.x). The minimum version of Python now supported by openstack/python-manilaclient is Python 3.6. ././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/add-metadata-for-share-access-rule-11a6b36c0f4c87c2.yamlpython-manilaclient-2.1.0/releasenotes/notes/add-metadata-for-share-access-rule-11a6b36c0f4c87c2.yam0000664000175000017500000000034713644133413032655 0ustar zuulzuul00000000000000--- features: - Added support to create share access rule metadata, update existing share access rule metadata and delete share access rule metadata. Also added a new shell command to show details of a share access rule. python-manilaclient-2.1.0/releasenotes/notes/add-like-filter-591572762357ef4b.yaml0000664000175000017500000000047313644133413027174 0ustar zuulzuul00000000000000--- features: - Added support for matching filters (name~, description~) to filter results of the list commands for shares, snapshots, share-networks, and share-groups. - Added support for ``description`` as a filter to the list commands for shares, snapshots, share-networks, and share-groups. python-manilaclient-2.1.0/releasenotes/notes/share-network-multiple-subnets-732309abfbf5987c.yaml0000664000175000017500000000104613644133413032554 0ustar zuulzuul00000000000000--- features: - Added CLI commands to get, add and delete share network subnets. - Updated CLI command for managing share servers to accept ``share_network_subnet`` parameter. - Deprecated ``neutron_subnet_id`` parameter from CLI command to update a share network. - Updated CLI command for listing share servers to show a new column ``Share Network Subnet Id``, and to accept a filter parameter ``share_network_subnet``. fixes: - Fixed share replica create API to make the replica inherit parent share's share network. ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/remove-nova-net-id-option-for-share-nets-82e424b75221528b.yamlpython-manilaclient-2.1.0/releasenotes/notes/remove-nova-net-id-option-for-share-nets-82e424b75221520000664000175000017500000000042013644133413032572 0ustar zuulzuul00000000000000--- upgrade: - | Added new microversion in which ``nova-net-id`` option is no longer allowed for share-network create or update commands. Nova networking was deprecated in OpenStack in Newton and is no longer supported for general deployments in Ocata. python-manilaclient-2.1.0/releasenotes/notes/deprecate-v1-a0cfa6fd723c2f46.yaml0000664000175000017500000000111513644133413027051 0ustar zuulzuul00000000000000--- deprecations: - | v1.Client is deprecated and will be removed with the 2.0.0 release of python-manilaclient. Please use v2.Client. - | Some kwargs when creating a Client instance (for v1.Client and v2.Client) are deprecated and will be removed with the 2.0.0 release of python-manilaclient. The arguments are 'share_service_name', 'proxy_tenant_id', 'proxy_token', 'os_cache' and 'api_key'. - | The method 'authenticate()' for a v1.Client and v2.Client instance is deprecated and will be removed with the 2.0.0 release of python-manilaclient. python-manilaclient-2.1.0/releasenotes/notes/migration-share-type-4fc3b7c6187f5201.yaml0000664000175000017500000000012213644133413030422 0ustar zuulzuul00000000000000--- features: - Added parameter to change share type when performing migration. python-manilaclient-2.1.0/releasenotes/notes/bp-ocata-migration-improvements-f63c5d233856fbee.yaml0000664000175000017500000000052313644133413032732 0ustar zuulzuul00000000000000--- features: - Added 'preserve-snapshots' to migration-start command. upgrades: - Share migration driver-assisted parameters are now mandatory. deprecations: - Support for the experimental share migration APIs has been dropped for API microversions prior to 2.29. fixes: - Updated descriptions of migration-start parameters. python-manilaclient-2.1.0/releasenotes/notes/type-create-extra-specs-691572762357ef3b.yaml0000664000175000017500000000011113644133413030702 0ustar zuulzuul00000000000000--- fixes: - User can specify any number of extra-specs in type_create. ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bug-1814094-fix-_get_base_url-method-168e12292aeec8f1.yamlpython-manilaclient-2.1.0/releasenotes/notes/bug-1814094-fix-_get_base_url-method-168e12292aeec8f1.y0000664000175000017500000000035313644133413032206 0ustar zuulzuul00000000000000--- fixes: - | `Launchpad bug 1814094 `_ has been fixed and the client now correctly parses the base URL from manila's endpoint url, accounting for proxy URLs. python-manilaclient-2.1.0/releasenotes/notes/support-show-type-6380b7c539c95ba8.yaml0000664000175000017500000000006513644133413030043 0ustar zuulzuul00000000000000--- features: - Support show type details command. ././@LongLink0000000000000000000000000000017300000000000011216 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bug-1733494-allow-user-group-name-with-blank-access-fix-885a3e42bdc985ec.yamlpython-manilaclient-2.1.0/releasenotes/notes/bug-1733494-allow-user-group-name-with-blank-access-fix0000664000175000017500000000016313644133413033113 0ustar zuulzuul00000000000000--- fixes: - Allows the use of blank in user group name since the AD allow user group name to include blank. python-manilaclient-2.1.0/releasenotes/notes/share-revert-to-snapshot-e899a4b7e1126749.yaml0000664000175000017500000000010413644133413031176 0ustar zuulzuul00000000000000--- features: - Added support for the revert-to-snapshot feature. ././@LongLink0000000000000000000000000000015700000000000011220 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bp-support-query-user-messages-by-timestamp-34b70bba2d1b4d13.yamlpython-manilaclient-2.1.0/releasenotes/notes/bp-support-query-user-messages-by-timestamp-34b70bba2d10000664000175000017500000000023313644133413033440 0ustar zuulzuul00000000000000--- features: - Added ``since`` and ``before`` to messages list API. User messages can be queried by stimestamp with API version ``2.52`` and beyond.python-manilaclient-2.1.0/releasenotes/notes/add-dash-dash-help-subcomand-ad4226454aa07bc6.yaml0000664000175000017500000000013213644133413031700 0ustar zuulzuul00000000000000--- fixes: - Allow --help to print subcomands info. e.g. ``$ manila create --help`` ././@LongLink0000000000000000000000000000016300000000000011215 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/add-share-replicas-and-replica-gigabytes-quotas-909436c2b2420f2c.yamlpython-manilaclient-2.1.0/releasenotes/notes/add-share-replicas-and-replica-gigabytes-quotas-909436c0000664000175000017500000000051513644133413033274 0ustar zuulzuul00000000000000--- features: - | Added support for two new quotas for share replicas: `share_replicas` and `replica_gigabytes`. upgrade: - | Due to the new 'share_replicas' and 'replica_gigabytes' quotas for share replicas, it is now possible to hit an 'over limit' error while creating replicated shares and share replicas.././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bug-1724183-add-share-type-description-8c4b26f44b5da1e9.yamlpython-manilaclient-2.1.0/releasenotes/notes/bug-1724183-add-share-type-description-8c4b26f44b5da1e90000664000175000017500000000007713644133413032316 0ustar zuulzuul00000000000000--- fixes: - Added description in list/create share type CLI.././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000python-manilaclient-2.1.0/releasenotes/notes/bug-1558995-fix-endpoint-not-found.yaml-a06a3b0ba5006718.yamlpython-manilaclient-2.1.0/releasenotes/notes/bug-1558995-fix-endpoint-not-found.yaml-a06a3b0ba5006710000664000175000017500000000014113644133413032173 0ustar zuulzuul00000000000000--- fixes: - | Fix endpoint not found error when only the manilav2 endpoint is configured. python-manilaclient-2.1.0/releasenotes/notes/bug_1606168_fix-54d3c3bb78389f01.yaml0000664000175000017500000000007713644133413026725 0ustar zuulzuul00000000000000--- fixes: - Fix error handling for os-token and bypass-url. python-manilaclient-2.1.0/python_manilaclient.egg-info/0000775000175000017500000000000013644133466023260 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/python_manilaclient.egg-info/entry_points.txt0000664000175000017500000000311013644133465026550 0ustar zuulzuul00000000000000[console_scripts] manila = manilaclient.shell:main [openstack.cli.extension] share = manilaclient.osc.plugin [openstack.share.v2] share_access_create = manilaclient.osc.v2.share_access_rules:ShareAccessAllow share_access_delete = manilaclient.osc.v2.share_access_rules:ShareAccessDeny share_access_list = manilaclient.osc.v2.share_access_rules:ListShareAccess share_access_set = manilaclient.osc.v2.share_access_rules:SetShareAccess share_access_show = manilaclient.osc.v2.share_access_rules:ShowShareAccess share_access_unset = manilaclient.osc.v2.share_access_rules:UnsetShareAccess share_create = manilaclient.osc.v2.share:CreateShare share_delete = manilaclient.osc.v2.share:DeleteShare share_list = manilaclient.osc.v2.share:ListShare share_set = manilaclient.osc.v2.share:SetShare share_show = manilaclient.osc.v2.share:ShowShare share_type_access_create = manilaclient.osc.v2.share_type_access:ShareTypeAccessAllow share_type_access_delete = manilaclient.osc.v2.share_type_access:ShareTypeAccessDeny share_type_access_list = manilaclient.osc.v2.share_type_access:ListShareTypeAccess share_type_create = manilaclient.osc.v2.share_types:CreateShareType share_type_delete = manilaclient.osc.v2.share_types:DeleteShareType share_type_list = manilaclient.osc.v2.share_types:ListShareType share_type_set = manilaclient.osc.v2.share_types:SetShareType share_type_show = manilaclient.osc.v2.share_types:ShowShareType share_type_unset = manilaclient.osc.v2.share_types:UnsetShareType share_unset = manilaclient.osc.v2.share:UnsetShare [oslo.config.opts] manilaclient.config = manilaclient.config:list_opts python-manilaclient-2.1.0/python_manilaclient.egg-info/pbr.json0000664000175000017500000000005613644133465024736 0ustar zuulzuul00000000000000{"git_version": "3371c54", "is_release": true}python-manilaclient-2.1.0/python_manilaclient.egg-info/SOURCES.txt0000664000175000017500000003233413644133466025151 0ustar zuulzuul00000000000000.stestr.conf .zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING LICENSE README.rst lower-constraints.txt requirements.txt run_tests.sh setup.cfg setup.py test-requirements.txt tox.ini contrib/ci/post_test_hook.sh contrib/ci/pre_test_hook.sh doc/.gitignore doc/Makefile doc/requirements.txt doc/source/conf.py doc/source/index.rst doc/source/cli/.gitkeep doc/source/contributor/functional-tests.rst doc/source/contributor/index.rst doc/source/user/api.rst doc/source/user/shell.rst etc/manilaclient/README.manilaclient.conf etc/oslo-config-generator/manilaclient.conf manilaclient/__init__.py manilaclient/api_versions.py manilaclient/base.py manilaclient/client.py manilaclient/config.py manilaclient/exceptions.py manilaclient/extension.py manilaclient/shell.py manilaclient/utils.py manilaclient/common/__init__.py manilaclient/common/_i18n.py manilaclient/common/cliutils.py manilaclient/common/constants.py manilaclient/common/httpclient.py manilaclient/common/apiclient/__init__.py manilaclient/common/apiclient/base.py manilaclient/common/apiclient/exceptions.py manilaclient/common/apiclient/utils.py manilaclient/osc/__init__.py manilaclient/osc/plugin.py manilaclient/osc/utils.py manilaclient/osc/v2/__init__.py manilaclient/osc/v2/share.py manilaclient/osc/v2/share_access_rules.py manilaclient/osc/v2/share_type_access.py manilaclient/osc/v2/share_types.py manilaclient/tests/__init__.py manilaclient/tests/functional/__init__.py manilaclient/tests/functional/base.py manilaclient/tests/functional/client.py manilaclient/tests/functional/exceptions.py manilaclient/tests/functional/test_availability_zones.py manilaclient/tests/functional/test_common.py manilaclient/tests/functional/test_export_locations.py manilaclient/tests/functional/test_limits.py manilaclient/tests/functional/test_messages.py manilaclient/tests/functional/test_quotas.py manilaclient/tests/functional/test_scheduler_stats.py manilaclient/tests/functional/test_security_services.py manilaclient/tests/functional/test_services.py manilaclient/tests/functional/test_share_access.py manilaclient/tests/functional/test_share_network_subnets.py manilaclient/tests/functional/test_share_networks.py manilaclient/tests/functional/test_share_replica_export_locations.py manilaclient/tests/functional/test_share_servers.py manilaclient/tests/functional/test_share_types.py manilaclient/tests/functional/test_shares.py manilaclient/tests/functional/test_shares_listing.py manilaclient/tests/functional/test_shares_metadata.py manilaclient/tests/functional/test_snapshot_access.py manilaclient/tests/functional/test_snapshot_instances.py manilaclient/tests/functional/test_snapshot_instances_export_locations.py manilaclient/tests/functional/test_snapshots_export_locations.py manilaclient/tests/functional/utils.py manilaclient/tests/functional/osc/__init__.py manilaclient/tests/functional/osc/base.py manilaclient/tests/functional/osc/test_shares.py manilaclient/tests/unit/__init__.py manilaclient/tests/unit/fakes.py manilaclient/tests/unit/test_api_versions.py manilaclient/tests/unit/test_base.py manilaclient/tests/unit/test_client.py manilaclient/tests/unit/test_functional_utils.py manilaclient/tests/unit/test_shell.py manilaclient/tests/unit/test_utils.py manilaclient/tests/unit/utils.py manilaclient/tests/unit/common/__init__.py manilaclient/tests/unit/common/test_httpclient.py manilaclient/tests/unit/common/apiclient/__init__.py manilaclient/tests/unit/osc/__init__.py manilaclient/tests/unit/osc/osc_fakes.py manilaclient/tests/unit/osc/osc_utils.py manilaclient/tests/unit/osc/v2/__init__.py manilaclient/tests/unit/osc/v2/fakes.py manilaclient/tests/unit/osc/v2/test_share.py manilaclient/tests/unit/osc/v2/test_share_access_rules.py manilaclient/tests/unit/osc/v2/test_share_type.py manilaclient/tests/unit/osc/v2/test_share_type_access.py manilaclient/tests/unit/v1/test_limits.py manilaclient/tests/unit/v1/test_quota_classes.py manilaclient/tests/unit/v1/test_quotas.py manilaclient/tests/unit/v1/test_scheduler_stats.py manilaclient/tests/unit/v1/test_security_services.py manilaclient/tests/unit/v1/test_services.py manilaclient/tests/unit/v1/test_share_networks.py manilaclient/tests/unit/v1/test_share_servers.py manilaclient/tests/unit/v1/test_share_snapshots.py manilaclient/tests/unit/v1/test_share_type_access.py manilaclient/tests/unit/v1/test_share_types.py manilaclient/tests/unit/v1/test_shares.py manilaclient/tests/unit/v2/__init__.py manilaclient/tests/unit/v2/fake_clients.py manilaclient/tests/unit/v2/fakes.py manilaclient/tests/unit/v2/test_availability_zones.py manilaclient/tests/unit/v2/test_client.py manilaclient/tests/unit/v2/test_limits.py manilaclient/tests/unit/v2/test_messages.py manilaclient/tests/unit/v2/test_quota_classes.py manilaclient/tests/unit/v2/test_quotas.py manilaclient/tests/unit/v2/test_scheduler_stats.py manilaclient/tests/unit/v2/test_security_services.py manilaclient/tests/unit/v2/test_services.py manilaclient/tests/unit/v2/test_share_export_locations.py manilaclient/tests/unit/v2/test_share_group_snapshots.py manilaclient/tests/unit/v2/test_share_group_type_access.py manilaclient/tests/unit/v2/test_share_group_types.py manilaclient/tests/unit/v2/test_share_groups.py manilaclient/tests/unit/v2/test_share_instance_export_locations.py manilaclient/tests/unit/v2/test_share_instances.py manilaclient/tests/unit/v2/test_share_network_subnets.py manilaclient/tests/unit/v2/test_share_networks.py manilaclient/tests/unit/v2/test_share_replica_export_locations.py manilaclient/tests/unit/v2/test_share_replicas.py manilaclient/tests/unit/v2/test_share_servers.py manilaclient/tests/unit/v2/test_share_snapshot_export_locations.py manilaclient/tests/unit/v2/test_share_snapshot_instance_export_locations.py manilaclient/tests/unit/v2/test_share_snapshot_instances.py manilaclient/tests/unit/v2/test_share_snapshots.py manilaclient/tests/unit/v2/test_shares.py manilaclient/tests/unit/v2/test_shell.py manilaclient/tests/unit/v2/test_type_access.py manilaclient/tests/unit/v2/test_types.py manilaclient/v1/__init__.py manilaclient/v1/client.py manilaclient/v1/limits.py manilaclient/v1/quota_classes.py manilaclient/v1/quotas.py manilaclient/v1/scheduler_stats.py manilaclient/v1/security_services.py manilaclient/v1/services.py manilaclient/v1/share_networks.py manilaclient/v1/share_servers.py manilaclient/v1/share_snapshots.py manilaclient/v1/share_type_access.py manilaclient/v1/share_types.py manilaclient/v1/shares.py manilaclient/v1/contrib/__init__.py manilaclient/v1/contrib/list_extensions.py manilaclient/v2/__init__.py manilaclient/v2/availability_zones.py manilaclient/v2/client.py manilaclient/v2/limits.py manilaclient/v2/messages.py manilaclient/v2/quota_classes.py manilaclient/v2/quotas.py manilaclient/v2/scheduler_stats.py manilaclient/v2/security_services.py manilaclient/v2/services.py manilaclient/v2/share_access_rules.py manilaclient/v2/share_export_locations.py manilaclient/v2/share_group_snapshots.py manilaclient/v2/share_group_type_access.py manilaclient/v2/share_group_types.py manilaclient/v2/share_groups.py manilaclient/v2/share_instance_export_locations.py manilaclient/v2/share_instances.py manilaclient/v2/share_network_subnets.py manilaclient/v2/share_networks.py manilaclient/v2/share_replica_export_locations.py manilaclient/v2/share_replicas.py manilaclient/v2/share_servers.py manilaclient/v2/share_snapshot_export_locations.py manilaclient/v2/share_snapshot_instance_export_locations.py manilaclient/v2/share_snapshot_instances.py manilaclient/v2/share_snapshots.py manilaclient/v2/share_type_access.py manilaclient/v2/share_types.py manilaclient/v2/shares.py manilaclient/v2/shell.py manilaclient/v2/contrib/__init__.py manilaclient/v2/contrib/list_extensions.py playbooks/legacy/manilaclient-dsvm-neutron-functional/post.yaml playbooks/legacy/manilaclient-dsvm-neutron-functional/run.yaml python_manilaclient.egg-info/PKG-INFO python_manilaclient.egg-info/SOURCES.txt python_manilaclient.egg-info/dependency_links.txt python_manilaclient.egg-info/entry_points.txt python_manilaclient.egg-info/not-zip-safe python_manilaclient.egg-info/pbr.json python_manilaclient.egg-info/requires.txt python_manilaclient.egg-info/top_level.txt rally-jobs/rally-manila-no-ss.yaml rally-jobs/rally-manila.yaml releasenotes/notes/.placeholder releasenotes/notes/add-count-info-in-share-21a6b36c0f4c87b2.yaml releasenotes/notes/add-dash-dash-help-subcomand-ad4226454aa07bc6.yaml releasenotes/notes/add-export-location-filter-4cf3114doe40k598.yaml releasenotes/notes/add-ipv6-access-type-4dko90r1a9a1e0b8.yaml releasenotes/notes/add-like-filter-591572762357ef4b.yaml releasenotes/notes/add-message-list-and-delete-41b3323edd63d894.yaml releasenotes/notes/add-metadata-for-share-access-rule-11a6b36c0f4c87c2.yaml releasenotes/notes/add-query-params-pools-list-api-12cf1s14ddf40kdd.yaml releasenotes/notes/add-quota-per-share-type-support-3b2708ea232e69bc.yaml releasenotes/notes/add-share-group-quotas-support-b6563cec58209a1d.yaml releasenotes/notes/add-share-group-support-a3166f6ca4d06a81.yaml releasenotes/notes/add-share-replicas-and-replica-gigabytes-quotas-909436c2b2420f2c.yaml releasenotes/notes/add-snapshot-instances-admin-api-3cf3114doe40k598.yaml releasenotes/notes/add-support-filter-search-for-share-type-fdbaaa9510cc59dd.yaml releasenotes/notes/add-support-to-check-quota-usage-scfdg14dod40k71a.yaml releasenotes/notes/bp-export-locations-replica-az-commands-03aa32c08f59c42d.yaml releasenotes/notes/bp-ocata-migration-improvements-f63c5d233856fbee.yaml releasenotes/notes/bp-support-query-user-messages-by-timestamp-34b70bba2d1b4d13.yaml releasenotes/notes/bp-update-share-type-name-or-description-32d98b5a42cd8090.yaml releasenotes/notes/bug-1558995-fix-endpoint-not-found.yaml-a06a3b0ba5006718.yaml releasenotes/notes/bug-1608664-add-support-columns-share-replica-list-885bd8c8b4bfa8f1.yaml releasenotes/notes/bug-1611506-allow-deletion-multiple-resources releasenotes/notes/bug-1622540-add-missing-az-list-command-f90265de5c2f261b.yaml releasenotes/notes/bug-1650774-share-instance-reset-state-9c4b26f44b5da0e9.yaml releasenotes/notes/bug-1664877-c462bfad92ce03e5.yaml releasenotes/notes/bug-1674915-allow-user-access-fix-495b3e42bdc985ec.yaml releasenotes/notes/bug-1682726-remove-sort-key-export-location-from-list-885des26bd5ea2de.yaml releasenotes/notes/bug-1707303-fix-list-command-when-not-given-search-opts-c06af7b344e9cb91.yaml releasenotes/notes/bug-1709746-add-group-specs-in-share-group-type-create-command-f91265de5c2f251b.yaml releasenotes/notes/bug-1717940-dash-dash-column-reports-nothing-for-capabilities-db8c1234fae91af5.yaml releasenotes/notes/bug-1724183-add-share-type-description-8c4b26f44b5da1e9.yaml releasenotes/notes/bug-1733494-allow-user-group-name-with-blank-access-fix-885a3e42bdc985ec.yaml releasenotes/notes/bug-1738917-and-1738918-fix-access-share-group-type-by-name-3a8760522c147f28.yaml releasenotes/notes/bug-1799934-add-ou-parameter-260f9aaf939d1919.yaml releasenotes/notes/bug-1802059-fix-is_default-a8d3d95ffa0aede9.yaml releasenotes/notes/bug-1811516-bug-1811627-python3-fixes-4b26130027b2c076.yaml releasenotes/notes/bug-1814094-fix-_get_base_url-method-168e12292aeec8f1.yaml releasenotes/notes/bug-1830677-fix-13b30d6a89f43246.yaml releasenotes/notes/bug-share-access-list-3cf3114doe40k599.yaml releasenotes/notes/bug_1570085_fix-905786b797379309.yaml releasenotes/notes/bug_1603387_fix_env_variable_8ed5450aab41aa5f.yaml releasenotes/notes/bug_1606168_fix-54d3c3bb78389f01.yaml releasenotes/notes/bug_1715769_fix-3ec701b0fb9d7910.yaml releasenotes/notes/bug_1777849_1779935_fix-344cb8f09b7df502.yaml releasenotes/notes/bug_1782672-1954059b373f03de.yaml releasenotes/notes/deprecate-v1-a0cfa6fd723c2f46.yaml releasenotes/notes/drop-python2-support-71c7b9e1dcf8c890.yaml releasenotes/notes/expose-access-key-in-access-list-API-b57c386c9048ae55.yaml releasenotes/notes/fix-and-improve-access-rules-a118a7f8e22f65bb.yaml releasenotes/notes/fix-is-default-empty-557844001e0401e2.yaml releasenotes/notes/handle-missing-api-minor-version-5a6d242f28883442.yaml releasenotes/notes/manage-unmanage-share-servers-8c7b27a1fe80e5fa.yaml releasenotes/notes/migration-share-type-4fc3b7c6187f5201.yaml releasenotes/notes/mountable_snapshot-ced01da7dffc6d7e.yaml releasenotes/notes/newton-migration-improvements-166a03472948bdef.yaml releasenotes/notes/remove-experimental-flag-from-share-groups-feature-dcf2b0b67fe4cac4.yaml releasenotes/notes/remove-nova-net-id-option-for-share-nets-82e424b75221528b.yaml releasenotes/notes/share-network-multiple-subnets-732309abfbf5987c.yaml releasenotes/notes/share-revert-to-snapshot-e899a4b7e1126749.yaml releasenotes/notes/start-using-reno-b744cd0259c7a88c.yaml releasenotes/notes/support-create-share-from-snapshot-extra-spec-cdba92f179c1c5c6.yaml releasenotes/notes/support-show-type-6380b7c539c95ba8.yaml releasenotes/notes/type-create-extra-specs-691572762357ef3b.yaml releasenotes/notes/update-api-version-create-share-from-snapshot-another-pool-or-backend-694cfda84a41c4ff.yaml releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/newton.rst releasenotes/source/ocata.rst releasenotes/source/pike.rst releasenotes/source/queens.rst releasenotes/source/rocky.rst releasenotes/source/stein.rst releasenotes/source/train.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder tools/generate_authors.sh tools/install_venv.py tools/manila.bash_completion tools/with_venv.shpython-manilaclient-2.1.0/python_manilaclient.egg-info/PKG-INFO0000664000175000017500000001325313644133465024360 0ustar zuulzuul00000000000000Metadata-Version: 1.2 Name: python-manilaclient Version: 2.1.0 Summary: Client library for OpenStack Manila API. Home-page: https://docs.openstack.org/python-manilaclient/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/python-manilaclient.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on Python bindings to the OpenStack Manila API =========================================== .. only: html .. image:: https://img.shields.io/pypi/v/python-manilaclient.svg :target: https://pypi.org/project/python-manilaclient/ :alt: Latest Version This is a client for the OpenStack Manila API. There's a Python API (the ``manilaclient`` module), and a command-line script (``manila``). Each implements 100% of the OpenStack Manila API. See the `OpenStack CLI guide`_ for information on how to use the ``manila`` command-line tool. You may also want to look at the `OpenStack API documentation`_. .. _OpenStack CLI Guide: https://docs.openstack.org/python-openstackclient/latest/cli/ .. _OpenStack API documentation: https://docs.openstack.org/api/ The project is hosted on `Launchpad`_, where bugs can be filed. The code is hosted on `Github`_. Patches must be submitted using `Gerrit`_, *not* Github pull requests. .. _Github: https://github.com/openstack/python-manilaclient .. _Launchpad: https://launchpad.net/python-manilaclient .. _Gerrit: https://docs.openstack.org/infra/manual/developers.html#development-workflow This code is a fork of `Cinderclient`_ of Grizzly release and then it was developed separately. Cinderclient code is a fork of `Jacobian's python-cloudservers`__ If you need API support for the Rackspace API solely or the BSD license, you should use that repository. python-manilaclient is licensed under the Apache License like the rest of OpenStack. .. _Cinderclient: https://github.com/openstack/python-cinderclient __ https://github.com/jacobian/python-cloudservers .. contents:: Contents: :local: Command-line API ---------------- Installing this package gets you a shell command, ``manila``, that you can use to interact with any Rackspace compatible API (including OpenStack). You'll need to provide your OpenStack username and password. You can do this with the ``--os-username``, ``--os-password`` and ``--os-tenant-name`` params, but it's easier to just set them as environment variables:: export OS_USERNAME=foouser export OS_PASSWORD=barpass export OS_TENANT_NAME=fooproject You will also need to define the authentication url either with param ``--os-auth-url`` or as an environment variable:: export OS_AUTH_URL=http://example.com:5000/v2.0/ Since Keystone can return multiple regions in the Service Catalog, you can specify the one you want with ``--os-region-name`` (or ``export OS_REGION_NAME``). It defaults to the first in the list returned. You'll find complete documentation on the shell by running ``manila help``, see ``manila help COMMAND`` for help on a specific command. Python API ---------- There's also a complete Python API, but it has not yet been documented. Quick-start using keystone:: # use v2.0 auth with http://example.com:5000/v2.0/ >>> from manilaclient.v1 import client >>> nt = client.Client(USER, PASS, TENANT, AUTH_URL, service_type="share") >>> nt.shares.list() [...] * License: Apache License, Version 2.0 * `PyPi`_ - package installation * `Online Documentation`_ * `Launchpad project`_ - release management * `Blueprints`_ - feature specifications * `Bugs`_ - issue tracking * `Source`_ * `How to Contribute`_ * `Release Notes`_ .. _PyPi: https://pypi.org/project/python-manilaclient .. _Online Documentation: https://docs.openstack.org/python-manilaclient/latest/ .. _Launchpad project: https://launchpad.net/python-manilaclient .. _Blueprints: https://blueprints.launchpad.net/python-manilaclient .. _Bugs: https://bugs.launchpad.net/python-manilaclient .. _Source: https://opendev.org/openstack/python-manilaclient .. _How to Contribute: https://docs.openstack.org/infra/manual/developers.html .. _Release Notes: https://docs.openstack.org/releasenotes/python-manilaclient Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Environment :: OpenStack Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=3.6 python-manilaclient-2.1.0/python_manilaclient.egg-info/dependency_links.txt0000664000175000017500000000000113644133465027325 0ustar zuulzuul00000000000000 python-manilaclient-2.1.0/python_manilaclient.egg-info/top_level.txt0000664000175000017500000000001513644133465026005 0ustar zuulzuul00000000000000manilaclient python-manilaclient-2.1.0/python_manilaclient.egg-info/not-zip-safe0000664000175000017500000000000113644133465025505 0ustar zuulzuul00000000000000 python-manilaclient-2.1.0/python_manilaclient.egg-info/requires.txt0000664000175000017500000000041413644133465025656 0ustar zuulzuul00000000000000pbr!=2.1.0,>=2.0.0 oslo.config>=5.2.0 oslo.log>=3.36.0 oslo.serialization!=2.19.1,>=2.18.0 oslo.utils>=3.33.0 PrettyTable<0.8,>=0.7.1 requests>=2.14.2 simplejson>=3.5.1 Babel!=2.4.0,>=2.3.4 six>=1.10.0 osc-lib>=1.10.0 python-keystoneclient>=3.8.0 debtcollector>=1.2.0 python-manilaclient-2.1.0/README.rst0000664000175000017500000000777013644133413017217 0ustar zuulzuul00000000000000======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/python-manilaclient.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on Python bindings to the OpenStack Manila API =========================================== .. only: html .. image:: https://img.shields.io/pypi/v/python-manilaclient.svg :target: https://pypi.org/project/python-manilaclient/ :alt: Latest Version This is a client for the OpenStack Manila API. There's a Python API (the ``manilaclient`` module), and a command-line script (``manila``). Each implements 100% of the OpenStack Manila API. See the `OpenStack CLI guide`_ for information on how to use the ``manila`` command-line tool. You may also want to look at the `OpenStack API documentation`_. .. _OpenStack CLI Guide: https://docs.openstack.org/python-openstackclient/latest/cli/ .. _OpenStack API documentation: https://docs.openstack.org/api/ The project is hosted on `Launchpad`_, where bugs can be filed. The code is hosted on `Github`_. Patches must be submitted using `Gerrit`_, *not* Github pull requests. .. _Github: https://github.com/openstack/python-manilaclient .. _Launchpad: https://launchpad.net/python-manilaclient .. _Gerrit: https://docs.openstack.org/infra/manual/developers.html#development-workflow This code is a fork of `Cinderclient`_ of Grizzly release and then it was developed separately. Cinderclient code is a fork of `Jacobian's python-cloudservers`__ If you need API support for the Rackspace API solely or the BSD license, you should use that repository. python-manilaclient is licensed under the Apache License like the rest of OpenStack. .. _Cinderclient: https://github.com/openstack/python-cinderclient __ https://github.com/jacobian/python-cloudservers .. contents:: Contents: :local: Command-line API ---------------- Installing this package gets you a shell command, ``manila``, that you can use to interact with any Rackspace compatible API (including OpenStack). You'll need to provide your OpenStack username and password. You can do this with the ``--os-username``, ``--os-password`` and ``--os-tenant-name`` params, but it's easier to just set them as environment variables:: export OS_USERNAME=foouser export OS_PASSWORD=barpass export OS_TENANT_NAME=fooproject You will also need to define the authentication url either with param ``--os-auth-url`` or as an environment variable:: export OS_AUTH_URL=http://example.com:5000/v2.0/ Since Keystone can return multiple regions in the Service Catalog, you can specify the one you want with ``--os-region-name`` (or ``export OS_REGION_NAME``). It defaults to the first in the list returned. You'll find complete documentation on the shell by running ``manila help``, see ``manila help COMMAND`` for help on a specific command. Python API ---------- There's also a complete Python API, but it has not yet been documented. Quick-start using keystone:: # use v2.0 auth with http://example.com:5000/v2.0/ >>> from manilaclient.v1 import client >>> nt = client.Client(USER, PASS, TENANT, AUTH_URL, service_type="share") >>> nt.shares.list() [...] * License: Apache License, Version 2.0 * `PyPi`_ - package installation * `Online Documentation`_ * `Launchpad project`_ - release management * `Blueprints`_ - feature specifications * `Bugs`_ - issue tracking * `Source`_ * `How to Contribute`_ * `Release Notes`_ .. _PyPi: https://pypi.org/project/python-manilaclient .. _Online Documentation: https://docs.openstack.org/python-manilaclient/latest/ .. _Launchpad project: https://launchpad.net/python-manilaclient .. _Blueprints: https://blueprints.launchpad.net/python-manilaclient .. _Bugs: https://bugs.launchpad.net/python-manilaclient .. _Source: https://opendev.org/openstack/python-manilaclient .. _How to Contribute: https://docs.openstack.org/infra/manual/developers.html .. _Release Notes: https://docs.openstack.org/releasenotes/python-manilaclient python-manilaclient-2.1.0/.stestr.conf0000664000175000017500000000011213644133413017760 0ustar zuulzuul00000000000000[DEFAULT] test_path=${OS_TEST_PATH:-./manilaclient/tests/unit} top_dir=./ python-manilaclient-2.1.0/test-requirements.txt0000664000175000017500000000076113644133413021762 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. # hacking should be first hacking>=3.0,<4.0.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 ddt>=1.0.1 # MIT fixtures>=3.0.0 # Apache-2.0/BSD mock>=2.0.0 # BSD os-testr>=1.0.0 # Apache-2.0 tempest>=17.1.0 # Apache-2.0 testtools>=2.2.0 # MIT python-openstackclient>=3.12.0 # Apache-2.0 python-manilaclient-2.1.0/tox.ini0000664000175000017500000000532013644133413017030 0ustar zuulzuul00000000000000[tox] distribute = False envlist = py3,pep8 minversion = 2.0 skipsdist = True [testenv] basepython = python3 install_command = pip install {opts} {packages} setenv = VIRTUAL_ENV={envdir} OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 whitelist_externals = find deps = -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = find . -type f -name "*.pyc" -delete stestr run {posargs} stestr slowest [testenv:debug] commands = oslo_debug_helper -t manilaclient/tests {posargs} [testenv:pep8] commands = flake8 [testenv:venv] commands = {posargs} [testenv:docs] usedevelop = True deps = -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/doc/requirements.txt whitelist_externals = rm commands = rm -rf doc/build sphinx-build -b html doc/source doc/build/html [testenv:pdf-docs] usedevelop = True deps = {[testenv:docs]deps} whitelist_externals = make commands = sphinx-build -W -b latex doc/source doc/build/pdf make -C doc/build/pdf [testenv:releasenotes] deps = -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/doc/requirements.txt whitelist_externals = rm commands = rm -rf releasenotes/build sphinx-build -a -E -W -d releasenotes/build/doctrees \ -b html releasenotes/source releasenotes/build/html [testenv:functional] setenv = {[testenv]setenv} OS_TEST_PATH = ./manilaclient/tests/functional OS_SHARE_API_VERSION = 2 passenv = OS_* commands = {envdir}/bin/python setup.py install stestr run {posargs} [testenv:genconfig] whitelist_externals = bash commands = {envdir}/bin/python setup.py install {envdir}/bin/oslo-config-generator --config-file etc/oslo-config-generator/manilaclient.conf [testenv:cover] setenv = {[testenv]setenv} PYTHON=coverage run --source manilaclient --parallel-mode commands = stestr run {posargs} coverage combine coverage html -d cover coverage xml -o cover/coverage.xml coverage report [flake8] # F821: undefined name # W503 line break before binary operator # W504 line break after binary operator ignore = F821,W503,W504 builtins = _ # [H106] Don't put vim configuration in source files. # [H203] Use assertIs(Not)None to check for None. # [H904] Delay string interpolations at logging calls. enable-extensions = H106,H203,H904 exclude = .venv,.tox,dist,doc,*egg,build [testenv:lower-constraints] deps = -c{toxinidir}/lower-constraints.txt -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt python-manilaclient-2.1.0/ChangeLog0000664000175000017500000005614213644133465017306 0ustar zuulzuul00000000000000CHANGES ======= 2.1.0 ----- * Remove experimental flag from share groups commands * Update the python manila client api version * Add new share replica quotas to the CLI * Cleanup py27 support * Support query user message by timestamp * Update hacking for Python3 * Implement OSC share type commands * Implement OSC share access rules commands * Add functional tests for OSC * Implement osc share set/unset commands * OSC Fix list for all projects 2.0.0 ----- * Put osc unit tests with all other unit tests * Fix osc delete share and show share tests * [ussuri][goal] Drop python2.7 support * [OSC] Init OpenStack Client implementation * Add more detail to API introduction * Fix update share network tests and skip exception issue * Fix lower bound for sphix in docs/requirements.txt * Update the constraints url * Update master for stable/train 1.29.0 ------ * Add PDF documentation build * Add CLI commands for Share Networks with multiple Subnets * Add update share-type to SDK and CLI 1.28.0 ------ * Add Python 3 Train unit tests * Add "functional tests" documentation * Change the default value of description of type show * Replace git.openstack.org URLs with opendev.org URLs * OpenDev Migration Patch * Dropping the py35 testing * Replace openstack.org git:// URLs with https:// * [doc] Fix command output generation * Update master for stable/stein * base job has changed to bionic * [CI] Bump timeout for manilaclient-dsvm-neutron-functional * Remove docutils requirement 1.27.0 ------ * Add CLI commands for Manage-Unmanage of Share Servers * Fix get\_base\_url * Add default value for 'is\_public' attribute in the help text * [CI] Fix logs for the functional test job 1.26.0 ------ * Convert functional tests to python3 * [Python3] Fix python3 bugs in code * [CI] Use openstackclient instead of neutronclient * Add missing organizational unit (--ou) parameter in manila cli * Run manilaclient-dsvm-neutron-functional on bionic * Return is\_default as a value instead of a function object * Add support for replica export location APIs * Change openstack-dev to openstack-discuss * Add Python 3.6 classifier to setup.cfg * Add Release Notes in README * Remove setup.py check from pep8 job 1.25.0 ------ * Change python3.5 job to python3.7 job on Stein+ * Don't quote {posargs} in tox.ini * Stop encoding "~" in query parameters * Add unit-test for availability\_zones.AvailabilityZone repr() * fix tox python3 overrides * Use templates for cover and lower-constraints * Drop 'clidoc' from sphinx extensions * add python 3.6 unit test job * switch documentation job to new PTI * import zuul job settings from project-config * Fix functional tests * Fix sort options --sort-key and --sort-dir in list command * Fix type-show command with name * Loosen docutils requirement pin * Update reno for stable/rocky 1.24.0 ------ * Fix is\_default value is empty issue when create * Support metadata for access rule resource * Fix using column reports nothing for capabilities * Replace unicode with six.text\_type * Add release note link in README * Add check to the flake8 job * Remove PyPI downloads * fix tox python3 overrides 1.23.0 ------ * allow '--help' to print subcomands info * Delete unused files to increase test coverage * Increase coverage test * Switch to use stestr instead of testr * Support filter search for share type API * Fix failed to import test module * Mark functional tests passwords as secrets * Remove fake\_client module as it is unused * Trivial: Update pypi url to new url * Fix allow the use of blank in user group name to access the share 1.22.0 ------ * Allow clone of manila-tempest-plugin * add lower-constraints job * Follow the new PTI for document build * Drop unused test-requirement * Updated from global requirements * add os-testr to test-requirements * Add unit-test for apiversions.APIVersion.is\_latest * Update links in README * Fix manila endpoint in catalog not find error * Fix 'List' command filters do not accept unicode symbols * Update reno for stable/queens * Clean imports in code * Updated from global requirements * Zuul: Remove project name * Cover string representations of api\_versions.APIVersion 1.21.0 ------ * Added 'description' in share type * Add missing assertions for APIVersion comparison * Add 'count' in list command * Add show share type detail command * Add functional tests for MAPRFS protocal 1.20.0 ------ * Add search\_opts in func list of ManagerWithFind type classes * Fix share can not be found by name in admin context * Updated from global requirements * Avoid tox\_install.sh for constraints support 1.19.0 ------ * Updated from global requirements * Compare API call body in assertions with respect to values * Fix 'List' command filters do not accept unicode symbols * Remove setting of version/release from releasenotes * Updated from global requirements * Updated from global requirements * Updated from global requirements 1.18.0 ------ * Fix missing domain arguments in functional tests * Migrating legacy jobs * Increase code coverage * Increase code coverage * Use generic user for both zuul v2 and v3 * Add MAPRFS protocol in create help * [TrivialFix] Add three kinds of ignorable file to .gitignore * Use more parts from keystoneauth * Fix for use endpoint\_type in \_discover\_client method * Updated from global requirements * Properly deprecate v1.client and some other client related things * Updated from global requirements * Allows the use of dollar sign in usernames to allow for windows computer authentication in an active directory environment * tests: use .stestr.conf except for functional tests * Correct import of keystoneauth1 session * TrivialFix flake8 and docs build in tox.ini * doc-migration: new directory layout * Updated from global requirements * TrivialFix for HACKING file * Add license to doc/source/conf.py * doc migration: openstackdocstheme completion * Updated from global requirements * Group specs can not be set in share group type create CLI * Stop skipping ipv6 delete access rule test * Add rally job manifests * Fix list command when not given search\_opts * Update reno for stable/pike * Updated from global requirements 1.17.1 ------ * Fix manilclient.v2 import error 1.17.0 ------ * Enable IPv6 in manila(client) * Add like filter * Updated from global requirements * Add export-location filter in share and share instance list * Add share group quotas * Add quotas per share type * Add commands for user messages * Update the documentation link for doc migration * Enable some off-by-default checks * Replace http with https * Updated from global requirements * Increases code coverage * Correcting author * Switch from oslosphinx to openstackdocstheme * Updated from global requirements * Updated from global requirements * Updated from global requirements 1.16.0 ------ * Updated from global requirements * Change to share access list API * Updated from global requirements * Replace assertRaisesRegexp with assertRaisesRegex * Updated from global requirements * Incorrect case, parenthesis & periods in CLI Guide * Updated from global requirements * Optimize the link address * Use Identity v3 API always for functional tests * doc: Remove cruft from conf.py * Use Sphinx 1.5 warning-is-error * Explicitly set 'builders' option * Remove unused sort key export\_location from list CLI * Updated from global requirements 1.15.0 ------ * Updated from global requirements * Updated from global requirements * Add release note for fixing bug 1664877 * Replaces uuid.uuid4 with uuidutils.generate\_uuid() * change user access name limit from 32 to 255 characters * Updated from global requirements * Remove log translations * Clean docs/build before building docs * Updated from global requirements * Fix gate breakage * Raise TypeError in manilaclient/common/httpclient.py * Updated from global requirements * Updated from global requirements * Fixed log messages format * Use more specific asserts and fix an assertion in tests * Remove support for py34 * Handle log message interpolation by the logger * Replace six.iteritems() with .items() * Missing parenthesis in help text * Update reno for stable/ocata 1.14.0 ------ * Update help message for share-instance-reset-state command 1.13.0 ------ * Add mountable snapshots support to manila client * Add share group support to Manila client 1.12.0 ------ * Implement Share Migration Ocata improvements * Support changes to access rules APIs * Support share revert to snapshot in Manila client * Add the ability to check tenant quota detail * Remove Nova Net option for share networks * Enable coverage report in console output * Removes unnecessary utf-8 encoding * Add create\_share\_from\_snapshot\_support extra spec * Fix "replication\_type" extra-spec values in tests * Add share\_type filter to pool\_list * Add Constraints support * Use assertGreater(len(x), 0) instead of assertTrue(len(x) > 0) * Updated from global requirements * Configures coverage tool * Show team and repo badges on README * Updated from global requirements * Fix misleading help text * Updated from global requirements * Updated from global requirements * Add Python 3.5 classifier and venv * Switch test runner to ostestr wrapper * Use dummy driver in functional tests CI job * Add support of Identity API V3 in functional tests * Updated from global requirements * Enable release notes translation * Run pre\_test\_hook.sh of manila in client's CI * Fix share-server-delete command * Remove copy of incubated Oslo code * Config: no need to set default=None * Modify use of assertTrue(A in B) * Add reno for availability-zone-list * Add \_\_ne\_\_ built-in function * Add support of Availability Zones API * Allow deletion of multiple resources for some manila commands * Update reno for stable/newton * type-create should support specifying extra-specs 1.11.0 ------ * Add share type change to Share Migration CLI * Update Share Migration CLI * add access\_key to access\_list API's response * Remove ordereddict from test-requirements.txt * Updated from global requirements * Add snapshot instances admin CLIs * Add columns support for share-replica-list * Add validation for token and bypass\_url * Updated from global requirements * Replace OpenStack LLC with OpenStack Foundation * Use consistent env variable naming 1.10.0 ------ * Updated from global requirements * Define experimental API header on python client level instead of CLI * Remove white space between print and () * Updated from global requirements * Updated from global requirements * manila list --all-tenants should display projectID * [CI] Fix devstack hooks to unblock CI * Make dict.keys() PY3 compatible * Handle error for missing minor api version * Remove discover from test-requirements * Updated from global requirements 1.9.0 ----- * Updated from global requirements * Add support for releasenotes * Remove iso8601 and pycrypto useless requirements * Updated from global requirements * Fix view of doc info for functional tests * Update the home-page with developer documentation * Added CONTRIBUTING.rst file to the project * Add possibility to provide auth token via CLI * Fix "single\_alias" action for CLI commands * Updated from global requirements * Hacking version update and pep8 issues fix * Fix list access function comment * Updated from global requirements * Replace tempest-lib with tempest.lib * Updated from global requirements * UnsupportedVersion Thrown for Supported Version * Fix typos 1.8.1 ----- * Revert fix access rules functional tests * Fix typos * Microversion 'cephx' access type in API v2 * Fixed description of auth\_url config parameter 1.8.0 ----- * Add 'cephx' authentication type * Added new CLI commands for Share migration * Use 'example' instead of 'foo' or 'bar' in help outputs * Annotate CLI commands for experimental features * CLI for manage/unmanage snapshot * Support preferred export locations in Manila CLI * Client Support for Manila Share Replication * Fix post\_test\_hook.sh script auth error * Updated from global requirements * To tag all the Admin-only APIs as Admin only * Fix function name * Add missing element in functional test * Eliminate unnecessary character * Fix functional tests to respect insecure SSL option * Updated from global requirements * Fix Access Rules functional tests * Add support of export location metadata feature * Set default access\_level to rw in help * Updated from global requirements 1.7.0 ----- * Fix Mutable default argument * Updated typos * Remove argparse from requirements * Fix backwards compatibility for v1 API imports * Keep using the session we had when creating keystone client * Add uppercase env var names for consistency * Fix the "OpenStack CLI guide" link * Updated from global requirements * Add debug testenv in tox * Removes MANIFEST.in as it is not needed explicitly by PBR * Updated from global requirements * Replace deprecated LOG.warn with warning * Replace assertTrue(isinstance()) with assertIsInstance() * Remove vim header from source files * Show selected columns in list commands of manila 1.6.0 ----- * Remove unreachable code from unit tests * Client fix for api\_version in positional and kwarg * Replace assertEqual(None, \*) with assertIsNone in tests * Updated from global requirements * Fix microversion comparison approach in functional tests * Drop py33 support * Deprecated tox -downloadcache option removed * Fix share size units to match the API * API 'manage' can set share attr 'is\_public' * Updated from global requirements * Add python classifiers to setup.cfg * Allows type-create to return dict instead of list * Updated from global requirements * Remove py26 from tox config * Delete python bytecode before every test run * Updated from global requirements * Fix client backwards compatibility * Updated from global requirements * Add support of new API URLs after port of extensions to core API * Nova Style API Version Support for Client * Manila list shows one export location not multiple * Wrong help for 'manila cg-snapshot-create' command * Fix usage of setUpClass method in functional tests * Updated from global requirements * Add useful links to project README.rst * Add support for enable/disable service API 1.5.0 ----- * Add Keystone v3 API support * Incorrect help of export\_path for the manage utility * Updated from global requirements * Fix args parsing for quota-class-update * Change Manilaclient to use --os-region-name param * Updated from global requirements 1.4.0 ----- * Fix shares CLI for using CGs * Workaround broken openstack client for functional tests * Bump latest supported version to 2.6 * Add consistency group support to manila client 1.3.0 ----- * Fix client API version to support Manila API * Implement Share Instances Admin API * Add Share Migration support to Manila Client * Update path to subunit2html in post\_test\_hook * Manila REST API Microversion Support for Client * Add support of new extra spec 'snapshot\_support' * Allow user to unset security service params * Updated from global requirements * Allow user to unset share network params * Move requirement Openstack client to test-requirements * Updated from global requirements * Make spec\_driver\_handles\_share\_servers required * Modify the manage command prompt information * Updated from global requirements * Add availability zones support * Updated from global requirements * Add functional tests for access rules * Updated from global requirements * Fix post\_test\_hook and update test-requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * Add share shrink API * Updated from global requirements * Updated from global requirements * Add rw functional tests for shares metadata * Add rw functional tests for shares * Updated from global requirements 1.2.0 ----- * Drop incubating theme from docs * Add share extend API * Fix configuration for tox 2.0.x * Rename functional test module from shares to shares\_listing * Increase quota for share networks in manila installation * Updated from global requirements * Updated from global requirements * Drop use of 'oslo' namespace package * Add rw functional tests for share networks * Add rw functional tests for share type extra specs * Add rw functional tests for private share types * Add rw functional tests for public share types * Implement wrapper for ascii table parser from tempest\_lib.cli * Update README to work with release tools 1.1.0 ----- * Uncap library requirements for liberty 1.0.4 ----- * Validate required parameters for token-based authentication * Improve docstrings for share and snapshot API methods * Updated from global requirements 1.0.3 ----- * Implement private share\_types * Print share export locations more pretty * Make public shares visible for all tenants * Add Manila client and CLI support for listing scheduler pools * Remove workaround from type\_create() shell command * Add basic manage/unmanage share functionality * Updated from global requirements * Updated from global requirements * Manila access-allow CLI command doesn't accept backslash * Make extra spec driver\_handles\_share\_servers required * Remove redundant call to API in ShareType.get\_keys() method * Add support of snapshot gigabytes quotas * Add support of nova network to share networks * Add keystone-session support * Updated from global requirements * Add is\_default column to type-list command output * Rename volume\_type to share\_type * Updated from global requirements * Add hint for HDFS protocol while creating shares * Add -d short option for --debug * Remove links field from Manila client share details output * Print expected and actual request body in fake client * Updated from global requirements * Updated from global requirements * Add support of arg "access\_level" to allow\_access operation * Fix share-server-list functional test * Add info about functional tests run to docs * Use print\_list and print\_dict functions from common code * Add read-only functional tests * Sync oslo common cliutils code to fix the prints incompatibility in py3 * Remove nonexistent directory "tests" from pep8 scanning * Sync the oslo commom exceptions file to resolve detailed error message * Add oslo.utils to requirements.txt * Add service id to information printed by console client * Use six.moves.urllib.parse for py2/3 compat * Add manila cli help output to doc * Workflow documentation is now in infra-manual 1.0.2 ----- * Add 'docs' tox job for generation of docs * Remove explicit version definition * Make functional tests ci job save testr logs * Fix common readme info for manilaclient * Fix snapshot-list filter key 'usage' * Updated from global requirements * Add gate hook scripts for functional tests * Implement functionality for functional tests using tempest-lib * Improve documentation * Updated from global requirements * Remove unneeded init file * Add filtering to share-network-list command * Improve snapshots list API filtering * Sync with oslo-incubator * Use oslo.utils * Use oslo.serialization * Updated from global requirements * Move tests into manilaclient package * Add new filters for 'security-service-list' command 1.0.1 ----- * Set manilaclient version greater than last release * Improve share list API filtering * Handle ambiguous option error for aliases properly 1.0.0 ----- * Fix endless loop of getattr for share-server instance * Add temp files of vim and cover tox job to .gitignore file * Use oslosphinx * Update .gitreview file for move to openstack * Stop using intersphinx * Allow search security services by name too * Add .venv and subunit.log to .gitignore * Add GlusterFS protocol as a supported Share/NAS type * Warn against sorting requirements * Add support for cert-based access type * Work toward Python 3.4 support and testing * Improve help strings * Rename 'sid' to 'user' in access rules and sec services * Print delete errors to stderr * Fix deletion of nonexistent share * Cleanup manilaclient.utils module (part 2) * Cleanup manilaclient.utils module (part 1) * Use Resource class from common code * Use getid func from common code * Add new option to the 'list' shell command * Fix run\_tests.sh * Reuse exceptions from common apiclient code * Sync common modules from Oslo * Update requirements and fix pep issues after it * Fix and enable F811 pep8 rule (redefinitions) * Fix and enable H102 pep8 rule (apache license header) * Fix and enable E12 pep8 rule (indentation issues) * Fix docstring in manilaclient.v1.client module * Add an ability to cache auth token * Add support for share-server-delete API * Add .testrepository to .gitignore * Remove py33 incompatibilities * Remove locals() from code base and enable H501 pep/flake rule * Enable H23\* rules in pep/flake (py3-compat) * Enable H30\* rules in pep/flake * Enable H40\* rules in pep/flake * Replace usage of unittest.TestCase with tests.utils.TestCase * Code cleanup: use oslo's to\_slug() instead of slugify() * Replace json with jsonutils from common code * Fix creation of share without share-network * Fix description for share deletion * Update manila docs * Pass share network by name in manilaclient - part 2 * Sync common modules from Oslo * Fixed args descriptions for share-networks * Added force-delete api support * Add ability to pass share network by name in manilaclient * Remove unused imports, reenable pyflakes * Switch to Hacking 0.8 / Pep8 1.4.x * Added share-server api support * Removed share\_network activation API * Skip sdist step in tox setup * Switch to using PBR * Fix various Pep8 1.4.x warnings * Sync with global requirements * Rename to manila.bash\_completion * Rename pip/test-requires to standard naming * Added volume\_types support to manilalcient * Added service-list request * Added type key into security service list view * Added detail parameter to list methods * Added quota for share-networks * Add share-network activate and deactivate commands * Fix misspellings in python manilaclient * Add client API for share-networks and security-services * Adds an ability to delete multiple shares at once * Adds snapshot-reset-state command * Fixes bug with checking length of sid access arg * Adds reset-state command * Adds args check on share and snapshot rename * Removes support of passing ip address using asterisk * Adds an ability to pass share and snapshot names to commands * Renames access management commands * Adds ip prefix validation in allow-access * Rename 'passwd' access type to 'sid' * Changes logic of retrieving share metadata * Adds new API for share metadata operating * Support building wheels (PEP-427) * Added support of per-tenant-user quotas * Fixes using quota-show with default tenant id * Removes deprecated global opts from python-manilaclient/shell.py * Default tenant id for quota\_defaults * Rename functionality implementation for manila client * Add .gitreview file * fixed pep8 * merged cinder-to-manila * access allow test fixed * share create empty body fix * print list fixed * fix for package version * added testr conf. pep8 tests fixed * fixed tests * log files added to gitignore * cleaned log files * cleaned testfiles and pyc files * env folders removed * removed disabled volume functionality * cleaned client with fixed tests. no quotas functionality implemented * renamed service\_type to share * deleted volume v1 * mass replace cinder to manila * fixed service type * fixed service type * setup.py fixed * setup.py fixed * setup.py fixed * cinderclient to manilaclient * Initial checkin * Initial commit python-manilaclient-2.1.0/lower-constraints.txt0000664000175000017500000000320213644133413021750 0ustar zuulzuul00000000000000alabaster==0.7.10 appdirs==1.3.0 asn1crypto==0.23.0 Babel==2.3.4 cffi==1.7.0 cliff==2.8.0 cmd2==0.8.0 coverage==4.0 cryptography==2.1 ddt==1.0.1 debtcollector==1.2.0 decorator==3.4.0 deprecation==1.0 docutils==0.11 dogpile.cache==0.6.2 dulwich==0.15.0 extras==1.0.0 fasteners==0.7.0 fixtures==3.0.0 future==0.16.0 idna==2.6 imagesize==0.7.1 ipaddress==1.0.17 iso8601==0.1.11 Jinja2==2.10 jmespath==0.9.0 jsonpatch==1.16 jsonpointer==1.13 jsonschema==2.6.0 keystoneauth1==3.4.0 linecache2==1.0.0 MarkupSafe==1.0 mock==2.0.0 monotonic==0.6 msgpack-python==0.4.0 munch==2.1.0 netaddr==0.7.18 netifaces==0.10.4 openstackdocstheme==1.32.1 openstacksdk==0.11.2 os-client-config==1.28.0 os-service-types==1.2.0 os-testr==1.0.0 osc-lib==1.10.0 oslo.concurrency==3.25.0 oslo.config==5.2.0 oslo.context==2.19.2 oslo.i18n==3.15.3 oslo.log==3.36.0 oslo.serialization==2.18.0 oslo.utils==3.33.0 paramiko==2.0.0 pbr==2.0.0 positional==1.2.1 prettytable==0.7.1 pyasn1==0.1.8 pycparser==2.18 Pygments==2.2.0 pyinotify==0.9.6 pyOpenSSL==17.1.0 pyparsing==2.1.0 pyperclip==1.5.27 python-cinderclient==3.3.0 python-dateutil==2.5.3 python-glanceclient==2.8.0 python-keystoneclient==3.8.0 python-mimeparse==1.6.0 python-novaclient==9.1.0 python-openstackclient==3.12.0 python-subunit==1.0.0 pytz==2013.6 PyYAML==3.12 reno==2.5.0 requests==2.14.2 requestsexceptions==1.2.0 rfc3986==0.3.1 simplejson==3.5.1 six==1.10.0 snowballstemmer==1.2.1 Sphinx==1.6.5 sphinxcontrib-websupport==1.0.1 sphinxcontrib-programoutput==0.11 stestr==1.0.0 stevedore==1.20.0 tempest==17.1.0 testrepository==0.0.18 testtools==2.2.0 traceback2==1.4.0 unittest2==1.1.0 urllib3==1.21.1 warlock==1.2.0 wrapt==1.7.0 python-manilaclient-2.1.0/CONTRIBUTING.rst0000664000175000017500000000105113644133413020153 0ustar zuulzuul00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps documented at: https://docs.openstack.org/infra/manual/developers.html Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: https://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad, not GitHub: https://bugs.launchpad.net/python-manilaclient python-manilaclient-2.1.0/tools/0000775000175000017500000000000013644133466016665 5ustar zuulzuul00000000000000python-manilaclient-2.1.0/tools/generate_authors.sh0000775000175000017500000000005113644133413022547 0ustar zuulzuul00000000000000#!/bin/bash git shortlog -se | cut -c8- python-manilaclient-2.1.0/tools/manila.bash_completion0000664000175000017500000000055113644133413023207 0ustar zuulzuul00000000000000_manila() { local cur prev opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" opts="$(manila bash_completion)" COMPLETION_CACHE=~/.manilaclient/*/*-cache opts+=" "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ') COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) } complete -F _manila manila python-manilaclient-2.1.0/tools/with_venv.sh0000775000175000017500000000012413644133413021222 0ustar zuulzuul00000000000000#!/bin/bash TOOLS=`dirname $0` VENV=$TOOLS/../.venv source $VENV/bin/activate && $@ python-manilaclient-2.1.0/tools/install_venv.py0000664000175000017500000001644713644133413021747 0ustar zuulzuul00000000000000# Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2010 OpenStack Foundation # # 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. """ Installation script for Nova's development virtualenv """ from __future__ import print_function import optparse import os import subprocess import sys ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) VENV = os.path.join(ROOT, '.venv') PIP_REQUIRES = os.path.join(ROOT, 'requirements.txt') TEST_REQUIRES = os.path.join(ROOT, 'test-requirements.txt') PY_VERSION = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) def die(message, *args): print(message % args, file=sys.stderr) sys.exit(1) def check_python_version(): if sys.version_info < (2, 6): die("Need Python Version >= 2.6") def run_command_with_code(cmd, redirect_output=True, check_exit_code=True): """Runs a command in an out-of-process shell. Runs a command in an out-of-process shell, returning the output of that command. Working directory is ROOT. """ if redirect_output: stdout = subprocess.PIPE else: stdout = None proc = subprocess.Popen(cmd, cwd=ROOT, stdout=stdout) output = proc.communicate()[0] if check_exit_code and proc.returncode != 0: die('Command "%s" failed.\n%s', ' '.join(cmd), output) return (output, proc.returncode) def run_command(cmd, redirect_output=True, check_exit_code=True): return run_command_with_code(cmd, redirect_output, check_exit_code)[0] class Distro(object): def check_cmd(self, cmd): return bool(run_command(['which', cmd], check_exit_code=False).strip()) def install_virtualenv(self): if self.check_cmd('virtualenv'): return if self.check_cmd('easy_install'): print('Installing virtualenv via easy_install...', end=' ') if run_command(['easy_install', 'virtualenv']): print('Succeeded') return else: print('Failed') die('ERROR: virtualenv not found.\n\nDevelopment' ' requires virtualenv, please install it using your' ' favorite package management tool') def post_process(self): """Any distribution-specific post-processing gets done here. In particular, this is useful for applying patches to code inside the venv. """ pass class Debian(Distro): """This covers all Debian-based distributions.""" def check_pkg(self, pkg): return run_command_with_code(['dpkg', '-l', pkg], check_exit_code=False)[1] == 0 def apt_install(self, pkg, **kwargs): run_command(['sudo', 'apt-get', 'install', '-y', pkg], **kwargs) def apply_patch(self, originalfile, patchfile): run_command(['patch', originalfile, patchfile]) def install_virtualenv(self): if self.check_cmd('virtualenv'): return if not self.check_pkg('python-virtualenv'): self.apt_install('python-virtualenv', check_exit_code=False) super(Debian, self).install_virtualenv() class Fedora(Distro): """This covers all Fedora-based distributions. Includes: Fedora, RHEL, CentOS, Scientific Linux. """ def check_pkg(self, pkg): return run_command_with_code(['rpm', '-q', pkg], check_exit_code=False)[1] == 0 def yum_install(self, pkg, **kwargs): run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs) def apply_patch(self, originalfile, patchfile): run_command(['patch', originalfile, patchfile]) def install_virtualenv(self): if self.check_cmd('virtualenv'): return if not self.check_pkg('python-virtualenv'): self.yum_install('python-virtualenv', check_exit_code=False) super(Fedora, self).install_virtualenv() def get_distro(): if (os.path.exists('/etc/fedora-release') or os.path.exists('/etc/redhat-release')): return Fedora() elif os.path.exists('/etc/debian_version'): return Debian() else: return Distro() def check_dependencies(): get_distro().install_virtualenv() def create_virtualenv(venv=VENV, no_site_packages=True): """Creates the virtual environment. Creates the virtual environment installs PIP only into the virtual environment. """ print('Creating venv...', end=' ') if no_site_packages: run_command(['virtualenv', '-q', '--no-site-packages', VENV]) else: run_command(['virtualenv', '-q', VENV]) print('done.') print('Installing pip in virtualenv...', end=' ') if not run_command(['tools/with_venv.sh', 'easy_install', 'pip>1.0']).strip(): die("Failed to install pip.") print('done.') def pip_install(*args): run_command(['tools/with_venv.sh', 'pip', 'install', '--upgrade'] + list(args), redirect_output=False) def install_dependencies(venv=VENV): print('Installing dependencies with pip (this can take a while)...') # First things first, make sure our venv has the latest pip and distribute. pip_install('pip') pip_install('distribute') pip_install('-r', PIP_REQUIRES) pip_install('-r', TEST_REQUIRES) # Tell the virtual env how to "import manila" pthfile = os.path.join( venv, "lib", PY_VERSION, "site-packages", "manilaclient.pth") f = open(pthfile, 'w') f.write("%s\n" % ROOT) def post_process(): get_distro().post_process() def print_help(): help = """ python-manilaclient development environment setup is complete. python-manilaclient development uses virtualenv to track and manage Python dependencies while in development and testing. To activate the python-manilaclient virtualenv for the extent of your current shell session you can run: $ source .venv/bin/activate Or, if you prefer, you can run commands in the virtualenv on a case by case basis by running: $ tools/with_venv.sh Also, make test will automatically use the virtualenv. """ print(help) def parse_args(): """Parse command-line arguments.""" parser = optparse.OptionParser() parser.add_option( "-n", "--no-site-packages", dest="no_site_packages", default=False, action="store_true", help="Do not inherit packages from global Python install", ) return parser.parse_args() def main(argv): (options, args) = parse_args() check_python_version() check_dependencies() create_virtualenv(no_site_packages=options.no_site_packages) install_dependencies() post_process() print_help() if __name__ == '__main__': main(sys.argv)