././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.633288 python-manilaclient-4.8.0/0000775000175000017500000000000000000000000015525 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/.stestr.conf0000664000175000017500000000011200000000000017770 0ustar00zuulzuul00000000000000[DEFAULT] test_path=${OS_TEST_PATH:-./manilaclient/tests/unit} top_dir=./ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286955.0 python-manilaclient-4.8.0/AUTHORS0000664000175000017500000001606500000000000016605 0ustar00zuulzuul00000000000000119Vik Adam Harwell Aleks Chirko Alexander Pugachev Alexey Ovchinnikov Andreas Jaeger Andreas Jaeger Andrei V. Ostapenko Andrey Kurilin Ankit Agrawal Archana Kumari Asha Saravanamohan Ashley Rodriguez Ben Swartzlander Bertrand Lallau Besjana Gjika Bob Callaway CaiqueMello Cao Xuan Hoang ChangBo Guo(gcb) Chen Chris MacNaughton Christian Berendt Chuck Fouts Clinton Knight Cloud User Corey Bryant Cyril Roelandt Daniel Gonzalez Daniel Russell David Sariel Deepak C Shetty Denis Cavalcante Dina Saparbaeva Dirk Mueller Dirk Müller Doug Hellmann Douglas Viroel Dustin Schoenbrun Eduardo Santos Eric Harney Fabio Oliveira Faiz Abidi Felipe Rodrigues Flavio Percoco Franca Mgbogu 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 June Yi Kafilat Adeleke Kiran Pawar Kudyukin Dmitry Leslie Stevens LiuNanke Lucas de Oliveira Luigi Toscano Luisa Ferraz do Amaral M V P Nitesh Maari Tamm Maari Tamm Marc Koderer Matt Riedemann Maurice Escher Nahim Alves de Souza Namrata Sitlani Nguyen Hai Nguyen Hung Phuong OTSUKA, Yuanying Ondřej Nový OpenStack Release Bot Pete Zaitcev Pierre Riteau Ramana Raja Rodrigo Barbieri Rodrigo Barbieri Sascha Peilicke Sean McGinnis Shane Wang Shuquan Huang Spyros Trigazis Stephen Finucane Stephen Finucane Sun Jun Swapnil Kulkarni (coolsvap) Takashi Kajinami Takashi Kajinami Thomas Bechtold Tom Barron Tom Patzig Tony Breeds Tony Xu Valeriy Ponomaryov Victoria Martinez de la Cruz Vida Haririan Vieri <15050873171@163.com> Vincent Untz Vladyslav Drok Vu Cong Tuan Xing Yang Your Name YuehuiLei Yulia Portnova altanai andrebeltrami archanaserver ashrod98 binean bswartz cFouts celenamichaud chenxiangui czl389 daiki kato debeltrami deepak_mourya dengzhaosen devin dineshbhor drngsl e gugug haixin <653186640@qq.com> haixin haixin hassanasghar houming-wang howardlee iswarya_vakati jacky06 ji-xuepeng junboli kafilat-adeleke kayrus kedy kpdev kuangcx kushalaa lijunbo lingyongxu liusheng liuyamin lkuchlan luke.li maaoyu mark.sturdevant melissaml namrata nidhimittalhada paulali pengyuesheng qingszhao ricolin sdailey shu-mutou silvacarloss sneha.gaddam sonu.kumar sriram ramakrishnan sunjia ting.wang tpsilva venkatamahesh vik vkmc vponomaryov wangzhenyu wangzihao wu.chunyang wu.shiming wuchunyang xiaozhuangqing yanjun.fu yara yogesh yuyafei zhangboye zhiguo.li zhongjun zhongjun2 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/CONTRIBUTING.rst0000664000175000017500000000121100000000000020161 0ustar00zuulzuul00000000000000The source repository for this project can be found at: https://opendev.org/openstack/python-manilaclient Pull requests submitted through GitHub are not monitored. To start contributing to OpenStack, follow the steps in the contribution guide to set up and use Gerrit: https://docs.openstack.org/contributors/code-and-documentation/quick-start.html Bugs should be filed on Launchpad https://bugs.launchpad.net/python-manilaclient For more specific information about contributing to this repository, see the python-manilaclient contributor guide: https://docs.openstack.org/python-manilaclient/latest/contributor/contributing.html ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286955.0 python-manilaclient-4.8.0/ChangeLog0000664000175000017500000010125500000000000017303 0ustar00zuulzuul00000000000000CHANGES ======= 4.8.0 ----- * CLI for disable service reason * reno: Update master for unmaintained/yoga * Fix share force delete case * Bump hacking * Fix "per\_share\_gigabytes" in "openstack quota set" * Fix list call when search\_opts isn't provided * Update python classifier in setup.cfg 4.7.0 ----- * Fix default share type resolution in OSC * Fix "test\_share\_access\_show" osc functional test * Fix decoder path and add transfer commands * [OSC] Add OSC Functional Tests Snapshot Instances * Update master for stable/2023.2 4.6.0 ----- * Bump tox version * Implement share backup * Allow restricting access rules * Support for resource locks * Deprecate "manila" CLI client * Add '--count' option for snapshot list API * [CI] switch to using osc for bootstrap 4.5.1 ----- * Enable the option to return the response body * Enable the option to return the response body 4.5.0 ----- * Metadata for Share Network Subnets * Use suitable api version for OSC * Fix share network create command with the AZ option * Add defaultadsite to security service * Update master for stable/2023.1 * Update micversion to 2.77,support share transfer between project 4.3.0 ----- * Add quiesce\_wait\_time for replica promote * Metadata for Share Snapshots * Drop duplicate tox config * Fix tox4 errors * Support --os-key option * Fix functional tests failing with python 3.10 * Fix formatting of OSC server migration check-only 4.2.0 ----- * Fix the Create Share Group from Snap Shot create * Respect OS\_CACERT when using Manila OSC plugin * Switch to 2023.1 Python3 unit tests and generic template name * Update master for stable/zed 4.1.0 ----- * [OSC] Implement Share Server Migration set task-state * Support multiple subnets per AZ * [OSC] Implement Share Server Migration check * Make the OSC plugin handle the --insecure flag * Add 'share-network' option for share replica create * [reno] Announce openstackclient GA * Fixes minor typos and removes redundants lines * [OSC] Implement Share Server Migration Start Command * [OSC] Implement Share Server Migration Show Command * [OSC] Implement Share Restore Command * [OSC] Implement Share Server Migration Cancel and Complete Command * requirements: Bump python-openstackclient version * Ensures API microversion is greater than or equal to '2.69' * Add share group type functional tests for OSC * requirements: Add stestr * [OSC] Don't use default type with snapshot clones * Fix missing space before end quote * [OSC] Fix typos in help messages for security services command * [OSC] Add OSC Functional Tests Export Location * [OSC] Implement Share Migration show and add argument to Share Set Command * Python3.11: fix unit test crashes * [OSC] Fix share deletion when using share group id * [OSC] Add functional tests for share snapshots * Add support for --soft when deleting a share or more shares * [OSC] Implement security services commands * Add functional tests for group type access * [OSC] Add OSC Functional Tests Replica Export Location * [OSC] Implement Share Server Commands * Avoid any capitalization of the name "None" * Fix share server listing with network subnet id * [OSC] Implement Share Group Snapshot Commands * [OSC] Add OSC Functional Tests Network 4.0.0 ----- * Add availability zones functional tests for OSC * Add FIPS testing jobs * [OSC] Add OSC Functional Tests Share Actions * [OSC] Implement Share Migration Cancel and Complete Command * [OSC] Implement Share Migration Start Command * Fix "test\_share\_server\_migration" sporadic failure * Fix test to not check service updated\_at time * [OSC] Add functional tests for share access rules * [OSC] Add functional tests for share services * Add functional tests for OSC User Messages * [OSC] Add OSC Functional Tests Share set unset * Add support for --export-location filtering when listing shares * [OSC] Add functional tests for share types * Avoid using "force" kwarg in extend API * Update Python as per the Zed cycle testing runtime * Add functional tests for "os share limits show" 3.4.0 ----- * Fix incompatible headers on OS share group type list * Add functional tests for "os share pool list" * Fix for displaying export locations for share snapshot * Allow tests to choose client privileges * Drop lower-constraints.txt and its testing * Add --wait flag to the share access rule * fix manager \_list() method when called from another manager * Update master for stable/yoga * Fix typo in unit test 3.3.0 ----- * API 2.69,Manila client support recycle bin * Avoid formatting \*\_extra\_specs unless format is table * Add CLI error notification in case there is no share type * Add --scheduler\_hints to share replica create command * Add a --wait flag to share manage/unmanage * Add Python 3 only classifier * Add missing space in server migration complete * Fix tox to ignore\_basepython\_conflict * Fix typo in subnet delete description * Refactor code from oslo\_incubator 3.2.0 ----- * Implement OSC quota set default and project mandatory * Bump max API microversion * [OSC] Add missing waiters * [OSC] Implement Share Group Type Commands * Update python testing classifier * Move release notes to correct folders * Add Python3 yoga unit tests * [CI] Change rally and lower-constraints jobs * Fix the id attr for share group type access repr * Add export location information to share replica show command * api 2.64, manilaclient support force extend share * [OSC] Implement share network subnet commands * Add support for ~name and ~description filtering when listing shares * Add --wait flag to the mange share server operation 3.1.0 ----- * Fix displaying column headers for OSC list commands * Remove check for args.force * Refractor logs * Add share groups/share group snaps to quota class * Bump lower-constraints * Fix python-manilaclient functional tests * Update master for stable/xena * Add --scheduler\_hints to share create command 3.0.0 ----- * Fix OSC share groups * [OSC] Implement Share Group Commands * Implement OSC share instance export location commands * OSC Implementation for Share Networks * [OSC] Implement Share Limits Show command * [OSC] Implement share instance commands * [OSC] Add Share Snapshot Instances commands * Fix incorrect dict member lookup * [OSC] Implement Share Pools List command * Add --wait flag to the create share group operation * [OSC] Implement Replica Export Locations commands * [OSC] Implement Share Services Commands * [OSC] Implement Availability Zones Command * Fix issue while creating share from snapshot * Fix PDF docs issue * Re-enable shell completion cache * [OSC] Fix the default API\_VERSION in unit tests * Replace deprecated inspect.getargspec * Add --wait flag to the delete share group operation * tests: Remove unused fakes * utils: Remove unicode-related helpers * tools: Remove dead tools * Remove six * Uncap PrettyTable * Add "--wait" option for deleting a share server * Fix typo in quota set error message * [OSC] Implement Share Replica Commands * Changed minversion in tox to 3.18.0 2.7.0 ----- * [TrivialFix] Follow up per-share-gigabytes quota changes * Add per share gigabytes quota to the CLI * [ci] fix neutron services in functional job * setup.cfg: Replace dashes with underscores * Fix error in CLI 'manila list' with sorting key 'availability\_zone' * Further fix to "test\_list\_shares\_by\_status" * Add Python3 xena unit tests * Update master for stable/wallaby * Fix test failures for "test\_list\_shares\_by\_{filter}" 2.6.0 ----- * Add security add/update services for in-use share networks 2.5.0 ----- * Correctly pass mTLS certificate/key to HTTPClient * Add "--wait" option for force-deleting a share/snapshot/share-instance * Change public share tests * [OSC] Implement user messages commands * tox miniversion update to 3.1.1 * Remove some deprecated options * Add profiling support to manilaclient * Forbid users to create shares with the name "None" * [OSC] Implement Share Revert Command * Implement openstack share properties show command * [OSC] Add the option to explicitly set share status * Tighten asserts in some wait tests * Add a waiter to share extend/shrink * [OSC] Implement Share Export Location Commands * [OSC] Implement the second part of share snapshots commands * remove unicode from code * [OSC] Implement Share Adopt & Abandon Commands * Replace deprecated UPPER\_CONSTRAINTS\_FILE variable * Fixes create share from snapshot in OSC 2.4.0 ----- * Fix duplicate wait functional test * Add waiters to OSC share create/delete/resize * Implements usage of '-c /--column' instead of additional logic * Fix undesirable raw Python error * Remove six.moves.urllib * Remove python2 installation from bindep * Add a waiter to share create/delete * Use importlib to take place of imp module * Remove install unnecessary package * Remove install unnecessary packages * Remove install unnecessary packages * Add Python3 wallaby unit tests * Update master for stable/victoria 2.3.0 ----- * Implement the first half of OSC share snapshots commands * Add commands for share server migration * Add noqa markup to intentionally redefined methods * Implement OSC share quota commands * Graduate share replication feature * [goal] Migrate testing to ubuntu focal * Improve OSC test coverage * Add initial documentation for OSC * Implement OSC share resize command * Migrate functional job to native zuulv3 * Use unittest.mock instead of third party lib 2.2.0 ----- * Switch to newer openstackdocstheme and reno versions * Fix hacking min version to 3.0.1 * TrivialFix: Remove unnecessary future imports * [ussuri][goal] Change contributor guide * Add py38 package metadata * Drop install\_command usage in tox * Add Python3 victoria unit tests * Update master for stable/ussuri 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 * Replace "tenant" terminology with "project" * 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/HACKING0000664000175000017500000000434300000000000016520 0ustar00zuulzuul00000000000000Manila 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 """ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/LICENSE0000664000175000017500000002707500000000000016545 0ustar00zuulzuul00000000000000Copyright (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. ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.633288 python-manilaclient-4.8.0/PKG-INFO0000664000175000017500000001351100000000000016623 0ustar00zuulzuul00000000000000Metadata-Version: 1.2 Name: python-manilaclient Version: 4.8.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 :: Only Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Requires-Python: >=3.8 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/README.rst0000664000175000017500000000777000000000000017227 0ustar00zuulzuul00000000000000======================== 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/bindep.txt0000664000175000017500000000071600000000000017533 0ustar00zuulzuul00000000000000# This is a cross-platform list tracking distribution packages needed by tests; # see https://docs.openstack.org/infra/bindep/ for additional information. gettext libffi-dev [platform:dpkg] libffi-devel [platform:rpm] libssl-dev [platform:ubuntu-xenial] locales [platform:debian] python3-all-dev [platform:ubuntu !platform:ubuntu-precise] python3-dev [platform:dpkg] python3-devel [platform:fedora] # PDF Docs package dependencies tex-gyre [platform:dpkg doc] ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5532665 python-manilaclient-4.8.0/doc/0000775000175000017500000000000000000000000016272 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/doc/.gitignore0000664000175000017500000000000700000000000020257 0ustar00zuulzuul00000000000000build/ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/doc/Makefile0000664000175000017500000000616400000000000017741 0ustar00zuulzuul00000000000000# 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." ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/doc/requirements.txt0000664000175000017500000000060300000000000021555 0ustar00zuulzuul00000000000000# 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>=2.2.1 # Apache-2.0 sphinx>=2.0.0,!=2.1.0 # BSD reno>=3.1.0 # Apache-2.0 sphinxcontrib-programoutput>=0.11 # BSD python-openstackclient>=3.12.0 # Apache-2.0 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5532665 python-manilaclient-4.8.0/doc/source/0000775000175000017500000000000000000000000017572 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5532665 python-manilaclient-4.8.0/doc/source/cli/0000775000175000017500000000000000000000000020341 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/doc/source/cli/decoder.rst0000664000175000017500000000071400000000000022502 0ustar00zuulzuul00000000000000============================= OpenStackClient Mapping Guide ============================= The following is a mapping between the legacy ``manila`` CLI client and OpenStackClient. Command options are only shown when necessary. .. only:: html .. csv-table:: :widths: 25, 25, 50 :file: osc/manila.csv :header-rows: 1 .. only:: pdf The mapping can be accessed `here `_ ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5532665 python-manilaclient-4.8.0/doc/source/cli/osc/0000775000175000017500000000000000000000000021125 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/doc/source/cli/osc/manila.csv0000664000175000017500000003205100000000000023104 0ustar00zuulzuul00000000000000manila command,openstack command,command description --version,module list,List the client software version absolute-limits,share limits show --absolute,Print a list of absolute limits for a user access-allow,share access create,Allow access to a given share access-deny,share access delete,Deny access to a share access-list,share access list,Show access list for share access-metadata,share access set --property or share access unset --property,Set or delete metadata on a share access rule access-show,share access show,Show details about a NAS share access rule api-version,versions show --service sharev2,Display the API version information availability-zone-list,share availability zone list,List all availability zones create,share create,Creates a new share credentials,token issue,Show user credentials returned from auth delete,share delete,Remove one or more shares endpoints,endpoint list --service sharev2,Discover endpoints that get returned from the authenticate services extend,share resize,Increases the size of an existing share extra-specs-list,share type list,Print a list of current 'share types and extra specs' (Admin Only) force-delete,share delete --force,Attempt force-delete of share regardless of state list,share list,List NAS shares with filters manage,share adopt,Manage share not handled by Manila message-delete,share message delete,Remove one or more messages message-list,share message list,Lists all messages message-show,share message show,Show details about a message metadata,share set --property or share unset --property,Set or delete metadata on a share metadata-show,share show -c properties,Show metadata of given share metadata-update-all,,Update all metadata of a share migration-cancel,share migration cancel,Cancel migration of a given share when copying migration-complete,share migration complete,Complete migration for a given share migration-get-progress,share migration show,Get migration progress of a given share when copying migration-start,share migration start,Migrate share to a new host pool-list,share pool list,List all backend storage pools known to the scheduler quota-class-show,share quota show --class,List the quotas for a quota class quota-class-update,share quota update --class,Update the quotas for a quota class quota-defaults,share quota show --class default,List the default quotas for a project quota-delete,share quota delete,Delete quota for a project or project/user or project/share-type quota-show,share quota show,List the quotas for a project or user or share type quota-update,share quota set,Update the quotas for a project/user and/or share type rate-limits,share limits show --rate,Print a list of rate limits for a user reset-state,share set --status,Explicitly update the state of a share reset-task-state,share set --task-state,Explicitly update the task state of a share restore,share restore,Restore one or more shares from recycle bin revert-to-snapshot,share revert,Revert a share to the specified snapshot security-service-create,share security service create,Create security service used by project security-service-delete,share security service delete,Delete one or more security services security-service-list,share security service list,Get a list of security services security-service-show,share security service show,Show security service security-service-update,share security service set,Update security service service-disable,share service set --disable,Disables 'manila-share' or 'manila-scheduler' services service-enable,share service set --enable,Enables 'manila-share' or 'manila-scheduler' services service-list,share service list,List all services share-export-location-list,share export location list,List export locations of a given share share-export-location-show,share export location show,Show export location of the share share-group-create,share group create,Creates a new share group share-group-delete,share group delete,Delete one or more share groups share-group-list,share group list,List share groups with filters share-group-reset-state,share group set --status,Explicitly update the state of a share group share-group-show,share group show,Show details about a share group share-group-update,share group set or share group unset,Update a share group share-group-snapshot-create,share group snapshot create,Creates a new share group snapshot share-group-snapshot-delete,share group snapshot delete,Remove one or more share group snapshots share-group-snapshot-list,share group snapshot list,List share group snapshots with filters share-group-snapshot-list-members,share group snapshot members list,List members of a share group snapshot share-group-snapshot-reset-state,share group snapshot set --status,Explicitly update the state of a share group snapshot share-group-snapshot-show,share group snapshot show,Show details about a share group snapshot share-group-snapshot-update,share group snapshot set or share group snapshot unset,Update a share group snapshot share-group-type-access-add,share group type access create,Adds share group type access for the given project share-group-type-access-list,share group type access list,Print access information about a share group type share-group-type-access-remove,share group type access delete,Removes share group type access for the given project share-group-type-create,share group type create,Create a new share group type share-group-type-delete,share group type delete,Delete a specific share group type share-group-type-key,share group type set --group-specs,Set or unset group_spec for a share group type share-group-type-list,share group type list,Print a list of available 'share group types' share-group-type-specs-list,share group type list,Print a list of 'share group types specs' (Admin Only) share-instance-export-location-list,share instance export location list,List export locations of a given share instance share-instance-export-location-show,share instance export location show,Show export location for the share instance share-instance-force-delete,share instance delete,Force-delete the share instance regardless of state share-instance-list,share instance list,List share instances share-instance-reset-state,share instance set --status,Explicitly update the state of a share instance share-instance-show,share instance show,Show details about a share instance share-network-create,share network create,Create a share network to export shares to share-network-delete,share network delete,Delete one or more share networks share-network-list,share network list,Get a list of share networks share-network-reset-state,share network set --status,Explicitly update the state of a share network share-network-security-service-add,share network set --security-service,Associate security service with share network share-network-security-service-add-check,share network set --check-only --new-security-service,Associate security service with share network share-network-security-service-list,share network show,Get list of security services associated with a given share network share-network-security-service-remove,share network unset --security-service,Dissociate security service from share network share-network-security-service-update,share network set --current-security-service --new-security-service,Update a current security service to a new security service share-network-security-service-update-check,share network set --check-only --current-security-service --new-security-service,Check if a security service update on the share network is supported share-network-show,share network show,Retrieve details for a share network share-network-subnet-create,share network subnet create,Add a new subnet into a share network share-network-subnet-create-check,share network subnet create --check-only,Check if a new subnet can be added to a share network share-network-subnet-delete,share network subnet delete,Delete one or more share network subnets share-network-subnet-show,share network subnet show,Show share network subnet share-network-update,share network set or share network unset,Update share network data share-replica-create,share replica create,Create a share replica share-replica-delete,share replica delete,Remove one or more share replicas share-replica-export-location-list,share replica export location list,List export locations of a share replica share-replica-export-location-show,share replica export location show,Show details of a share replica's export location share-replica-list,share replica list,List share replicas share-replica-promote,share replica promote,Promote specified replica to 'active' replica_state share-replica-reset-replica-state,share replica set --replica-state,Explicitly update the 'replica_state' of a share replica share-replica-reset-state,share replica set --status,Explicitly update the 'status' of a share replica share-replica-resync,share replica resync,Attempt to update the share replica with its 'active' mirror share-replica-show,share replica show,Show details about a replica share-server-delete,share server delete,Delete one or more share servers share-server-details,share server show,Show share server details share-server-list,share server list,List all share servers share-server-manage,share server adopt,Manage share server not handled by Manila share-server-migration-cancel,share server migration cancel,Cancels migration of a given share server when copying share-server-migration-check,share server migration start --check-only,Check migration compatibility for a share server with desired properties share-server-migration-complete,share server migration complete,Complete migration for a given share server share-server-migration-get-progress,share server migration show,Get migration progress of a given share server when copying share-server-migration-start,share server migration start,Migrate share server to a new host share-server-reset-state,share server set --status,Explicitly update the state of a share server share-server-reset-task-state,share server set --task-state,Explicitly update the task state of a share share-server-show,share server show,Show share server info share-server-unmanage,share server abandon,Unmanage share server share-transfer-accept,share transfer accept,Accepts a share transfer. share-transfer-create,share transfer create,Creates a share transfer. share-transfer-delete,share transfer delete,Remove one or more transfers share-transfer-list,share transfer list,Lists all transfers share-transfer-show,share transfer show,Delete a transfer show,share show,Show details about a NAS share shrink,share resize,Decreases the size of an existing share snapshot-access-allow,share snapshot access create,Allow read only access to a snapshot snapshot-access-deny,share snapshot access delete,Deny access to a snapshot snapshot-access-list,share snapshot access list,Show access list for a snapshot snapshot-create,share snapshot create,Add a new snapshot snapshot-delete,share snapshot delete,Remove one or more snapshots snapshot-export-location-list,share snapshot export location list,List export locations of a given snapshot snapshot-export-location-show,share snapshot export location show,Show export location of the share snapshot snapshot-force-delete,share snapshot delete --force,Attempt force-deletion of one or more snapshots Regardless of the state snapshot-instance-export-location-list,share snapshot instance export location list,List export locations of a given snapshot instance snapshot-instance-export-location-show,share snapshot instance export location show,Show export location of the share instance snapshot snapshot-instance-list,share snapshot instance list,List share snapshot instances snapshot-instance-reset-state,share snapshot instance set --status,Explicitly update the state of a share snapshot instance snapshot-instance-show,share snapshot instance show,Show details about a share snapshot instance snapshot-list,share snapshot list,List all the snapshots snapshot-manage,share snapshot adopt,Manage share snapshot not handled by Manila snapshot-rename,share snapshot set --name,Rename a snapshot snapshot-reset-state,share snapshot set --status,Explicitly update the state of a snapshot snapshot-show,share snapshot show,Show details about a snapshot snapshot-unmanage,share snapshot abandon,Unmanage one or more share snapshots soft-delete,share delete --soft,Soft delete one or more shares type-access-add,share type access create,Add share type access for the given project type-access-list,share type access list,Print access information about the given share type type-access-remove,share type access delete,Remove share type access for the given project type-create,share type create,Create a new share type type-delete,share type delete,Delete one or more specific share types type-key,share type set --extra-specs,Set or unset extra_spec for a share type type-list,share type list,Print a list of available 'share types' type-show,share type show,Show share type details type-update,share type set or share type unset,Update share type name and/or description and/or visibility unmanage,share abandon,Unmanage share update,share set or share unset,Rename a share bash-completion,complete,Print arguments for bash_completion list-extensions,,List all the os-api extensions that are available ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5532665 python-manilaclient-4.8.0/doc/source/cli/osc/v2/0000775000175000017500000000000000000000000021454 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/doc/source/cli/osc/v2/index.rst0000664000175000017500000001067100000000000023322 0ustar00zuulzuul00000000000000Command Reference ================= List of released CLI commands available in openstack client. These commands can be referenced by executing ``openstack help share``. ====== shares ====== .. autoprogram-cliff:: openstack.share.v2 :command: share create .. autoprogram-cliff:: openstack.share.v2 :command: share list .. autoprogram-cliff:: openstack.share.v2 :command: share show .. autoprogram-cliff:: openstack.share.v2 :command: share delete .. autoprogram-cliff:: openstack.share.v2 :command: share set .. autoprogram-cliff:: openstack.share.v2 :command: share unset .. autoprogram-cliff:: openstack.share.v2 :command: share properties show .. autoprogram-cliff:: openstack.share.v2 :command: share resize .. autoprogram-cliff:: openstack.share.v2 :command: share adopt .. autoprogram-cliff:: openstack.share.v2 :command: share abandon .. autoprogram-cliff:: openstack.share.v2 :command: share export location show .. autoprogram-cliff:: openstack.share.v2 :command: share export location list .. autoprogram-cliff:: openstack.share.v2 :command: share revert .. autoprogram-cliff:: openstack.share.v2 :command: share restore =============== share instances =============== .. autoprogram-cliff:: openstack.share.v2 :command: share instance [!e]* ================== share access rules ================== .. autoprogram-cliff:: openstack.share.v2 :command: share access * =============== share migration =============== .. autoprogram-cliff:: openstack.share.v2 :command: share migration start .. autoprogram-cliff:: openstack.share.v2 :command: share migration cancel .. autoprogram-cliff:: openstack.share.v2 :command: share migration complete .. autoprogram-cliff:: openstack.share.v2 :command: share migration show ============== share networks ============== .. autoprogram-cliff:: openstack.share.v2 :command: share network [!s]* .. autoprogram-cliff:: openstack.share.v2 :command: share network show .. autoprogram-cliff:: openstack.share.v2 :command: share network set ===================== share network subnets ===================== .. autoprogram-cliff:: openstack.share.v2 :command: share network subnet * =========== share types =========== .. autoprogram-cliff:: openstack.share.v2 :command: share type * ============ share quotas ============ .. autoprogram-cliff:: openstack.share.v2 :command: share quota * =============== share snapshots =============== .. autoprogram-cliff:: openstack.share.v2 :command: share snapshot [!i]* ======================== share snapshot instances ======================== .. autoprogram-cliff:: openstack.share.v2 :command: share snapshot instance * =============== user messages =============== .. autoprogram-cliff:: openstack.share.v2 :command: share message * ============== share replicas ============== .. autoprogram-cliff:: openstack.share.v2 :command: share replica * ======================== share availability zones ======================== .. autoprogram-cliff:: openstack.share.v2 :command: share availability zone list ============== share services ============== .. autoprogram-cliff:: openstack.share.v2 :command: share service * ======================= share security services ======================= .. autoprogram-cliff:: openstack.share.v2 :command: share security service * =========== share pools =========== .. autoprogram-cliff:: openstack.share.v2 :command: share pool list ============ share limits ============ .. autoprogram-cliff:: openstack.share.v2 :command: share limits * ============================== share instance export location ============================== .. autoprogram-cliff:: openstack.share.v2 :command: share instance export location * ============ share groups ============ .. autoprogram-cliff:: openstack.share.v2 :command: share group [!ts]* .. autoprogram-cliff:: openstack.share.v2 :command: share group set ================= share group types ================= .. autoprogram-cliff:: openstack.share.v2 :command: share group type * ===================== share group snapshots ===================== .. autoprogram-cliff:: openstack.share.v2 :command: share group snapshot * ============== share servers ============== .. autoprogram-cliff:: openstack.share.v2 :command: share server * ============== resource locks ============== .. autoprogram-cliff:: openstack.share.v2 :command: share lock * ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/doc/source/cli/osc_plugin_cli.rst0000664000175000017500000000323700000000000024071 0ustar00zuulzuul00000000000000================================================ ``openstack share`` Command-Line Interface (CLI) ================================================ .. program:: openstack share Synopsis ======== :program:`openstack [options] share` [command-options] :program:`openstack help share` Description =========== The OpenStack Client plugin interacts with the Manila service through the ``openstack share`` command line interface (CLI). To use the CLI, you must provide your OpenStack username, password, project, auth endpoint and the share API version. You can use configuration options ``--os-username``, ``--os-password``, ``--os-project-name``, ``--os-auth-url`` and ``--os-share-api-version``, or set the corresponding environment variables:: export OS_USERNAME=foo export OS_PASSWORD=bar export OS_TENANT_NAME=foobarproject export OS_AUTH_URL=http://... export OS_SHARE_API_VERSION=2.51 Getting help ============ To get a full list of all possible commands, run:: $ openstack help share To get detailed help for one command, run:: $ openstack help share Examples ======== Get information about the openstack share create command:: $ openstack help share create Create one share:: $ openstack share create NFS 1 --name "myshare" List shares:: $ openstack share list Display a share:: $ openstack share show myshare Delete a share:: $ openstack share delete myshare Extend a 1gb share to 2gb:: $ openstack share resize myshare 2 Shrink a 2gb share to 1gb:: $ openstack share resize myshare 1 Command Reference ================= .. toctree:: :glob: :maxdepth: 3 osc/v2/*././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/doc/source/conf.py0000664000175000017500000000701700000000000021076 0ustar00zuulzuul00000000000000# 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 # 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', 'cliff.sphinxext'] # 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. copyright = '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 = 'native' # -- Options for HTML output -------------------------------------------------- # Output file base name for HTML help builder. htmlhelp_basename = 'python-manilaclientdoc' html_theme = 'openstackdocs' # openstackdocstheme options openstackdocs_repo_name = 'openstack/python-manilaclient' openstackdocs_pdf_link = True openstackdocs_bug_project = 'python-manilaclient' openstackdocs_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', 'Manila Python Client Documentation', '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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5572674 python-manilaclient-4.8.0/doc/source/contributor/0000775000175000017500000000000000000000000022144 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/doc/source/contributor/contributing.rst0000664000175000017500000000531100000000000025405 0ustar00zuulzuul00000000000000============================ So You Want to Contribute... ============================ For general information on contributing to OpenStack, check out the `contributor guide `_ to get started. It covers all the basics that are common to all OpenStack projects: the accounts you need, the basics of interacting with our Gerrit review system, how we communicate as a community, etc. This project contains a python SDK and command line clients to interact with the API exposed by `Manila `_, the OpenStack Shared File Systems service. Refer to the `Contributor guide for Manila `_ for information regarding the team's task trackers, communicating with other project developers and contacting the core team. Bugs ~~~~ You found an issue and want to make sure we are aware of it? You can do so on `Launchpad `_. If you're looking to contribute, search for the `low-hanging-fruit`_ tag to see issues that are easier to get started with. .. _project-structure: Project Structure ~~~~~~~~~~~~~~~~~ This project includes three distinct components: - manilaclient SDK: python bindings for Manila API `version V1`_ and `version V2`_ - manilaclient shell: A `command line utility`_ (``manila``) - OpenStack client shell: A `plugin to support the OpenStack Client`_ Command Line Interface. The version 2 of the API for Manila supports microversions. The manilaclient library is expected to handle these for complete backwards compatibility. All versions of the Manila API are currently supported, however, future releases of manilaclient may drop support for older versions of the API. If you're working on the OpenStack Client command line interface plugin that exists in this project, do read the `OpenStack Client Developer Documentation`_. This includes the Human Interface Guide and some design priciples including command structure and command specs that you will find helpful. .. _low-hanging-fruit: https://bugs.launchpad.net/python-manilaclient/+bugs?field.tag=low-hanging-fruit .. _version V1: https://opendev.org/openstack/python-manilaclient/src/branch/master/manilaclient/v1 .. _version V2: https://opendev.org/openstack/python-manilaclient/src/branch/master/manilaclient/v2 .. _command line utility: https://opendev.org/openstack/python-manilaclient/src/branch/master/manilaclient/shell.py .. _plugin to support the OpenStack Client: https://opendev.org/openstack/python-manilaclient/src/branch/master/manilaclient/osc .. _OpenStack Client Developer Documentation: https://docs.openstack.org/python-openstackclient/latest/contributor/index.html././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/doc/source/contributor/functional-tests.rst0000664000175000017500000002114600000000000026204 0ustar00zuulzuul00000000000000Functional tests ================ There is a suite of functional tests under the python-manilaclient/tests/functional directory. Unlike unit tests, these tests perform API calls to Manila and are designed to run on a DevStack. 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 wish to use DevStack to run manila, a good place to start would be the `manila contributor guide`_. .. note:: We absolutely recommend using a ``fake shared file system back end`` as opposed to a real storage system, because our tests are written with the assumption that all APIs manila exposes are usable. In reality, different real world storage back ends have `different capabilities`_ and this project doesn't need to worry about them to provide a general purpose interface to Manila. A fake driver provides fake storage, so don't expect to be able to mount or use the shared file systems that you create with it. You can use the following local.conf file to configure DevStack including Manila using a few fake back ends: .. code-block:: console [[local|localrc]] # auth ADMIN_PASSWORD=nomoresecret DATABASE_PASSWORD=$ADMIN_PASSWORD RABBIT_PASSWORD=$ADMIN_PASSWORD SERVICE_PASSWORD=$ADMIN_PASSWORD # enable logging for DevStack LOGFILE=/opt/stack/logs/stack.sh.log # Logging mode for DevStack services VERBOSE=True # manila enable_plugin manila https://opendev.org/openstack/manila # python-manilaclient LIBS_FROM_GIT=python-manilaclient # 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 Configuration ------------- The functional tests require a couple of configuration files, so you will need to generate them before running the tests. For DevStack ^^^^^^^^^^^^ On your DevStack machine, you can run the following script. It assumes that ``devstack`` is cloned onto your base folder. .. code-block:: console DEST=${DEST:-/opt/stack} MANILACLIENT_DIR=${MANILACLIENT_DIR:-$DEST/python-manilaclient} 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 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. .. _manila contributor guide: https://docs.openstack.org/manila/latest/contributor/development-environment-devstack.html .. _different capabilities: https://docs.openstack.org/manila/latest/admin/share_back_ends_feature_support_mapping.html././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/doc/source/contributor/index.rst0000664000175000017500000000074200000000000024010 0ustar00zuulzuul00000000000000Contributing ============ Basic Information ----------------- .. toctree:: :maxdepth: 3 contributing 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.rst././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/doc/source/index.rst0000664000175000017500000000276300000000000021443 0ustar00zuulzuul00000000000000Python bindings to the OpenStack Manila API =========================================== This is a client for OpenStack Manila API. There's :doc:`a Python API ` (the :mod:`manilaclient` module), and a :doc:`command-line script ` (installed as :program:`manila`). Each implements the entire OpenStack Manila API. See :ref:`project-structure` for more information. You'll need credentials for an OpenStack cloud that implements the Manila API in order to use the manila client. Command-Line Reference ~~~~~~~~~~~~~~~~~~~~~~ There are two shell implementations in python-manilaclient. .. important:: The legacy "manila" shell client is deprecated as of version ``5.0.0``. A future version of python-manilaclient may not ship this legacy shell client. If you rely on it, it is highly recommended that you begin using the openstack CLI client right away. Refer to the `mapping guide `_ to help with this transition. The "openstack" CLI client intends to be fully compatible with the manila API: .. toctree:: :maxdepth: 1 cli/osc_plugin_cli cli/decoder The legacy "manila" client is deprecated and may not support newer API features: .. toctree:: :maxdepth: 2 user/shell Using the python module ~~~~~~~~~~~~~~~~~~~~~~~ .. toctree:: :maxdepth: 2 user/api Contributing ~~~~~~~~~~~~ .. toctree:: :maxdepth: 2 contributor/index .. only:: html Indices and tables ~~~~~~~~~~~~~~~~~~ * :ref:`genindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5572674 python-manilaclient-4.8.0/doc/source/user/0000775000175000017500000000000000000000000020550 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/doc/source/user/api.rst0000664000175000017500000000410600000000000022054 0ustar00zuulzuul00000000000000The :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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/doc/source/user/shell.rst0000664000175000017500000000332200000000000022411 0ustar00zuulzuul00000000000000The :program:`manila` shell utility ========================================= .. important:: This shell client is deprecated as of version ``5.0.0``. A future version of python-manilaclient may not ship this legacy shell client. If you rely on it, it is highly recommended that you begin using the openstack CLI client right away. Refer to the `mapping guide <../cli/decoder.html>`_ to help with this transition. .. program:: manila .. highlight:: bash The :program:`manila` shell utility interacts with the OpenStack Manila API from the command line. It supports the entirety of the OpenStack Manila API. You'll need to provide :program:`manila` with your OpenStack username and API key. You can do this with the `--os-username`, `--os-password` and `--os-tenant-name` options, but it's easier to just set them as environment variables by setting two environment variables: .. envvar:: OS_USERNAME or MANILA_USERNAME Your OpenStack Manila username. .. envvar:: OS_PASSWORD or MANILA_PASSWORD Your password. .. envvar:: OS_TENANT_NAME or MANILA_PROJECT_ID Project for work. .. envvar:: OS_AUTH_URL or MANILA_URL The OpenStack API server URL. .. envvar:: OS_SHARE_API_VERSION The OpenStack Shared Filesystems API version. For example, in Bash you'd use:: export OS_USERNAME=foo export OS_PASSWORD=bar export OS_TENANT_NAME=foobarproject export OS_AUTH_URL=http://... export OS_SHARE_API_VERSION=2 From there, all shell commands take the form:: manila [arguments...] Run :program:`manila help` to get a full list of all possible commands, and run :program:`manila help ` to get detailed help for that command. .. program-output:: manila --help ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5452642 python-manilaclient-4.8.0/etc/0000775000175000017500000000000000000000000016300 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5572674 python-manilaclient-4.8.0/etc/manilaclient/0000775000175000017500000000000000000000000020740 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/etc/manilaclient/README.manilaclient.conf0000664000175000017500000000021400000000000025200 0ustar00zuulzuul00000000000000To generate the sample manilaclient.conf file, run the following command from the top level of the manilaclient directory: tox -egenconfig ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5572674 python-manilaclient-4.8.0/etc/oslo-config-generator/0000775000175000017500000000000000000000000022503 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/etc/oslo-config-generator/manilaclient.conf0000664000175000017500000000014200000000000026007 0ustar00zuulzuul00000000000000[DEFAULT] output_file = etc/manilaclient/manilaclient.conf.sample namespace = manilaclient.config ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5572674 python-manilaclient-4.8.0/manilaclient/0000775000175000017500000000000000000000000020165 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/__init__.py0000664000175000017500000000234000000000000022275 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/api_versions.py0000664000175000017500000003552200000000000023247 0ustar00zuulzuul00000000000000# 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.83' 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/base.py0000664000175000017500000004364000000000000021460 0ustar00zuulzuul00000000000000# 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 abc import contextlib import copy import hashlib import os from manilaclient import api_versions from manilaclient.common import cliutils from manilaclient 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 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, manager=None, body=None, return_raw=None): """List the collection. :param url: a partial URL, e.g., '/shares' :param response_key: the key to be looked up in response dictionary, e.g., 'shares'. If response_key is None - all response body will be used. :param manager: manager instance for constructing the returned objects (self will be used by default) :param body: data that will be encoded as JSON and passed in POST request (GET will be sent by default) """ resp = None if body: resp, body = self.api.client.post(url, body=body) else: resp, body = self.api.client.get(url) if manager is None: manager = self obj_class = manager.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"): if return_raw: return data resource = [obj_class(manager, 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="~/.cache/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.sha1(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: try: cache.write("%s\n" % val) except UnicodeEncodeError: pass def _get(self, url, response_key, return_raw=False): resp, body = self.api.client.get(url) if response_key: if return_raw: return body[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 _accept(self, url, body): resp, body = self.api.client.post(url, body=body) 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 {} 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} resources = self.list(search_opts=search_opts) if ('v2.shares.ShareManager' in str(self.__class__) and self.api_version >= api_versions.APIVersion("2.69")): search_opts_2 = {'all_tenants': 1, 'is_soft_deleted': True} shares_soft_deleted = self.list(search_opts=search_opts_2) resources += shares_soft_deleted for obj in resources: 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 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) class MetadataCapableResource(Resource, metaclass=abc.ABCMeta): superresource = None def _get_subresource_and_resource(self, superresource): resource = self subresource = None superresource = superresource or self.superresource if superresource is not None: resource = superresource subresource = self return resource, subresource def get_metadata(self, superresource=None): """Get metadata of a resource :param superresource: either a parent resource object or text with its ID. Required for sub-resources such as share export locations which do not include a reference to the parent object by default """ resource, subresource = self._get_subresource_and_resource( superresource) return self.manager.get_metadata(resource, subresource=subresource) def set_metadata(self, metadata, superresource=None): """Set or update metadata for the resource. :param metadata: A dictionary of key:value pairs to be set as resource metadata :param superresource: either a parent resource object or text with its ID. Required for sub-resources such as share share export locations which do not include a reference to the parent object by default """ resource, subresource = self._get_subresource_and_resource( superresource) return self.manager.set_metadata(resource, metadata, subresource=subresource) def delete_metadata(self, keys, superresource=None): """Delete specified keys from the given resource. :param keys: An iterable with keys of metadata items to be deleted :param superresource: either a parent resource object or text with its ID. Required for sub-resources such as share share export locations which do not include a reference to the parent object by default """ resource, subresource = self._get_subresource_and_resource( superresource) return self.manager.delete_metadata(resource, keys, subresource=subresource) def update_all_metadata(self, metadata, superresource=None): """Update all metadata for this resource. :param metadata: A dictionary of key:value pairs of resource metadata to be updated :param superresource: either a parent resource object or text with its ID. Required for sub-resources such as share share export locations which do not include a reference to the parent object by default """ resource, subresource = self._get_subresource_and_resource( superresource) return self.manager.update_all_metadata(resource, metadata, subresource=subresource) class MetadataCapableManager(ManagerWithFind, metaclass=abc.ABCMeta): """Provides extended behavior to objects to handle key=value metadata.""" resource_path = None subresource_path = None def get_metadata(self, resource, subresource=None): """Get metadata of a resource. :param resource: either resource object or text with its ID. :param subresource: either a child resource object or text with its ID """ resource = getid(resource) if subresource: subresource = getid(subresource) resource = f"{resource}{self.subresource_path}/{subresource}" return self._get(f"{self.resource_path}/{resource}/metadata", "metadata") def set_metadata(self, resource, metadata, subresource=None): """Set or update metadata for resource. :param resource: either resource object or text with its ID. :param metadata: A dictionary of key:value pairs to be set as resource metadata :param subresource: either a child resource object or text with its ID """ body = {'metadata': metadata} resource = getid(resource) if subresource: subresource = getid(subresource) resource = f"{resource}{self.subresource_path}/{subresource}" return self._create(f"{self.resource_path}/{resource}/metadata", body, "metadata") def delete_metadata(self, resource, keys, subresource=None): """Delete specified keys from resource metadata. :param resource: either resource object or text with its ID. :param keys: An iterable with keys of metadata items to be deleted :param subresource: either a child resource object or text with its ID """ resource = getid(resource) if subresource: subresource = getid(subresource) resource = f"{resource}{self.subresource_path}/{subresource}" for key in keys: self._delete(f"{self.resource_path}/{resource}/metadata/{key}") def update_all_metadata(self, resource, metadata, subresource=None): """Update all metadata of a resource. :param resource: either resource object or text with its ID. :param metadata: A dictionary of key:value pairs of resource metadata to be updated :param subresource: either a child resource object or text with its ID """ body = {'metadata': metadata} resource = getid(resource) if subresource: subresource = getid(subresource) resource = f"{resource}{self.subresource_path}/{subresource}" return self._update(f"{self.resource_path}/{resource}/metadata", body) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/client.py0000664000175000017500000000452300000000000022021 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5572674 python-manilaclient-4.8.0/manilaclient/common/0000775000175000017500000000000000000000000021455 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/common/__init__.py0000664000175000017500000000000000000000000023554 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/common/_i18n.py0000664000175000017500000000215300000000000022746 0ustar00zuulzuul00000000000000# 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5572674 python-manilaclient-4.8.0/manilaclient/common/apiclient/0000775000175000017500000000000000000000000023425 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/common/apiclient/__init__.py0000664000175000017500000000000000000000000025524 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/common/apiclient/exceptions.py0000664000175000017500000003112700000000000026164 0ustar00zuulzuul00000000000000# 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 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 str(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/common/apiclient/utils.py0000664000175000017500000000533500000000000025145 0ustar00zuulzuul00000000000000# # 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 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: 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/common/cliutils.py0000664000175000017500000001775400000000000023675 0ustar00zuulzuul00000000000000# 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 import getpass import inspect import os import sys import textwrap from oslo_utils import encodeutils from oslo_utils import strutils import prettytable 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.getfullargspec(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) print(encodeutils.safe_encode(pt.get_string(**kwargs)).decode()) 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 = str(v) if wrap > 0: v = textwrap.fill(str(v), wrap) # if value has a newline, add in multiple rows # e.g. fault with stacktrace if v and isinstance(v, str) 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]) print(encodeutils.safe_encode(pt.get_string()).decode()) 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 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(choices): return ', '.join("'%s'" % choice for choice in choices) def exit(msg=''): if msg: print(msg, file=sys.stderr) sys.exit(1) def convert_dict_list_to_string(data, ignored_keys=None): ignored_keys = ignored_keys or [] if not isinstance(data, list): data = [data] data_string = '' for datum in data: if hasattr(datum, '_info'): datum_dict = datum._info else: datum_dict = datum for k, v in datum_dict.items(): if k not in ignored_keys: data_string += '\n%(k)s = %(v)s' % { 'k': k, 'v': v} return data_string ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/common/constants.py0000664000175000017500000001000200000000000024034 0ustar00zuulzuul00000000000000# 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_id', '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', ) SHARE_TRANSFER_SORT_KEY_VALUES = ( 'id', 'resource_type', 'resource_id', 'name', 'source_project_id', 'destination_project_id', 'created_at', 'expires_at', ) RESOURCE_LOCK_SORT_KEY_VALUES = ( 'id', 'created_at', 'updated_at', 'resource_id', 'resource_type', 'resource_action' 'lock_reason', ) BACKUP_SORT_KEY_VALUES = ( 'id', 'status', 'size', 'share_id', 'progress', 'restore_progress', 'name', 'host', 'topic', 'project_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' STATUS_SERVER_MIGRATING = 'server_migrating' 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' CONSISTENT_SNAPSHOT_SUPPORT = 'consistent_snapshot_support' BOOL_SPECS = ( SNAPSHOT_SUPPORT, CREATE_SHARE_FROM_SNAPSHOT_SUPPORT, REVERT_TO_SNAPSHOT_SUPPORT, MOUNT_SNAPSHOT_SUPPORT, ) # share group types GROUP_BOOL_SPECS = ( CONSISTENT_SNAPSHOT_SUPPORT, ) REPLICA_GRADUATION_VERSION = '2.56' REPLICA_PRE_GRADUATION_VERSION = '2.55' SHARE_TRANSFER_VERSION = '2.77' RESOURCE_LOCK_VERSION = '2.81' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/common/httpclient.py0000664000175000017500000001615500000000000024215 0ustar00zuulzuul00000000000000# 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 urllib import parse from oslo_serialization import jsonutils from oslo_utils import importutils from oslo_utils import strutils import re import requests from manilaclient import exceptions try: from eventlet import sleep except ImportError: from time import sleep # noqa try: osprofiler_web = importutils.try_import("osprofiler.web") except Exception: pass 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, cert=None): 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, cert) 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, cert=None): options = {'verify': True} if insecure: options['verify'] = False elif cacert: options['verify'] = cacert if cert: options['cert'] = cert 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 osprofiler_web: headers.update(osprofiler_web.get_trace_id_headers()) 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", str(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 }) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/config.py0000664000175000017500000002443400000000000022013 0ustar00zuulzuul00000000000000# 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."), cfg.BoolOpt("run_share_servers_migration_tests", default=False, help="Defines whether to run share server migration 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/exceptions.py0000664000175000017500000000344100000000000022722 0ustar00zuulzuul00000000000000# 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._i18n import _ from manilaclient.common.apiclient.exceptions import * # noqa class ManilaclientException(Exception): """A generic client error.""" message = _("An unexpected error occured.") def __init__(self, message): self.message = message or self.message def __str__(self): return self.message class ResourceInErrorState(ManilaclientException): """A resource is in an unexpected 'error' state.""" message = _("Resource is in error state") class TimeoutException(ManilaclientException): """A request has timed out""" message = _("Request has timed out") 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} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/extension.py0000664000175000017500000000257500000000000022564 0ustar00zuulzuul00000000000000# 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5612686 python-manilaclient-4.8.0/manilaclient/osc/0000775000175000017500000000000000000000000020751 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/__init__.py0000664000175000017500000000000000000000000023050 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/plugin.py0000664000175000017500000001041700000000000022624 0ustar00zuulzuul00000000000000# 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 import client 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] service_type, manila_endpoint_url = _get_manila_url_from_service_catalog( instance) instance.setup_auth() debugging_enabled = instance._cli_options.debug client_args = dict(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, cacert=instance.cacert, cert=instance.cert, insecure=not instance.verify) # Cast the API version into an object for further processing requested_api_version = api_versions.APIVersion( version_str=requested_api_version) max_version = api_versions.APIVersion(api_versions.MAX_VERSION) client_args.update(dict(api_version=max_version)) temp_client = client.Client(max_version, **client_args) discovered_version = api_versions.discover_version(temp_client, requested_api_version) shared_file_system_client = utils.get_client_class( API_NAME, discovered_version.get_string(), API_VERSIONS) LOG.debug('Instantiating Shared File System (share) client: %s', shared_file_system_client) LOG.debug('Shared File System API version: %s', discovered_version) client_args.update(dict(api_version=discovered_version)) return shared_file_system_client(**client_args) 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/utils.py0000664000175000017500000000766500000000000022501 0ustar00zuulzuul00000000000000# 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 oslo_utils import strutils from manilaclient.common._i18n import _ from manilaclient.common import constants from manilaclient import exceptions LOG = logging.getLogger(__name__) 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, bool_specs=constants.BOOL_SPECS): try: 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 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, str(value))) raise exceptions.CommandError(msg) else: extra_specs[key] = value except ValueError: msg = LOG.error(_( "Wrong format: specs should be key=value pairs.")) raise exceptions.CommandError(msg) return extra_specs def extract_group_specs(extra_specs, specs_to_add): return extract_extra_specs(extra_specs, specs_to_add, constants.GROUP_BOOL_SPECS) def format_column_headers(columns): column_headers = [] for column in columns: column_headers.append( column.replace('_', ' ').title().replace('Id', 'ID')) return column_headers def format_share_group_type(share_group_type, formatter='table'): printable_share_group_type = share_group_type._info is_public = printable_share_group_type.pop('is_public') printable_share_group_type['visibility'] = ( 'public' if is_public else 'private') if formatter == 'table': printable_share_group_type['group_specs'] = ( format_properties(share_group_type.group_specs)) printable_share_group_type['share_types'] = ( "\n".join(printable_share_group_type['share_types']) ) return printable_share_group_type ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5652697 python-manilaclient-4.8.0/manilaclient/osc/v2/0000775000175000017500000000000000000000000021300 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/__init__.py0000664000175000017500000000000000000000000023377 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/availability_zones.py0000664000175000017500000000243600000000000025547 0ustar00zuulzuul00000000000000# 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.command import command from osc_lib import utils as oscutils from manilaclient.common._i18n import _ class ShareAvailabilityZoneList(command.Lister): """List all availability zones.""" _description = _("List all availability zones") def get_parser(self, prog_name): parser = super(ShareAvailabilityZoneList, self).get_parser( prog_name) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share availability_zones = share_client.availability_zones.list() fields = ("Id", "Name", "Created At", "Updated At") return (fields, (oscutils.get_item_properties (s, fields) for s in availability_zones)) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5652697 python-manilaclient-4.8.0/manilaclient/osc/v2/data/0000775000175000017500000000000000000000000022211 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/data/manila.csv0000664000175000017500000003205100000000000024170 0ustar00zuulzuul00000000000000manila command,openstack command,command description --version,module list,List the client software version absolute-limits,share limits show --absolute,Print a list of absolute limits for a user access-allow,share access create,Allow access to a given share access-deny,share access delete,Deny access to a share access-list,share access list,Show access list for share access-metadata,share access set --property or share access unset --property,Set or delete metadata on a share access rule access-show,share access show,Show details about a NAS share access rule api-version,versions show --service sharev2,Display the API version information availability-zone-list,share availability zone list,List all availability zones create,share create,Creates a new share credentials,token issue,Show user credentials returned from auth delete,share delete,Remove one or more shares endpoints,endpoint list --service sharev2,Discover endpoints that get returned from the authenticate services extend,share resize,Increases the size of an existing share extra-specs-list,share type list,Print a list of current 'share types and extra specs' (Admin Only) force-delete,share delete --force,Attempt force-delete of share regardless of state list,share list,List NAS shares with filters manage,share adopt,Manage share not handled by Manila message-delete,share message delete,Remove one or more messages message-list,share message list,Lists all messages message-show,share message show,Show details about a message metadata,share set --property or share unset --property,Set or delete metadata on a share metadata-show,share show -c properties,Show metadata of given share metadata-update-all,,Update all metadata of a share migration-cancel,share migration cancel,Cancel migration of a given share when copying migration-complete,share migration complete,Complete migration for a given share migration-get-progress,share migration show,Get migration progress of a given share when copying migration-start,share migration start,Migrate share to a new host pool-list,share pool list,List all backend storage pools known to the scheduler quota-class-show,share quota show --class,List the quotas for a quota class quota-class-update,share quota update --class,Update the quotas for a quota class quota-defaults,share quota show --class default,List the default quotas for a project quota-delete,share quota delete,Delete quota for a project or project/user or project/share-type quota-show,share quota show,List the quotas for a project or user or share type quota-update,share quota set,Update the quotas for a project/user and/or share type rate-limits,share limits show --rate,Print a list of rate limits for a user reset-state,share set --status,Explicitly update the state of a share reset-task-state,share set --task-state,Explicitly update the task state of a share restore,share restore,Restore one or more shares from recycle bin revert-to-snapshot,share revert,Revert a share to the specified snapshot security-service-create,share security service create,Create security service used by project security-service-delete,share security service delete,Delete one or more security services security-service-list,share security service list,Get a list of security services security-service-show,share security service show,Show security service security-service-update,share security service set,Update security service service-disable,share service set --disable,Disables 'manila-share' or 'manila-scheduler' services service-enable,share service set --enable,Enables 'manila-share' or 'manila-scheduler' services service-list,share service list,List all services share-export-location-list,share export location list,List export locations of a given share share-export-location-show,share export location show,Show export location of the share share-group-create,share group create,Creates a new share group share-group-delete,share group delete,Delete one or more share groups share-group-list,share group list,List share groups with filters share-group-reset-state,share group set --status,Explicitly update the state of a share group share-group-show,share group show,Show details about a share group share-group-update,share group set or share group unset,Update a share group share-group-snapshot-create,share group snapshot create,Creates a new share group snapshot share-group-snapshot-delete,share group snapshot delete,Remove one or more share group snapshots share-group-snapshot-list,share group snapshot list,List share group snapshots with filters share-group-snapshot-list-members,share group snapshot members list,List members of a share group snapshot share-group-snapshot-reset-state,share group snapshot set --status,Explicitly update the state of a share group snapshot share-group-snapshot-show,share group snapshot show,Show details about a share group snapshot share-group-snapshot-update,share group snapshot set or share group snapshot unset,Update a share group snapshot share-group-type-access-add,share group type access create,Adds share group type access for the given project share-group-type-access-list,share group type access list,Print access information about a share group type share-group-type-access-remove,share group type access delete,Removes share group type access for the given project share-group-type-create,share group type create,Create a new share group type share-group-type-delete,share group type delete,Delete a specific share group type share-group-type-key,share group type set --group-specs,Set or unset group_spec for a share group type share-group-type-list,share group type list,Print a list of available 'share group types' share-group-type-specs-list,share group type list,Print a list of 'share group types specs' (Admin Only) share-instance-export-location-list,share instance export location list,List export locations of a given share instance share-instance-export-location-show,share instance export location show,Show export location for the share instance share-instance-force-delete,share instance delete,Force-delete the share instance regardless of state share-instance-list,share instance list,List share instances share-instance-reset-state,share instance set --status,Explicitly update the state of a share instance share-instance-show,share instance show,Show details about a share instance share-network-create,share network create,Create a share network to export shares to share-network-delete,share network delete,Delete one or more share networks share-network-list,share network list,Get a list of share networks share-network-reset-state,share network set --status,Explicitly update the state of a share network share-network-security-service-add,share network set --security-service,Associate security service with share network share-network-security-service-add-check,share network set --check-only --new-security-service,Associate security service with share network share-network-security-service-list,share network show,Get list of security services associated with a given share network share-network-security-service-remove,share network unset --security-service,Dissociate security service from share network share-network-security-service-update,share network set --current-security-service --new-security-service,Update a current security service to a new security service share-network-security-service-update-check,share network set --check-only --current-security-service --new-security-service,Check if a security service update on the share network is supported share-network-show,share network show,Retrieve details for a share network share-network-subnet-create,share network subnet create,Add a new subnet into a share network share-network-subnet-create-check,share network subnet create --check-only,Check if a new subnet can be added to a share network share-network-subnet-delete,share network subnet delete,Delete one or more share network subnets share-network-subnet-show,share network subnet show,Show share network subnet share-network-update,share network set or share network unset,Update share network data share-replica-create,share replica create,Create a share replica share-replica-delete,share replica delete,Remove one or more share replicas share-replica-export-location-list,share replica export location list,List export locations of a share replica share-replica-export-location-show,share replica export location show,Show details of a share replica's export location share-replica-list,share replica list,List share replicas share-replica-promote,share replica promote,Promote specified replica to 'active' replica_state share-replica-reset-replica-state,share replica set --replica-state,Explicitly update the 'replica_state' of a share replica share-replica-reset-state,share replica set --status,Explicitly update the 'status' of a share replica share-replica-resync,share replica resync,Attempt to update the share replica with its 'active' mirror share-replica-show,share replica show,Show details about a replica share-server-delete,share server delete,Delete one or more share servers share-server-details,share server show,Show share server details share-server-list,share server list,List all share servers share-server-manage,share server adopt,Manage share server not handled by Manila share-server-migration-cancel,share server migration cancel,Cancels migration of a given share server when copying share-server-migration-check,share server migration start --check-only,Check migration compatibility for a share server with desired properties share-server-migration-complete,share server migration complete,Complete migration for a given share server share-server-migration-get-progress,share server migration show,Get migration progress of a given share server when copying share-server-migration-start,share server migration start,Migrate share server to a new host share-server-reset-state,share server set --status,Explicitly update the state of a share server share-server-reset-task-state,share server set --task-state,Explicitly update the task state of a share share-server-show,share server show,Show share server info share-server-unmanage,share server abandon,Unmanage share server share-transfer-accept,share transfer accept,Accepts a share transfer. share-transfer-create,share transfer create,Creates a share transfer. share-transfer-delete,share transfer delete,Remove one or more transfers share-transfer-list,share transfer list,Lists all transfers share-transfer-show,share transfer show,Delete a transfer show,share show,Show details about a NAS share shrink,share resize,Decreases the size of an existing share snapshot-access-allow,share snapshot access create,Allow read only access to a snapshot snapshot-access-deny,share snapshot access delete,Deny access to a snapshot snapshot-access-list,share snapshot access list,Show access list for a snapshot snapshot-create,share snapshot create,Add a new snapshot snapshot-delete,share snapshot delete,Remove one or more snapshots snapshot-export-location-list,share snapshot export location list,List export locations of a given snapshot snapshot-export-location-show,share snapshot export location show,Show export location of the share snapshot snapshot-force-delete,share snapshot delete --force,Attempt force-deletion of one or more snapshots Regardless of the state snapshot-instance-export-location-list,share snapshot instance export location list,List export locations of a given snapshot instance snapshot-instance-export-location-show,share snapshot instance export location show,Show export location of the share instance snapshot snapshot-instance-list,share snapshot instance list,List share snapshot instances snapshot-instance-reset-state,share snapshot instance set --status,Explicitly update the state of a share snapshot instance snapshot-instance-show,share snapshot instance show,Show details about a share snapshot instance snapshot-list,share snapshot list,List all the snapshots snapshot-manage,share snapshot adopt,Manage share snapshot not handled by Manila snapshot-rename,share snapshot set --name,Rename a snapshot snapshot-reset-state,share snapshot set --status,Explicitly update the state of a snapshot snapshot-show,share snapshot show,Show details about a snapshot snapshot-unmanage,share snapshot abandon,Unmanage one or more share snapshots soft-delete,share delete --soft,Soft delete one or more shares type-access-add,share type access create,Add share type access for the given project type-access-list,share type access list,Print access information about the given share type type-access-remove,share type access delete,Remove share type access for the given project type-create,share type create,Create a new share type type-delete,share type delete,Delete one or more specific share types type-key,share type set --extra-specs,Set or unset extra_spec for a share type type-list,share type list,Print a list of available 'share types' type-show,share type show,Show share type details type-update,share type set or share type unset,Update share type name and/or description and/or visibility unmanage,share abandon,Unmanage share update,share set or share unset,Rename a share bash-completion,complete,Print arguments for bash_completion list-extensions,,List all the os-api extensions that are available ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/messages.py0000664000175000017500000001474700000000000023476 0ustar00zuulzuul00000000000000# 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 import api_versions from manilaclient.common._i18n import _ from manilaclient.common.apiclient import utils as apiutils LOG = logging.getLogger(__name__) MESSAGE_ATTRIBUTES = [ 'id', 'resource_type', 'resource_id', 'action_id', 'user_message', 'message_level', 'detail_id', 'created_at', 'expires_at', 'request_id' ] class DeleteMessage(command.Command): """Remove one or more messages.""" _description = _("Remove one or more messages") def get_parser(self, prog_name): parser = super(DeleteMessage, self).get_parser(prog_name) parser.add_argument( 'message', metavar='', nargs='+', help=_('ID of the message(s).')) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share failure_count = 0 for message in parsed_args.message: try: message_ref = apiutils.find_resource( share_client.messages, message) share_client.messages.delete(message_ref) except Exception as e: failure_count += 1 LOG.error(_( "Delete for message %(message)s failed: %(e)s"), {'message': message, 'e': e}) if failure_count > 0: raise exceptions.CommandError(_( "Unable to delete some or all of the specified messages.")) class ListMessage(command.Lister): """Lists all messages.""" _description = _("Lists all messages") def get_parser(self, prog_name): parser = super(ListMessage, self).get_parser(prog_name) parser.add_argument( '--resource-id', metavar='', default=None, help=_('Filters results by a resource uuid. Default=None.')) parser.add_argument( '--resource-type', metavar='', default=None, help=_('Filters results by a resource type. Default=None. ' 'Example: "openstack message list --resource-type share"')) parser.add_argument( '--action-id', metavar='', default=None, help=_('Filters results by action id. Default=None.')) parser.add_argument( '--detail-id', metavar='', default=None, help=_('Filters results by detail id. Default=None.')) parser.add_argument( '--request-id', metavar='', default=None, help=_('Filters results by request id. Default=None.')) parser.add_argument( '--message-level', metavar='', default=None, help=_('Filters results by the message level. Default=None. ' 'Example: "openstack message list --message-level ERROR".')) parser.add_argument( '--limit', metavar='', type=int, default=None, help=_('Maximum number of messages to return. (Default=None)')) parser.add_argument( '--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.')) parser.add_argument( '--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.')) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share search_opts = { 'limit': parsed_args.limit, 'request_id': parsed_args.request_id, 'resource_type': parsed_args.resource_type, 'resource_id': parsed_args.resource_id, 'action_id': parsed_args.action_id, 'detail_id': parsed_args.detail_id, 'message_level': parsed_args.message_level } if share_client.api_version < api_versions.APIVersion("2.52"): if getattr(parsed_args, 'since') or getattr(parsed_args, 'before'): raise exceptions.CommandError(_( "Filtering messages by 'since' and 'before'" " is possible only with Manila API version >=2.52")) else: search_opts['created_since'] = parsed_args.since search_opts['created_before'] = parsed_args.before messages = share_client.messages.list(search_opts=search_opts) columns = [ 'ID', 'Resource Type', 'Resource ID', 'Action ID', 'User Message', 'Detail ID', 'Created At'] return (columns, (oscutils.get_item_properties (m, columns) for m in messages)) class ShowMessage(command.ShowOne): """Show details about a message.""" _description = _("Show details about a message") def get_parser(self, prog_name): parser = super(ShowMessage, self).get_parser(prog_name) parser.add_argument( 'message', metavar='', help=_('ID of the message.')) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share message = apiutils.find_resource( share_client.messages, parsed_args.message) return (MESSAGE_ATTRIBUTES, oscutils.get_dict_properties( message._info, MESSAGE_ATTRIBUTES)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/quotas.py0000664000175000017500000003456300000000000023201 0ustar00zuulzuul00000000000000# 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.command import command from osc_lib import exceptions from osc_lib import utils from manilaclient import api_versions from manilaclient.common._i18n import _ class QuotaSet(command.Command): """Set quotas for a project or project/user or project/share-type. It can be used to set the default class for all projects. """ _description = _("Set Quota for a project, or project/user or " "project/share-type or a class.") def get_parser(self, prog_name): parser = super(QuotaSet, self).get_parser(prog_name) quota_type = parser.add_mutually_exclusive_group() parser.add_argument( 'project', metavar='', help=_("A project (name/ID) or a class (e.g.: default).") ) quota_type.add_argument( '--class', dest='quota_class', action='store_true', default=False, help=_("Update class quota to all projects. " "Mutually exclusive with '--user' and '--share-type'.") ) quota_type.add_argument( '--user', metavar='', default=None, help=_("Name or ID of a user to set the quotas for. " "Mutually exclusive with '--share-type' and '--class'.") ) quota_type.add_argument( '--share-type', metavar='', type=str, default=None, help=_("Name or ID of a share type to set the quotas for. " "Mutually exclusive with '--user' and '--class'. " "Available only for microversion >= 2.39") ) parser.add_argument( '--shares', metavar='', type=int, default=None, help=_('New value for the "shares" quota.') ) parser.add_argument( '--snapshots', metavar='', type=int, default=None, help=_('New value for the "snapshots" quota.') ) parser.add_argument( '--gigabytes', metavar='', type=int, default=None, help=_('New value for the "gigabytes" quota.') ) parser.add_argument( '--snapshot-gigabytes', metavar='', type=int, default=None, help=_('New value for the "snapshot-gigabytes" quota.') ) parser.add_argument( '--share-networks', metavar='', type=int, default=None, help=_('New value for the "share-networks" quota.') ) parser.add_argument( '--share-groups', metavar='', type=int, default=None, help=_('New value for the "share-groups" quota. ' 'Available only for microversion >= 2.40') ) parser.add_argument( '--share-group-snapshots', metavar='', type=int, default=None, help=_('New value for the "share-group-snapshots" quota. ' 'Available only for microversion >= 2.40') ) parser.add_argument( '--share-replicas', metavar='', type=int, default=None, help=_("Number of share replicas. " "Available only for microversion >= 2.53") ) parser.add_argument( '--replica-gigabytes', metavar='', type=int, default=None, help=_("Capacity of share replicas in total. " "Available only for microversion >= 2.53") ) parser.add_argument( '--per-share-gigabytes', metavar='', type=int, default=None, help=_("New value for the 'per-share-gigabytes' quota." "Available only for microversion >= 2.62") ) parser.add_argument( '--force', dest='force', action="store_true", default=None, help=_('Force update the quota. ' 'Not applicable for class update.') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share identity_client = self.app.client_manager.identity user_id = None if parsed_args.user: user_id = utils.find_resource( identity_client.users, parsed_args.user).id kwargs = { "shares": parsed_args.shares, "snapshots": parsed_args.snapshots, "gigabytes": parsed_args.gigabytes, "snapshot_gigabytes": parsed_args.snapshot_gigabytes, "share_networks": parsed_args.share_networks, } if parsed_args.share_type is not None: if share_client.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"] = parsed_args.share_type if parsed_args.share_groups is not None: if share_client.api_version < api_versions.APIVersion('2.40'): raise exceptions.CommandError(_( "'share group' quotas are available only starting with " "'2.40' API microversion.")) kwargs["share_groups"] = parsed_args.share_groups if parsed_args.share_group_snapshots is not None: if share_client.api_version < api_versions.APIVersion('2.40'): raise exceptions.CommandError(_( "'share group snapshots' quotas are available only " "starting with '2.40' API microversion.")) kwargs["share_group_snapshots"] = parsed_args.share_group_snapshots if parsed_args.share_replicas is not None: if share_client.api_version < api_versions.APIVersion('2.53'): raise exceptions.CommandError(_( "setting the number of 'share replicas' is available only " "starting with API microversion '2.53'.")) kwargs["share_replicas"] = parsed_args.share_replicas if parsed_args.replica_gigabytes is not None: if share_client.api_version < api_versions.APIVersion('2.53'): raise exceptions.CommandError(_( "setting the capacity of share replicas in total " "is available only starting with API microversion '2.53'.") ) kwargs["replica_gigabytes"] = parsed_args.replica_gigabytes if parsed_args.per_share_gigabytes is not None: if share_client.api_version < api_versions.APIVersion('2.62'): raise exceptions.CommandError(_( "'per share gigabytes' quotas are available only " "starting with '2.62' API microversion.") ) kwargs["per_share_gigabytes"] = parsed_args.per_share_gigabytes if all(value is None for value in kwargs.values()): raise exceptions.CommandError(_( "Nothing to set. " "New quota must be specified to at least one of the following " "resources: 'shares', 'snapshots', 'gigabytes', " "'snapshot-gigabytes', 'share-networks', 'share-type', " "'share-groups', 'share-group-snapshots', 'share-replicas', " "'replica-gigabytes', 'per-share-gigabytes'")) if parsed_args.quota_class: kwargs.update({ "class_name": parsed_args.project, }) try: share_client.quota_classes.update(**kwargs) except Exception as e: raise exceptions.CommandError(_( "Failed to set quotas for %s class: '%s'") % (parsed_args.project, e)) else: project_id = utils.find_resource( identity_client.projects, parsed_args.project).id kwargs.update({ "tenant_id": project_id, "force": parsed_args.force, "user_id": user_id }) try: share_client.quotas.update(**kwargs) except Exception as e: raise exceptions.CommandError(_( "Failed to set quotas for project '%s' : '%s'") % (parsed_args.project, e)) class QuotaShow(command.ShowOne): """List the quotas for a project or project/user or project/share-type.""" _description = _("Show Quota") def get_parser(self, prog_name): parser = super(QuotaShow, self).get_parser(prog_name) quota_type = parser.add_mutually_exclusive_group() parser.add_argument( 'project', metavar='', help=_('Name or ID of the project to list quotas for.') ) quota_type.add_argument( '--user', metavar='', default=None, help=_("Name or ID of user to list the quotas for. Optional. " "Mutually exclusive with '--share-type'.") ) quota_type.add_argument( '--share-type', metavar='', type=str, default=None, help=_("Name or ID of a share type to list the quotas for. " "Optional. " "Mutually exclusive with '--user'. " "Available only for microversion >= 2.39") ) parser.add_argument( '--detail', action='store_true', default=False, help=_('Optional flag to indicate whether to show quota in detail.' ' Default false, available only for microversion >= 2.25.') ) parser.add_argument( '--defaults', action='store_true', default=False, help=_('Show the default quotas for the project.') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share identity_client = self.app.client_manager.identity user_id = None if parsed_args.user: user_id = utils.find_resource( identity_client.users, parsed_args.user).id project_id = utils.find_resource( identity_client.projects, parsed_args.project).id quotas = {} if parsed_args.defaults: quotas = share_client.quotas.defaults(project_id) else: kwargs = { "tenant_id": project_id, "user_id": user_id, "detail": parsed_args.detail, } if parsed_args.share_type is not None: if share_client.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"] = parsed_args.share_type quotas = share_client.quotas.get(**kwargs) printable_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())]) printable_quotas[quota_k] = quota_v return self.dict2columns(printable_quotas) class QuotaDelete(command.Command): """Delete quota for project/user or project/share-type. The quota will revert back to default. """ _description = _("Delete Quota") def get_parser(self, prog_name): parser = super(QuotaDelete, self).get_parser(prog_name) quota_type = parser.add_mutually_exclusive_group() parser.add_argument( 'project', metavar='', help=_('Name or ID of the project to delete quotas for.') ) quota_type.add_argument( '--user', metavar='', default=None, help=_("Name or ID of user to delete the quotas for. Optional. " "Mutually exclusive with '--share-type'.") ) quota_type.add_argument( '--share-type', metavar='', type=str, default=None, help=_("Name or ID of a share type to delete the quotas for. " "Optional. " "Mutually exclusive with '--user'. " "Available only for microversion >= 2.39") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share identity_client = self.app.client_manager.identity user_id = None if parsed_args.user: user_id = utils.find_resource( identity_client.users, parsed_args.user).id project_id = utils.find_resource( identity_client.projects, parsed_args.project).id kwargs = { "tenant_id": project_id, "user_id": user_id } if parsed_args.share_type: if share_client.api_version < api_versions.APIVersion("2.39"): raise exceptions.CommandError(_( "'share type' quotas are available only starting with " "API microversion '2.39'.")) kwargs["share_type"] = parsed_args.share_type share_client.quotas.delete(**kwargs) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/resource_locks.py0000664000175000017500000003332100000000000024676 0ustar00zuulzuul00000000000000# 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.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils from oslo_utils import uuidutils from manilaclient.common._i18n import _ from manilaclient.common.apiclient import utils as apiutils from manilaclient.common import constants LOG = logging.getLogger(__name__) LOCK_DETAIL_ATTRIBUTES = [ 'ID', 'Resource Id', 'Resource Type', 'Resource Action', 'Lock Context', 'User Id', 'Project Id', 'Created At', 'Updated At', 'Lock Reason', ] LOCK_SUMMARY_ATTRIBUTES = [ 'ID', 'Resource Id', 'Resource Type', 'Resource Action', ] RESOURCE_TYPE_MANAGERS = { 'share': 'shares', 'access_rule': 'share_access_rules' } class CreateResourceLock(command.ShowOne): """Create a new resource lock.""" _description = _("Lock a resource action from occurring on a resource") def get_parser(self, prog_name): parser = super(CreateResourceLock, self).get_parser(prog_name) parser.add_argument( 'resource', metavar='', help='Name or ID of resource to lock.') parser.add_argument( 'resource_type', metavar='', help='Type of the resource (e.g.: share, access).') parser.add_argument( '--resource-action', '--resource_action', metavar='', default='delete', help='Action to lock on the resource (default="delete")') parser.add_argument( '--lock-reason', '--lock_reason', '--reason', metavar='', help='Reason for the resource lock.') return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share resource_type = parsed_args.resource_type if resource_type not in RESOURCE_TYPE_MANAGERS: raise exceptions.CommandError(_("Unsupported resource type")) res_manager = RESOURCE_TYPE_MANAGERS[resource_type] resource = osc_utils.find_resource(getattr(share_client, res_manager), parsed_args.resource) resource_lock = share_client.resource_locks.create( resource.id, resource_type, parsed_args.resource_action, parsed_args.lock_reason ) resource_lock._info.pop('links', None) return self.dict2columns(resource_lock._info) class DeleteResourceLock(command.Command): """Remove one or more resource locks.""" _description = _("Remove one or more resource locks") def get_parser(self, prog_name): parser = super(DeleteResourceLock, self).get_parser(prog_name) parser.add_argument( 'lock', metavar='', nargs='+', help='ID(s) of the lock(s).') return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share failure_count = 0 for lock in parsed_args.lock: try: lock = apiutils.find_resource( share_client.resource_locks, lock ) lock.delete() except Exception as e: failure_count += 1 LOG.error(_( "Failed to delete %(lock)s: %(e)s"), {'lock': lock, 'e': e}) if failure_count > 0: raise exceptions.CommandError(_( "Unable to delete some or all of the specified locks.")) class ListResourceLock(command.Lister): """Lists all resource locks.""" _description = _("Lists all resource locks") def get_parser(self, prog_name): parser = super(ListResourceLock, self).get_parser(prog_name) parser.add_argument( '--all-projects', action='store_true', help=_("Filter resource locks for all projects. (Admin only).") ) parser.add_argument( '--project', default=None, help=_("Filter resource locks for specific project by name or ID, " "combine with --all-projects (Admin only).") ) parser.add_argument( '--user', default=None, help=_("Filter resource locks for specific user by name or ID, " "combine with --all-projects to search across projects " "(Admin only).") ) parser.add_argument( '--id', metavar='', default=None, help='Filter resource locks by ID. Default=None.') parser.add_argument( '--resource', '--resource-id', '--resource_id', default=None, metavar='', dest='resource', help=_("Filter resource locks for a resource by ID, specify " "--resource-type to look up by name.") ) parser.add_argument( '--resource-type', '--resource_type', default=None, metavar='', help=_("Filter resource locks by type of resource.") ) parser.add_argument( '--resource-action', '--resource_action', default=None, metavar='', help=_("Filter resource locks by resource action.") ) parser.add_argument( '--lock-context', '--lock_context', '--context', default=None, choices=['user', 'admin', 'service'], metavar='', help=_("Filter resource locks by context.") ) parser.add_argument( '--since', default=None, metavar='', help=_("Filter resource locks created since given date. " "The date format must be conforming to ISO8601. ") ) parser.add_argument( '--before', default=None, metavar='', help=_("Filter resource locks created before given date. " "The date format must be conforming to ISO8601. ") ) parser.add_argument( '--limit', metavar='', type=int, default=None, help=_("Number of resource locks to list. (Default=None)")) parser.add_argument( '--offset', metavar="", default=None, help='Starting position of resource lock records ' 'in a paginated list.') parser.add_argument( '--sort-key', '--sort_key', metavar='', type=str, default=None, choices=constants.RESOURCE_LOCK_SORT_KEY_VALUES, help='Key to be sorted, available keys are %(keys)s. ' 'Default=None.' % {'keys': constants.RESOURCE_LOCK_SORT_KEY_VALUES}) parser.add_argument( '--sort-dir', '--sort_dir', metavar='', type=str, default=None, choices=constants.SORT_DIR_VALUES, help='Sort direction, available values are %(values)s. ' 'OPTIONAL: Default=None.' % { 'values': constants.SORT_DIR_VALUES}) parser.add_argument( '--detailed', dest='detailed', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help="Show detailed information about filtered resource locks.") return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share columns = ( LOCK_SUMMARY_ATTRIBUTES if not parsed_args.detailed else LOCK_DETAIL_ATTRIBUTES ) project_id = None user_id = None if parsed_args.project: project_id = identity_common.find_project( identity_client, parsed_args.project, parsed_args.project_domain).id if parsed_args.user: user_id = identity_common.find_user(identity_client, parsed_args.user, parsed_args.user_domain).id # set all_projects when using project option all_projects = bool(parsed_args.project) or parsed_args.all_projects resource_id = parsed_args.resource resource_type = parsed_args.resource_type if resource_type is not None: if resource_type not in RESOURCE_TYPE_MANAGERS: raise exceptions.CommandError(_("Unsupported resource type")) if resource_id is not None: res_manager = RESOURCE_TYPE_MANAGERS[resource_type] resource_id = osc_utils.find_resource( getattr(share_client, res_manager), parsed_args.resource ).id elif resource_id and not uuidutils.is_uuid_like(resource_id): raise exceptions.CommandError( _("Provide resource ID or specify --resource-type.")) search_opts = { 'all_projects': all_projects, 'project_id': project_id, 'user_id': user_id, 'id': parsed_args.id, 'resource_id': resource_id, 'resource_type': parsed_args.resource_type, 'resource_action': parsed_args.resource_action, 'lock_context': parsed_args.lock_context, 'created_before': parsed_args.before, 'created_since': parsed_args.since, 'limit': parsed_args.limit, 'offset': parsed_args.offset, } resource_locks = share_client.resource_locks.list( search_opts=search_opts, sort_key=parsed_args.sort_key, sort_dir=parsed_args.sort_dir ) return (columns, (osc_utils.get_item_properties (m, columns) for m in resource_locks)) class ShowResourceLock(command.ShowOne): """Show details about a resource lock.""" _description = _("Show details about a resource lock") def get_parser(self, prog_name): parser = super(ShowResourceLock, self).get_parser(prog_name) parser.add_argument( 'lock', metavar='', help=_('ID of resource lock to show.')) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share resource_lock = apiutils.find_resource( share_client.resource_locks, parsed_args.lock) return ( LOCK_DETAIL_ATTRIBUTES, osc_utils.get_dict_properties(resource_lock._info, LOCK_DETAIL_ATTRIBUTES) ) class SetResourceLock(command.Command): """Set resource lock properties.""" _description = _("Update resource lock properties") def get_parser(self, prog_name): parser = super(SetResourceLock, self).get_parser(prog_name) parser.add_argument( 'lock', metavar='', help='ID of lock to update.') parser.add_argument( '--resource-action', '--resource_action', metavar='', help='Resource action to set in the resource lock') parser.add_argument( '--lock-reason', '--lock_reason', '--reason', dest='lock_reason', help="Reason for the resource lock") return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share update_kwargs = {} if parsed_args.resource_action is not None: update_kwargs['resource_action'] = parsed_args.resource_action if parsed_args.lock_reason is not None: update_kwargs['lock_reason'] = parsed_args.lock_reason if update_kwargs: share_client.resource_locks.update( parsed_args.lock, **update_kwargs ) class UnsetResourceLock(command.Command): """Unsets a property on a resource lock.""" _description = _("Remove resource lock properties") def get_parser(self, prog_name): parser = super(UnsetResourceLock, self).get_parser(prog_name) parser.add_argument( 'lock', metavar='', help='ID of resource lock to update.') parser.add_argument( '--lock-reason', '--lock_reason', '--reason', dest='lock_reason', action='store_true', default=False, help="Unset the lock reason. (Default=False)") return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share if parsed_args.lock_reason: share_client.resource_locks.update( parsed_args.lock, lock_reason=None ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/security_services.py0000664000175000017500000004754100000000000025437 0ustar00zuulzuul00000000000000# 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 _ LOG = logging.getLogger(__name__) class CreateShareSecurityService(command.ShowOne): """Create security service used by project.""" _description = _("Create security service used by project.") def get_parser(self, prog_name): parser = super(CreateShareSecurityService, self).get_parser(prog_name) parser.add_argument( 'type', metavar='', default=None, choices=['ldap', 'kerberos', 'active_directory'], help=_("Security service type. Possible options are: " "'ldap', 'kerberos', 'active_directory'.") ) parser.add_argument( '--dns-ip', metavar='', default=None, help=_("DNS IP address of the security service used " "inside project's network.") ) parser.add_argument( '--ou', metavar='', default=None, help=_("Security service OU (Organizational Unit). " "Available only for microversion >= 2.44.") ) parser.add_argument( '--server', metavar='', default=None, help=_("Security service IP address or hostname.") ) parser.add_argument( '--domain', metavar='', default=None, help=_("Security service domain.") ) parser.add_argument( '--user', metavar='= 2.76. Can be provided in the " "place of '--server' but not along with it.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share kwargs = { 'dns_ip': parsed_args.dns_ip, 'server': parsed_args.server, 'domain': parsed_args.domain, 'user': parsed_args.user, 'password': parsed_args.password, 'name': parsed_args.name, 'description': parsed_args.description, } if share_client.api_version >= api_versions.APIVersion("2.44"): kwargs['ou'] = parsed_args.ou elif parsed_args.ou: raise exceptions.CommandError( "Defining a security service Organizational Unit is " "available only for microversion >= 2.44") if share_client.api_version >= api_versions.APIVersion("2.76"): kwargs['default_ad_site'] = parsed_args.default_ad_site elif parsed_args.default_ad_site: raise exceptions.CommandError( "Defining a security service Default AD site is " "available only for microversion >= 2.76") if parsed_args.type == 'active_directory': server = parsed_args.server default_ad_site = parsed_args.default_ad_site if server and default_ad_site: raise exceptions.CommandError( "Cannot create security service because both " "server and 'default_ad_site' were provided. " "Specify either server or 'default_ad_site'.") security_service = share_client.security_services.create( parsed_args.type, **kwargs) return self.dict2columns(security_service._info) class DeleteShareSecurityService(command.Command): """Delete one or more security services.""" _description = _("Delete one or more security services.") def get_parser(self, prog_name): parser = super(DeleteShareSecurityService, self).get_parser(prog_name) parser.add_argument( 'security_service', metavar='', nargs="+", help=_("Name or ID of the security service(s) to delete.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for security_service in parsed_args.security_service: try: security_service_obj = oscutils.find_resource( share_client.security_services, security_service) share_client.security_services.delete( security_service_obj) except Exception as e: result += 1 LOG.error(f"Failed to delete security service with " f"name or ID {security_service}: {e}") if result > 0: total = len(parsed_args.security_service) msg = (f"{result} of {total} security services failed " f"to be deleted.") raise exceptions.CommandError(msg) class ShowShareSecurityService(command.ShowOne): """Show security service.""" _description = _("Show security service.") def get_parser(self, prog_name): parser = super(ShowShareSecurityService, self).get_parser(prog_name) parser.add_argument( 'security_service', metavar='', help=_("Security service name or ID to show.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share security_service = oscutils.find_resource( share_client.security_services, parsed_args.security_service) data = security_service._info if parsed_args.formatter == 'table': if 'share_networks' in data.keys(): data['share_networks'] = "\n".join( data['share_networks']) return self.dict2columns(data) class SetShareSecurityService(command.Command): """Set security service.""" _description = _("Set security service.") def get_parser(self, prog_name): parser = super(SetShareSecurityService, self).get_parser(prog_name) parser.add_argument( 'security_service', metavar='', help=_("Security service name or ID.") ) parser.add_argument( '--dns-ip', metavar='', default=None, help=_("Set DNS IP address used inside project's network.") ) parser.add_argument( '--ou', metavar='', default=None, help=_("Set security service OU (Organizational Unit). " "Available only for microversion >= 2.44.") ) parser.add_argument( '--server', metavar='', default=None, help=_("Set security service IP address or hostname.") ) parser.add_argument( '--domain', metavar='', default=None, help=_("Set security service domain.") ) parser.add_argument( '--user', metavar='= 2.76.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share security_service = oscutils.find_resource( share_client.security_services, parsed_args.security_service) kwargs = { 'dns_ip': parsed_args.dns_ip, 'server': parsed_args.server, 'domain': parsed_args.domain, 'user': parsed_args.user, 'password': parsed_args.password, 'name': parsed_args.name, 'description': parsed_args.description, } if share_client.api_version >= api_versions.APIVersion("2.44"): kwargs['ou'] = parsed_args.ou elif parsed_args.ou: raise exceptions.CommandError(_( "Setting a security service Organizational Unit is " "available only for microversion >= 2.44")) if share_client.api_version >= api_versions.APIVersion("2.76"): kwargs['default_ad_site'] = parsed_args.default_ad_site elif parsed_args.default_ad_site: raise exceptions.CommandError( "Defining a security service Default AD site is " "available only for microversion >= 2.76") if security_service.type == 'active_directory': server = parsed_args.server default_ad_site = parsed_args.default_ad_site if server and default_ad_site: raise exceptions.CommandError( "Cannot set security service because both " "server and 'default_ad_site' were provided. " "Specify either server or 'default_ad_site'.") try: security_service.update(**kwargs) except Exception as e: raise exceptions.CommandError( f"One or more set operations failed: {e}") class UnsetShareSecurityService(command.Command): """Unset security service.""" _description = _("Unset security service.") def get_parser(self, prog_name): parser = super(UnsetShareSecurityService, self).get_parser(prog_name) parser.add_argument( 'security_service', metavar='', help=_("Security service name or ID.") ) parser.add_argument( '--dns-ip', action='store_true', help=_("Unset DNS IP address used inside project's network.") ) parser.add_argument( '--ou', action='store_true', help=_("Unset security service OU (Organizational Unit). " "Available only for microversion >= 2.44.") ) parser.add_argument( '--server', action='store_true', help=_("Unset security service IP address or hostname.") ) parser.add_argument( '--domain', action='store_true', help=_("Unset security service domain.") ) parser.add_argument( '--user', action='store_true', help=_("Unset security service user or group used by project.") ) parser.add_argument( '--password', action='store_true', help=_("Unset password used by user.") ) parser.add_argument( '--name', action='store_true', help=_("Unset security service name.") ) parser.add_argument( '--description', action='store_true', help=_("Unset security service description.") ) parser.add_argument( '--default-ad-site', dest='default_ad_site', action='store_true', help=_("Default AD site. " "Available only for microversion >= 2.76.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share security_service = oscutils.find_resource( share_client.security_services, parsed_args.security_service) kwargs = {} args = ['dns_ip', 'server', 'domain', 'user', 'password', 'name', 'description'] for arg in args: if getattr(parsed_args, arg): # the SDK unsets a value if it is an empty string kwargs[arg] = '' if (parsed_args.ou and share_client.api_version >= api_versions.APIVersion("2.44")): # the SDK unsets a value if it is an empty string kwargs['ou'] = '' elif parsed_args.ou: raise exceptions.CommandError(_( "Unsetting a security service Organizational Unit is " "available only for microversion >= 2.44")) if (parsed_args.default_ad_site and share_client.api_version >= api_versions.APIVersion("2.76")): # the SDK unsets a value if it is an empty string kwargs['default_ad_site'] = '' elif parsed_args.default_ad_site: raise exceptions.CommandError(_( "Unsetting a security service Default AD site is " "available only for microversion >= 2.76")) try: security_service.update(**kwargs) except Exception as e: raise exceptions.CommandError( f"One or more unset operations failed: {e}") class ListShareSecurityService(command.Lister): """List security services.""" _description = _("List security services.") def get_parser(self, prog_name): parser = super(ListShareSecurityService, self).get_parser(prog_name) parser.add_argument( '--all-projects', action='store_true', help=_("Display information from all projects (Admin only).") ) parser.add_argument( '--share-network', metavar='', default=None, help=_("Filter results by share network name or ID.") ) parser.add_argument( '--status', metavar='', default=None, help=_("Filter results by status.") ) parser.add_argument( '--name', metavar='', default=None, help=_("Filter results by security service name.") ) parser.add_argument( '--type', metavar='', default=None, help=_("Filter results by security service type.") ) parser.add_argument( '--user', metavar='= 2.44.") ) parser.add_argument( '--default-ad-site', metavar='', dest='default_ad_site', default=None, help=_("Filter results by security service default_ad_site. " "Available only for microversion >= 2.76.") ) parser.add_argument( '--server', metavar='', default=None, help=_("Filter results by security service IP " "address or hostname.") ) parser.add_argument( '--domain', metavar='', default=None, help=_("Filter results by security service domain.") ) parser.add_argument( '--detail', action='store_true', help=_("Show detailed information about filtered " "security services.") ) parser.add_argument( "--limit", metavar="", type=int, default=None, action=parseractions.NonNegativeAction, help=_("Limit the number of security services returned") ) parser.add_argument( "--marker", metavar="", help=_("The last security service ID of the previous page") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share columns = ['ID', 'Name', 'Status', 'Type'] if parsed_args.all_projects: columns.append('Project ID') if parsed_args.detail: columns.append('Share Networks') search_opts = { 'all_tenants': parsed_args.all_projects, 'status': parsed_args.status, 'name': parsed_args.name, 'type': parsed_args.type, 'user': parsed_args.user, 'dns_ip': parsed_args.dns_ip, 'server': parsed_args.server, 'domain': parsed_args.domain, 'offset': parsed_args.marker, 'limit': parsed_args.limit, } if (parsed_args.ou and share_client.api_version >= api_versions.APIVersion("2.44")): search_opts['ou'] = parsed_args.ou elif parsed_args.ou: raise exceptions.CommandError(_( "Filtering results by security service Organizational Unit is " "available only for microversion >= 2.44")) if (parsed_args.default_ad_site and share_client.api_version >= api_versions.APIVersion("2.76")): search_opts['default_ad_site'] = parsed_args.default_ad_site elif parsed_args.default_ad_site: raise exceptions.CommandError(_( "Filtering results by security service Default AD site is " "available only for microversion >= 2.76")) if parsed_args.share_network: search_opts['share_network_id'] = oscutils.find_resource( share_client.share_networks, parsed_args.share_network).id data = share_client.security_services.list( search_opts=search_opts, detailed=parsed_args.detail ) return ( columns, (oscutils.get_item_properties(s, columns) for s in data) ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/services.py0000664000175000017500000001255600000000000023506 0ustar00zuulzuul00000000000000# 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.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils from manilaclient import api_versions from manilaclient.common._i18n import _ class SetShareService(command.Command): """Enable/disable share service (Admin only).""" _description = _("Enable/Disable share service (Admin only).") def get_parser(self, prog_name): parser = super(SetShareService, self).get_parser(prog_name) parser.add_argument( 'host', metavar='', help=_("Host name as 'example_host@example_backend'.") ) parser.add_argument( 'binary', metavar='', help=_("Service binary, could be 'manila-share', " "'manila-scheduler' or 'manila-data'") ) enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', action='store_true', help=_('Enable share service'), ) enable_group.add_argument( '--disable', action='store_true', help=_('Disable share service'), ) parser.add_argument( "--disable-reason", metavar="", help=_("Reason for disabling the service " "(should be used with --disable option)") ) return parser def take_action(self, parsed_args): if parsed_args.disable_reason and not parsed_args.disable: msg = _("Cannot specify option --disable-reason without " "--disable specified.") raise exceptions.CommandError(msg) share_client = self.app.client_manager.share if parsed_args.enable: try: share_client.services.enable( parsed_args.host, parsed_args.binary) except Exception as e: raise exceptions.CommandError(_( "Failed to enable service: %s" % e)) if parsed_args.disable: if parsed_args.disable_reason: if share_client.api_version < api_versions.APIVersion("2.83"): raise exceptions.CommandError( "Service disable reason can be specified only with " "manila API version >= 2.83") try: if parsed_args.disable_reason: share_client.services.disable( parsed_args.host, parsed_args.binary, disable_reason=parsed_args.disable_reason) else: share_client.services.disable( parsed_args.host, parsed_args.binary) except Exception as e: raise exceptions.CommandError(_( "Failed to disable service: %s" % e)) class ListShareService(command.Lister): """List share services (Admin only).""" _description = _("List share services (Admin only).") def get_parser(self, prog_name): parser = super(ListShareService, self).get_parser(prog_name) parser.add_argument( "--host", metavar="", default=None, help=_("Filter services by name of the host.") ) parser.add_argument( "--binary", metavar="", default=None, help=_("Filter services by the name of the service.") ) parser.add_argument( "--status", metavar="", default=None, help=_("Filter results by status.") ) parser.add_argument( "--state", metavar="", default=None, choices=['up', 'down'], help=_("Filter results by state.") ) parser.add_argument( "--zone", metavar="", default=None, help=_("Filter services by their availability zone.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share search_opts = { 'host': parsed_args.host, 'binary': parsed_args.binary, 'status': parsed_args.status, 'state': parsed_args.state, 'zone': parsed_args.zone, } services = share_client.services.list(search_opts=search_opts) columns = [ 'ID', 'Binary', 'Host', 'Zone', 'Status', 'State', 'Updated At' ] if share_client.api_version >= api_versions.APIVersion("2.83"): columns.append('Disabled Reason') data = (osc_utils.get_dict_properties( service._info, columns) for service in services) return (columns, data) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share.py0000664000175000017500000014746100000000000022771 0ustar00zuulzuul00000000000000# 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 import api_versions from manilaclient.common._i18n import _ from manilaclient.common.apiclient import exceptions as apiclient_exceptions 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") 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=_('The share type to create the share with. If not ' 'specified, unless creating from a snapshot, the default ' 'share type will be used.') ) 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).') ) parser.add_argument( '--wait', action='store_true', default=False, help=_('Wait for share creation') ) parser.add_argument( "--scheduler-hint", metavar="", default={}, action=parseractions.KeyValueAction, help=_("Set Scheduler hints for the share as key=value pairs, " "possible keys are same_host, different_host." "(repeat option to set multiple hints)"), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share if parsed_args.name: if parsed_args.name.capitalize() == 'None': raise apiclient_exceptions.CommandError( "Share name cannot be with the value 'None'") share_type = None if parsed_args.share_type: share_type = apiutils.find_resource(share_client.share_types, parsed_args.share_type).id elif not parsed_args.snapshot_id: try: share_type = share_client.share_types.get( share_type='default').id except apiclient_exceptions.CommandError: msg = ("There is no default share type available. You must " "pick a valid share type to create a share.") raise exceptions.CommandError(msg) 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) snapshot_id = snapshot.id size = max(size or 0, snapshot.size) scheduler_hints = {} if parsed_args.scheduler_hint: if share_client.api_version < api_versions.APIVersion('2.65'): raise exceptions.CommandError( 'Setting share scheduler hints for a share is ' 'available only for API microversion >= 2.65') else: scheduler_hints = utils.extract_key_value_options( parsed_args.scheduler_hint) same_host_hint_shares = scheduler_hints.get('same_host') different_host_hint_shares = scheduler_hints.get( 'different_host') if same_host_hint_shares: same_host_hint_shares = [ apiutils.find_resource(share_client.shares, sh).id for sh in same_host_hint_shares.split(',') ] scheduler_hints['same_host'] = ( ','.join(same_host_hint_shares)) if different_host_hint_shares: different_host_hint_shares = [ apiutils.find_resource(share_client.shares, sh).id for sh in different_host_hint_shares.split(',') ] scheduler_hints['different_host'] = ( ','.join(different_host_hint_shares)) body = { 'share_proto': parsed_args.share_proto, 'size': 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, 'scheduler_hints': scheduler_hints } share = share_client.shares.create(**body) if parsed_args.wait: if not oscutils.wait_for_status( status_f=share_client.shares.get, res_id=share.id, success_status=['available'] ): LOG.error(_("ERROR: Share is in error state.")) share = apiutils.find_resource(share_client.shares, share.id) 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)") ) parser.add_argument( "--wait", action='store_true', default=False, help=_("Wait for share deletion") ) parser.add_argument( "--soft", action='store_true', default=False, help=_("Soft delete one or more shares.") ) 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 = None if parsed_args.share_group: share_group_id = apiutils.find_resource( share_client.share_groups, parsed_args.share_group).id if parsed_args.force: share_client.shares.force_delete(share_obj) elif parsed_args.soft: if share_client.api_version >= api_versions.APIVersion( '2.69'): share_client.shares.soft_delete(share_obj) else: raise exceptions.CommandError( "Soft Deleting shares is only " "available with manila API version >= 2.69") else: share_client.shares.delete(share_obj, share_group_id) if parsed_args.wait: if not oscutils.wait_for_delete( manager=share_client.shares, res_id=share_obj.id): result += 1 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( '--export-location', metavar='', help=_('Filter shares by export location id or path. ' 'Available only for microversion >= 2.35'), ) parser.add_argument( '--soft-deleted', action='store_true', help=_('Get shares in recycle bin. If this parameter is set to ' 'True (Default=False), only shares in the recycle bin ' 'will be displayed. Available only for microversion >= ' '2.69.') ) 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'), ) parser.add_argument( "--name~", metavar="", default=None, help=_("Filter results matching a share name pattern. " "Available only for microversion >= 2.36.") ) parser.add_argument( '--description~', metavar="", default=None, help=_("Filter results matching a share description pattern." "Available only for microversion >= 2.36.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share identity_client = self.app.client_manager.identity 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, } if share_client.api_version >= api_versions.APIVersion('2.69'): search_opts['is_soft_deleted'] = parsed_args.soft_deleted elif (getattr(parsed_args, 'soft_deleted')): raise exceptions.CommandError( "Filtering soft deleted shares is only " "available with manila API version >= 2.69") if share_client.api_version >= api_versions.APIVersion("2.35"): search_opts['export_location'] = parsed_args.export_location elif (getattr(parsed_args, 'export_location')): raise exceptions.CommandError( "Filtering by export location is only " "available with manila API version >= 2.35") # NOTE(vkmc) We implemented sorting and filtering in manilaclient # but we will use the one provided by osc if share_client.api_version >= api_versions.APIVersion("2.36"): search_opts['name~'] = getattr(parsed_args, 'name~') search_opts['description~'] = getattr(parsed_args, 'description~') elif (getattr(parsed_args, 'name~') or getattr(parsed_args, 'description~')): raise exceptions.CommandError( "Pattern based filtering (name~ and description~)" " is only available with manila API version >= 2.36") 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.convert_dict_list_to_string( export_locations, ignored_keys=['replica_state', 'availability_zone', 'share_replica_id'] ) 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. ') ) parser.add_argument( '--status', metavar='', default=None, help=_('Explicitly update the status of a share (Admin only). ' 'Examples include: available, error, creating, deleting, ' 'error_deleting.') ) parser.add_argument( '--task-state', metavar="", required=False, default=None, 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. ") ) 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_obj.set_metadata(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 parsed_args.status: try: share_obj.reset_state(parsed_args.status) except Exception as e: LOG.error(_( "Failed to set status for the share: %s"), e) result += 1 if parsed_args.task_state: try: share_obj.reset_task_state(parsed_args.task_state) except Exception as e: LOG.error(_("Failed to update share task state " "%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: %s"), e) result += 1 if parsed_args.property: for key in parsed_args.property: try: share_obj.delete_metadata([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")) class ResizeShare(command.Command): """Resize a share""" _description = _("Resize a share") def get_parser(self, prog_name): parser = super(ResizeShare, self).get_parser(prog_name) parser.add_argument( 'share', metavar="", help=_('Name or ID of share to resize') ) parser.add_argument( 'new_size', metavar="", type=int, help=_('New size of share, in GiBs') ) parser.add_argument( '--wait', action='store_true', default=False, help=_('Wait for share resize') ) parser.add_argument( '--force', action='store_true', default=False, help=_('Only applicable when increasing the size of the ' 'share,only available with microversion ' '2.64 and higher. (admin only)') ) 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) share_size = share._info['size'] new_size = parsed_args.new_size if share_size > new_size: try: share_client.shares.shrink(share, new_size) except Exception as e: raise exceptions.CommandError(_( "Share resize failed: %s" % e )) elif share_size < new_size: force = False if parsed_args.force: if share_client.api_version < api_versions.APIVersion("2.64"): raise exceptions.CommandError( 'args force is available only for ' 'API microversion >= 2.64') force = True try: if force: share_client.shares.extend(share, new_size, force=force) else: share_client.shares.extend(share, new_size) except Exception as e: raise exceptions.CommandError(_( "Share resize failed: %s" % e )) else: raise exceptions.CommandError(_( "Share size is already at %s GiBs" % new_size )) if parsed_args.wait: if not oscutils.wait_for_status( status_f=share_client.shares.get, res_id=share.id, success_status=['available'] ): raise exceptions.CommandError(_( "Share not available after resize attempt.")) class AdoptShare(command.ShowOne): """Adopt share not handled by Manila (Admin only).""" _description = _("Adopt a share") def get_parser(self, prog_name): parser = super(AdoptShare, self).get_parser(prog_name) parser.add_argument( 'service_host', metavar="", help=_('Service host: some.host@driver#pool.') ) parser.add_argument( 'protocol', metavar="", help=_( 'Protocol of the share to manage, such as NFS or CIFS.') ) parser.add_argument( 'export_path', metavar="", 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.') ) parser.add_argument( '--name', metavar="", default=None, help=_('Optional share name. (Default=None)') ) parser.add_argument( '--description', metavar="", default=None, help=_('Optional share description. (Default=None)') ) parser.add_argument( '--share-type', metavar="", default=None, help=_( 'Optional share type assigned to share. (Default=None)') ) parser.add_argument( '--driver-options', type=str, nargs='*', metavar='', default=None, help=_( 'Optional driver options as key=value pairs (Default=None).') ) parser.add_argument( '--public', action='store_true', help=_('Level of visibility for share. Defines whether other ' 'projects are able to see it or not. Available only for ' 'microversion >= 2.8. (Default=False)') ) parser.add_argument( '--share-server-id', metavar="", 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)') ) parser.add_argument( "--wait", action='store_true', help=_("Wait until share is adopted") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share kwargs = { 'service_host': parsed_args.service_host, 'protocol': parsed_args.protocol, 'export_path': parsed_args.export_path, 'name': parsed_args.name, 'description': parsed_args.description } share_type = None if parsed_args.share_type: share_type = apiutils.find_resource(share_client.share_types, parsed_args.share_type).id kwargs['share_type'] = share_type driver_options = None if parsed_args.driver_options: driver_options = utils.extract_properties( parsed_args.driver_options) kwargs['driver_options'] = driver_options if parsed_args.public: if share_client.api_version >= api_versions.APIVersion("2.8"): kwargs['public'] = True else: raise exceptions.CommandError( 'Setting share visibility while adopting a share is ' 'available only for API microversion >= 2.8') if parsed_args.share_server_id: if share_client.api_version >= api_versions.APIVersion("2.49"): kwargs['share_server_id'] = parsed_args.share_server_id else: raise exceptions.CommandError( 'Selecting a share server ID is available only for ' 'API microversion >= 2.49') share = share_client.shares.manage(**kwargs) if parsed_args.wait: if not oscutils.wait_for_status( status_f=share_client.shares.get, res_id=share.id, success_status=['available'], error_status=['manage_error', 'error'] ): LOG.error(_("ERROR: Share is in error state.")) share = apiutils.find_resource(share_client.shares, share.id) share._info.pop('links', None) return self.dict2columns(share._info) class AbandonShare(command.Command): """Abandon a share (Admin only).""" _description = _("Abandon a share") def get_parser(self, prog_name): parser = super(AbandonShare, self).get_parser(prog_name) parser.add_argument( 'share', metavar="", nargs="+", help=_('Name or ID of the share(s)') ) parser.add_argument( "--wait", action='store_true', help=_("Wait until share is abandoned") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for share in parsed_args.share: try: share_obj = apiutils.find_resource( share_client.shares, share ) share_client.shares.unmanage(share_obj) if parsed_args.wait: # 'wait_for_delete' checks that the resource is no longer # retrievable with the given 'res_id' so we can use it # to check that the share has been abandoned if not oscutils.wait_for_delete( manager=share_client.shares, res_id=share_obj.id): result += 1 except Exception as e: result += 1 LOG.error(_("Failed to abandon share with " "name or ID '%(share)s': %(e)s"), {'share': share, 'e': e}) if result > 0: total = len(parsed_args.share) msg = (_("Failed to abandon %(result)s out of %(total)s shares.") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) class ShareExportLocationShow(command.ShowOne): """Show export location of a share.""" _description = _("Show export location of a share") def get_parser(self, prog_name): parser = super(ShareExportLocationShow, self).get_parser( prog_name) parser.add_argument( 'share', metavar="", help=_('Name or ID of share') ) parser.add_argument( 'export_location', metavar="", help=_('ID of the share export location') ) 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) export_location = share_client.share_export_locations.get( share=share, export_location=parsed_args.export_location ) return self.dict2columns(export_location._info) class ShareExportLocationList(command.Lister): """List export locations of a share.""" _description = _("List export location of a share") def get_parser(self, prog_name): parser = super(ShareExportLocationList, self).get_parser( prog_name) parser.add_argument( 'share', metavar="", help=_('Name or ID of share') ) 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) export_locations = share_client.share_export_locations.list( share=share ) list_of_keys = [ 'ID', 'Path', 'Preferred' ] return (list_of_keys, (oscutils.get_item_properties (s, list_of_keys) for s in export_locations)) class ShowShareProperties(command.ShowOne): """Show properties of a share""" _description = _("Show share properties") def get_parser(self, prog_name): parser = super(ShowShareProperties, self).get_parser(prog_name) parser.add_argument( 'share', metavar="", help=_('Name or ID of share') ) 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) share_properties = share_client.shares.get_metadata(share_obj) return self.dict2columns(share_properties._info) class RevertShare(command.Command): """Revert a share to snapshot.""" _description = _("Revert a share to the specified snapshot.") def get_parser(self, prog_name): parser = super(RevertShare, self).get_parser(prog_name) parser.add_argument( 'snapshot', metavar="", help=_('Name or ID of the snapshot to restore. The snapshot ' 'must be the most recent one known to manila.') ) parser.add_argument( '--wait', action='store_true', default=False, help=_('Wait for share revert') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share snapshot = apiutils.find_resource(share_client.share_snapshots, parsed_args.snapshot) share = apiutils.find_resource(share_client.shares, snapshot.share_id) try: share.revert_to_snapshot(snapshot) except Exception as e: raise exceptions.CommandError(_( "Failed to revert share to snapshot: %s" % (e))) if parsed_args.wait: if not oscutils.wait_for_status( status_f=share_client.shares.get, res_id=share.id, success_status=['available'] ): raise exceptions.CommandError(_( "Share not available after revert attempt.")) class ShareMigrationStart(command.Command): """Migrates share to a new host (Admin only, Experimental).""" _description = _("Migrates share to a new host.") def get_parser(self, prog_name): parser = super(ShareMigrationStart, self).get_parser(prog_name) parser.add_argument( 'share', metavar="", help=_('Name or ID of share to migrate.') ) parser.add_argument( 'host', metavar="", help=_("Destination host where share will be migrated to. Use the " "format 'host@backend#pool'.") ) parser.add_argument( '--force-host-assisted-migration', metavar="", choices=['True', 'False'], default=False, help=_("Enforces the use of the host-assisted migration approach, " "which bypasses driver optimizations. Default=False.") ) parser.add_argument( '--preserve-metadata', metavar="", required=True, choices=['True', 'False'], help=_("Enforces migration to preserve all file metadata when " "moving its contents. If set to True, host-assisted" "migration will not be attempted.") ) parser.add_argument( '--preserve-snapshots', metavar="", required=True, choices=['True', 'False'], help=_("Enforces migration of the share snapshots to the " "destination. If set to True, host-assisted migration" "will not be attempted.") ) parser.add_argument( '--writable', metavar="", required=True, choices=['True', 'False'], help=_("Enforces migration to keep the share writable while " "contents are being moved. If set to True, host-assisted" "migration will not be attempted.") ) parser.add_argument( '--nondisruptive', metavar="", choices=['True', 'False'], required=True, help=_("Enforces migration to be nondisruptive. If set to True, " "host-assisted migration will not be attempted.") ) parser.add_argument( '--new-share-network', metavar="", default=None, 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.") ) parser.add_argument( '--new-share-type', metavar="", default=None, 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.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share new_share_net_id = None if parsed_args.new_share_network: new_share_net_id = apiutils.find_resource( share_client.share_networks, parsed_args.new_share_network).id new_share_type_id = None if parsed_args.new_share_type: new_share_type_id = apiutils.find_resource( share_client.share_types, parsed_args.new_share_type).id share = apiutils.find_resource(share_client.shares, parsed_args.share) share.migration_start(parsed_args.host, parsed_args.force_host_assisted_migration, parsed_args.preserve_metadata, parsed_args.writable, parsed_args.nondisruptive, parsed_args.preserve_snapshots, new_share_net_id, new_share_type_id) class ShareMigrationCancel(command.Command): """Cancels migration of a given share when copying (Admin only, Experimental). """ _description = _("Cancels migration of a given share when copying") def get_parser(self, prog_name): parser = super(ShareMigrationCancel, self).get_parser(prog_name) parser.add_argument( 'share', metavar="", help=_('Name or ID of share to migrate.') ) 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) share.migration_cancel() class ShareMigrationComplete(command.Command): """Completes migration for a given share (Admin only, Experimental).""" _description = _("Completes migration for a given share") def get_parser(self, prog_name): parser = super(ShareMigrationComplete, self).get_parser(prog_name) parser.add_argument( 'share', metavar="", help=_('Name or ID of share to migrate.') ) 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) share.migration_complete() class ShareMigrationShow(command.ShowOne): """Gets migration progress of a given share when copying (Admin only, Experimental). """ _description = _("Gets migration progress of a given share when copying") def get_parser(self, prog_name): parser = super(ShareMigrationShow, self).get_parser(prog_name) parser.add_argument( 'share', metavar="", help=_('Name or ID of the share to get share migration progress ' 'information.') ) 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) result = share.migration_get_progress() return self.dict2columns(result[1]) class RestoreShare(command.Command): """Restore one or more shares from recycle bin""" _description = _("Restores this share or more shares from the recycle bin") def get_parser(self, prog_name): parser = super(RestoreShare, self).get_parser(prog_name) parser.add_argument( 'share', metavar="", nargs="+", help=_('Name or ID of the share(s)') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share if share_client.api_version >= api_versions.APIVersion('2.69'): failure_count = 0 for share in parsed_args.share: try: share_client.shares.restore(share) except Exception as e: failure_count += 1 LOG.error(_("Failed to restore share with " "name or ID '%(share)s': %(e)s"), {'share': share, 'e': e}) if failure_count > 0: total = len(parsed_args.share) msg = (f"Failed to restore {failure_count} out of " f"{total} shares.") msg = _(msg) raise exceptions.CommandError(msg) else: raise exceptions.CommandError( "Restoring a share from the recycle bin is only " "available with manila API version >= 2.69") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_access_rules.py0000664000175000017500000004274100000000000025517 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.common._i18n import _ from manilaclient.common.apiclient import utils as apiutils from manilaclient.osc import utils ACCESS_RULE_ATTRIBUTES = [ 'id', 'share_id', 'access_level', 'access_to', 'access_type', 'state', 'access_key', 'created_at', 'updated_at', 'properties' ] LOG = logging.getLogger(__name__) class ShareAccessAllow(command.ShowOne): """Create a new share access rule.""" _description = _("Create new share access rule") def get_parser(self, prog_name): parser = super(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.') ) parser.add_argument( "--wait", action='store_true', help=_("Wait for share access rule creation.") ) parser.add_argument( "--lock-visibility", action='store_true', default=False, help=_("Whether the sensitive fields of the access rule redacted " "to other users. Only available with API version >= 2.82.") ) parser.add_argument( "--lock-deletion", action='store_true', default=False, help=_("When enabled, a 'delete' lock will be placed against the " "rule and the rule cannot be deleted while the lock " "exists. Only available with API version >= 2.82.") ) parser.add_argument( '--lock-reason', metavar="", type=str, default=None, help=_("Reason for locking the access rule. Should only be " "provided alongside a deletion or visibility lock. " "Only available with API version >= 2.82.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share = apiutils.find_resource(share_client.shares, parsed_args.share) lock_kwargs = {} if parsed_args.lock_visibility: lock_kwargs['lock_visibility'] = parsed_args.lock_visibility if parsed_args.lock_deletion: lock_kwargs['lock_deletion'] = parsed_args.lock_deletion if parsed_args.lock_reason: lock_kwargs['lock_reason'] = parsed_args.lock_reason if (lock_kwargs and share_client.api_version < api_versions.APIVersion( "2.82")): raise exceptions.CommandError( 'Restricted access rules are only available starting ' 'from API version 2.82.') if (lock_kwargs.get('lock_reason', None) and not (lock_kwargs.get('lock_visibility', None) or lock_kwargs.get('lock_deletion', None))): raise exceptions.CommandError( 'Lock reason can only be set while locking the deletion or ' 'visibility.') properties = {} if parsed_args.properties: if share_client.api_version >= api_versions.APIVersion("2.45"): properties = utils.extract_properties(parsed_args.properties) else: raise exceptions.CommandError( "Adding properties to access rules is supported only " "with API microversion 2.45 and beyond") try: share_access_rule = share.allow( access_type=parsed_args.access_type, access=parsed_args.access_to, access_level=parsed_args.access_level, metadata=properties, **lock_kwargs ) if parsed_args.wait: if not oscutils.wait_for_status( status_f=share_client.share_access_rules.get, res_id=share_access_rule['id'], status_field='state' ): LOG.error(_("ERROR: Share access rule is in error state.")) share_access_rule = oscutils.find_resource( share_client.share_access_rules, share_access_rule['id'])._info share_access_rule.update( { 'properties': utils.format_properties( share_access_rule.pop('metadata', {})) } ) return (ACCESS_RULE_ATTRIBUTES, oscutils.get_dict_properties( share_access_rule, ACCESS_RULE_ATTRIBUTES)) except Exception as e: raise exceptions.CommandError( "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") 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.') ) parser.add_argument( "--wait", action='store_true', default=False, help=_("Wait for share access rule deletion") ) parser.add_argument( "--unrestrict", action='store_true', default=False, help=_("Seek access rule deletion despite restrictions. Only " "available with API version >= 2.82.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share = apiutils.find_resource(share_client.shares, parsed_args.share) kwargs = {} if parsed_args.unrestrict: if share_client.api_version < api_versions.APIVersion("2.82"): raise exceptions.CommandError( 'Restricted access rules are only available starting from ' 'API version 2.82.') kwargs['unrestrict'] = True error = None try: share.deny(parsed_args.id, **kwargs) if parsed_args.wait: if not oscutils.wait_for_delete( manager=share_client.share_access_rules, res_id=parsed_args.id): error = _("Failed to delete share access rule with ID: %s" % parsed_args.id) except Exception as e: error = e if error: raise exceptions.CommandError(_( "Failed to delete share access rule for share " "'%s': %s" % (share, error))) class ListShareAccess(command.Lister): """List share access rules.""" _description = _("List share access rule") 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.') ) # 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'), ) parser.add_argument( "--access-type", metavar="", default=None, help=_("Filter access rules by the access type.") ) parser.add_argument( "--access-key", metavar="", default=None, help=_("Filter access rules by the access key.") ) parser.add_argument( "--access-to", metavar="", default=None, help=_("Filter access rules by the access to field.") ) parser.add_argument( "--access-level", metavar="", default=None, help=_("Filter access rules by the access level.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share = apiutils.find_resource(share_client.shares, parsed_args.share) access_type = parsed_args.access_type access_key = parsed_args.access_key access_to = parsed_args.access_to access_level = parsed_args.access_level extended_filter_keys = { 'access_type': access_type, 'access_key': access_key, 'access_to': access_to, 'access_level': access_level } if (any(extended_filter_keys.values()) and share_client.api_version < api_versions.APIVersion( "2.82")): raise exceptions.CommandError( 'Filtering access rules by access_type, access_key, access_to ' 'and access_level is available starting from API version ' '2.82.') search_opts = {} if share_client.api_version >= api_versions.APIVersion("2.82"): for filter_key, filter_value in extended_filter_keys.items(): if filter_value: search_opts[filter_key] = filter_value if share_client.api_version >= api_versions.APIVersion("2.45"): if parsed_args.properties: search_opts = { 'metadata': utils.extract_properties( parsed_args.properties) } access_list = share_client.share_access_rules.access_list( share, search_opts) elif parsed_args.properties: raise exceptions.CommandError( "Filtering access rules by properties is supported only " "with API microversion 2.45 and beyond.") else: access_list = share.access_list() list_of_keys = [ 'ID', 'Access Type', 'Access To', 'Access Level', 'State' ] if share_client.api_version >= api_versions.APIVersion("2.21"): list_of_keys.append('Access Key') if share_client.api_version >= api_versions.APIVersion("2.33"): list_of_keys.append('Created At') list_of_keys.append('Updated At') values = (oscutils.get_item_properties( a, list_of_keys) for a in access_list) return (list_of_keys, values) class ShowShareAccess(command.ShowOne): """Display a share access rule.""" _description = _( "Display a share access rule. " "Available for API microversion 2.45 and higher") def get_parser(self, prog_name): parser = super(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") 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") 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") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_backups.py0000664000175000017500000003334700000000000024476 0ustar00zuulzuul00000000000000# Copyright 2023 Cloudification GmbH. # 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 logging from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils from manilaclient.common._i18n import _ from manilaclient.common import constants from manilaclient.osc import utils LOG = logging.getLogger(__name__) class CreateShareBackup(command.ShowOne): """Create a share backup.""" _description = _("Create a backup of the given share") def get_parser(self, prog_name): parser = super(CreateShareBackup, self).get_parser(prog_name) parser.add_argument( "share", metavar="", help=_("Name or ID of the share to backup.") ) parser.add_argument( '--name', metavar='', default=None, help=_('Optional share backup name. (Default=None).') ) parser.add_argument( '--description', metavar='', default=None, help=_('Optional share backup description. (Default=None).') ) parser.add_argument( "--backup-options", metavar="", default={}, action=parseractions.KeyValueAction, help=_("Backup driver option key=value pairs (Optional, " "Default=None)."), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share = osc_utils.find_resource( share_client.shares, parsed_args.share) body = {} if parsed_args.backup_options: body['backup_options'] = utils.extract_key_value_options( parsed_args.backup_options) if parsed_args.description: body['description'] = parsed_args.description if parsed_args.name: body['name'] = parsed_args.name share_backup = share_client.share_backups.create(share, **body) share_backup._info.pop('links', None) return self.dict2columns(share_backup._info) class DeleteShareBackup(command.Command): """Delete one or more share backups.""" _description = _("Delete one or more share backups") def get_parser(self, prog_name): parser = super(DeleteShareBackup, self).get_parser(prog_name) parser.add_argument( "backup", metavar="", nargs="+", help=_("Name or ID of the backup(s) to delete") ) parser.add_argument( "--wait", action='store_true', default=False, help=_("Wait for share backup deletion") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for backup in parsed_args.backup: try: share_backup_obj = osc_utils.find_resource( share_client.share_backups, backup) share_client.share_backups.delete(share_backup_obj) if parsed_args.wait: if not osc_utils.wait_for_delete( manager=share_client.share_backups, res_id=share_backup_obj.id): result += 1 except Exception as e: result += 1 LOG.error(_( "Failed to delete a share backup with " "name or ID '%(backup)s': %(e)s"), {'backup': backup, 'e': e}) if result > 0: total = len(parsed_args.backup) msg = (_("%(result)s of %(total)s backups failed " "to delete.") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) class ListShareBackup(command.Lister): """List share backups.""" _description = _("List share backups") def get_parser(self, prog_name): parser = super(ListShareBackup, self).get_parser(prog_name) parser.add_argument( "--share", metavar="", default=None, help=_("Name or ID of the share to list backups for.") ) parser.add_argument( "--name", metavar="", default=None, help=_("Filter results by name. Default=None.") ) parser.add_argument( '--description', metavar="", default=None, help=_("Filter results by description. Default=None.") ) parser.add_argument( "--name~", metavar="", default=None, help=_("Filter results matching a share backup name pattern. ") ) parser.add_argument( '--description~', metavar="", default=None, help=_("Filter results matching a share backup description ") ) parser.add_argument( '--status', metavar="", default=None, help=_('Filter results by status. Default=None.') ) parser.add_argument( "--limit", metavar="", type=int, default=None, action=parseractions.NonNegativeAction, help=_("Limit the number of backups returned. Default=None.") ) parser.add_argument( '--offset', metavar="", default=None, help='Start position of backup records listing.') parser.add_argument( '--sort-key', '--sort_key', metavar='', type=str, default=None, help='Key to be sorted, available keys are %(keys)s. ' 'Default=None.' % {'keys': constants.BACKUP_SORT_KEY_VALUES}) parser.add_argument( '--sort-dir', '--sort_dir', metavar='', type=str, default=None, help='Sort direction, available values are %(values)s. ' 'OPTIONAL: Default=None.' % { 'values': constants.SORT_DIR_VALUES}) parser.add_argument( '--detail', dest='detail', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help="Show detailed information about share backups.") return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_id = None if parsed_args.share: share_id = osc_utils.find_resource(share_client.shares, parsed_args.share).id columns = [ 'ID', 'Name', 'Share ID', 'Status' ] if parsed_args.detail: columns.extend(['Description', 'Size', 'Created At', 'Updated At', 'Availability Zone', 'Progress', 'Restore Progress', 'Host', 'Topic']) search_opts = { 'limit': parsed_args.limit, 'offset': parsed_args.offset, 'name': parsed_args.name, 'description': parsed_args.description, 'status': parsed_args.status, 'share_id': share_id, } search_opts['name~'] = getattr(parsed_args, 'name~') search_opts['description~'] = getattr(parsed_args, 'description~') backups = share_client.share_backups.list( detailed=parsed_args.detail, search_opts=search_opts, sort_key=parsed_args.sort_key, sort_dir=parsed_args.sort_dir) return (columns, (osc_utils.get_item_properties(b, columns) for b in backups)) class ShowShareBackup(command.ShowOne): """Show share backup.""" _description = _("Show details of a backup") def get_parser(self, prog_name): parser = super(ShowShareBackup, self).get_parser(prog_name) parser.add_argument( "backup", metavar="", help=_("ID of the share backup. ") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share backup = osc_utils.find_resource(share_client.share_backups, parsed_args.backup) backup._info.pop('links', None) return self.dict2columns(backup._info) class RestoreShareBackup(command.Command): """Restore share backup to share""" _description = _("Attempt to restore share backup") def get_parser(self, prog_name): parser = super(RestoreShareBackup, self).get_parser(prog_name) parser.add_argument( "backup", metavar="", help=_('ID of backup to restore.') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_backup = osc_utils.find_resource( share_client.share_backups, parsed_args.backup) share_client.share_backups.restore(share_backup.id) class SetShareBackup(command.Command): """Set share backup properties.""" _description = _("Set share backup properties") def get_parser(self, prog_name): parser = super(SetShareBackup, self).get_parser(prog_name) parser.add_argument( "backup", metavar="", help=_('Name or ID of the backup to set a property for') ) parser.add_argument( "--name", metavar="", default=None, help=_("Set a name to the backup.") ) parser.add_argument( "--description", metavar="", default=None, help=_("Set a description to the backup.") ) parser.add_argument( "--status", metavar="", choices=['available', 'error', 'creating', 'deleting', 'restoring'], help=_("Assign a status to the backup(Admin only). " "Options include : available, error, creating, " "deleting, restoring.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 share_backup = osc_utils.find_resource( share_client.share_backups, parsed_args.backup) kwargs = {} if parsed_args.name is not None: kwargs['name'] = parsed_args.name if parsed_args.description is not None: kwargs['description'] = parsed_args.description try: share_client.share_backups.update(share_backup, **kwargs) except Exception as e: result += 1 LOG.error(_( "Failed to set share backup properties " "'%(properties)s': %(exception)s"), {'properties': kwargs, 'exception': e}) if parsed_args.status: try: share_client.share_backups.reset_status( share_backup, parsed_args.status ) except Exception as e: result += 1 LOG.error(_( "Failed to update backup status to " "'%(status)s': %(e)s"), {'status': parsed_args.status, 'e': e}) if result > 0: raise exceptions.CommandError(_("One or more of the " "set operations failed")) class UnsetShareBackup(command.Command): """Unset share backup properties.""" _description = _("Unset share backup properties") def get_parser(self, prog_name): parser = super(UnsetShareBackup, self).get_parser(prog_name) parser.add_argument( "backup", metavar="", help=_('Name or ID of the backup to unset a property for') ) parser.add_argument( "--name", action='store_true', help=_("Unset a name to the backup.") ) parser.add_argument( "--description", action='store_true', help=_("Unset a description to the backup.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_backup = osc_utils.find_resource( share_client.share_backups, parsed_args.backup) kwargs = {} if parsed_args.name: kwargs['name'] = None if parsed_args.description: kwargs['description'] = None if not kwargs: msg = "Either name or description must be provided." raise exceptions.CommandError(msg) try: share_client.share_backups.update(share_backup, **kwargs) except Exception as e: LOG.error(_( "Failed to unset share backup properties " "'%(properties)s': %(exception)s"), {'properties': kwargs, 'exception': e}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_group_snapshots.py0000664000175000017500000003475000000000000026303 0ustar00zuulzuul00000000000000# 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 from osc_lib import utils as osc_utils from manilaclient.common._i18n import _ LOG = logging.getLogger(__name__) class CreateShareGroupSnapshot(command.ShowOne): """Create a share group snapshot.""" _description = _( "Create a share group snapshot of the given share group") def get_parser(self, prog_name): parser = super( CreateShareGroupSnapshot, self).get_parser(prog_name) parser.add_argument( "share_group", metavar="", help=_("Name or ID of the share group.") ) parser.add_argument( "--name", metavar="", default=None, help=_("Optional share group snapshot name. (Default=None)") ) parser.add_argument( "--description", metavar="", default=None, help=_("Optional share group snapshot description. " "(Default=None)") ) parser.add_argument( '--wait', action='store_true', default=False, help=_('Wait for share group snapshot creation') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_group = osc_utils.find_resource( share_client.share_groups, parsed_args.share_group) share_group_snapshot = share_client.share_group_snapshots.create( share_group, name=parsed_args.name, description=parsed_args.description, ) if parsed_args.wait: if not osc_utils.wait_for_status( status_f=share_client.share_group_snapshots.get, res_id=share_group_snapshot.id, success_status=['available'] ): LOG.error(_("ERROR: Share group snapshot is in error state.")) share_group_snapshot = osc_utils.find_resource( share_client.share_group_snapshots, share_group_snapshot.id) data = share_group_snapshot._info data.pop('links', None) data.pop('members', None) return self.dict2columns(data) class DeleteShareGroupSnapshot(command.Command): """Delete one or more share group snapshots.""" _description = _("Delete one or more share group snapshot") def get_parser(self, prog_name): parser = super(DeleteShareGroupSnapshot, self).get_parser(prog_name) parser.add_argument( "share_group_snapshot", metavar="", nargs="+", help=_("Name or ID of the group snapshot(s) to delete") ) parser.add_argument( "--force", action='store_true', default=False, help=_("Attempt to force delete the share group snapshot(s) " "(Default=False) (Admin only).") ) parser.add_argument( "--wait", action='store_true', default=False, help=_("Wait for share group snapshot deletion") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for share_group_snapshot in parsed_args.share_group_snapshot: try: share_group_snapshot_obj = osc_utils.find_resource( share_client.share_group_snapshots, share_group_snapshot) share_client.share_group_snapshots.delete( share_group_snapshot_obj, force=parsed_args.force) if parsed_args.wait: if not osc_utils.wait_for_delete( manager=share_client.share_group_snapshots, res_id=share_group_snapshot_obj.id): result += 1 except Exception as e: result += 1 LOG.error( 'Failed to delete a share group snapshot with ' f'name or ID {share_group_snapshot}: {e}') if result > 0: total = len(parsed_args.share_group_snapshot) msg = (f'{result} of {total} share group snapshots failed ' 'to delete.') raise exceptions.CommandError(msg) class ShowShareGroupSnapshot(command.ShowOne): """Display a share group snapshot""" _description = _( "Show details about a share group snapshot") def get_parser(self, prog_name): parser = super(ShowShareGroupSnapshot, self).get_parser(prog_name) parser.add_argument( "share_group_snapshot", metavar="", help=_("Name or ID of the share group snapshot to display") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_group_snapshot = osc_utils.find_resource( share_client.share_group_snapshots, parsed_args.share_group_snapshot) data = share_group_snapshot._info data.pop('links', None) data.pop('members', None) return self.dict2columns(data) class SetShareGroupSnapshot(command.Command): """Set share group snapshot properties.""" _description = _("Set share group snapshot properties") def get_parser(self, prog_name): parser = super(SetShareGroupSnapshot, self).get_parser(prog_name) parser.add_argument( "share_group_snapshot", metavar="", help=_('Name or ID of the snapshot to set a property for') ) parser.add_argument( "--name", metavar="", default=None, help=_("Set a name to the snapshot.") ) parser.add_argument( "--description", metavar="", default=None, help=_("Set a description to the snapshot.") ) parser.add_argument( "--status", metavar="", choices=['available', 'error', 'creating', 'deleting', 'error_deleting'], help=_("Explicitly set the state of a share group snapshot" "(Admin only). " "Options include : available, error, creating, " "deleting, error_deleting.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 share_group_snapshot = osc_utils.find_resource( share_client.share_group_snapshots, parsed_args.share_group_snapshot) kwargs = {} if parsed_args.name is not None: kwargs['name'] = parsed_args.name if parsed_args.description is not None: kwargs['description'] = parsed_args.description if kwargs: try: share_client.share_group_snapshots.update( share_group_snapshot, **kwargs ) except Exception as e: result += 1 LOG.error('Failed to set name or desciption for ' 'share group snapshot with ID ' f'{share_group_snapshot.id}: {e}') if parsed_args.status: try: share_client.share_group_snapshots.reset_state( share_group_snapshot, parsed_args.status ) except Exception as e: result += 1 LOG.error('Failed to set status for ' 'share group snapshot with ID ' f'{share_group_snapshot.id}: {e}') if result > 0: raise exceptions.CommandError(_( "One or more of the set operations failed")) class UnsetShareGroupSnapshot(command.Command): """Unset a share group snapshot property.""" _description = _("Unset a share group snapshot property") def get_parser(self, prog_name): parser = super(UnsetShareGroupSnapshot, self).get_parser(prog_name) parser.add_argument( "share_group_snapshot", metavar="", help=_("Name or ID of the group snapshot to unset a property of") ) parser.add_argument( "--name", action='store_true', help=_("Unset share group snapshot name."), ) parser.add_argument( "--description", action='store_true', help=_("Unset share group snapshot description."), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_group_snapshot = osc_utils.find_resource( share_client.share_group_snapshots, parsed_args.share_group_snapshot) kwargs = {} if parsed_args.name: # the SDK unsets name if it is an empty string kwargs['name'] = '' if parsed_args.description: # the SDK unsets description if it is an empty string kwargs['description'] = '' if kwargs: try: share_client.share_group_snapshots.update( share_group_snapshot, **kwargs ) except Exception as e: raise exceptions.CommandError( 'Failed to unset name or description for ' f'share group snapshot : {e}') class ListShareGroupSnapshot(command.Lister): """List share group snapshots.""" _description = _("List share group snapshots") def get_parser(self, prog_name): parser = super(ListShareGroupSnapshot, self).get_parser(prog_name) parser.add_argument( "--all-projects", action='store_true', default=False, help=_("Display information from all projects (Admin only).") ) parser.add_argument( "--name", metavar="", default=None, help=_("Filter results by name.") ) parser.add_argument( "--status", metavar="", default=None, help=_("Filter results by status.") ) parser.add_argument( "--share-group", metavar="", default=None, help=_("Filter results by share group name or ID.") ) parser.add_argument( "--limit", metavar="", type=int, default=None, action=parseractions.NonNegativeAction, help=_("Limit the number of share groups returned") ) parser.add_argument( "--marker", metavar="", help=_("The last share group snapshot ID of the " "previous page") ) 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( "--detailed", action="store_true", help=_("Show detailed information about share group snapshot. ") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_group_id = None if parsed_args.share_group: share_group_id = osc_utils.find_resource( share_client.share_groups, parsed_args.share_group).id columns = [ 'ID', 'Name', 'Status', 'Description', ] search_opts = { 'all_tenants': parsed_args.all_projects, 'name': parsed_args.name, 'status': parsed_args.status, 'share_group_id': share_group_id, 'limit': parsed_args.limit, 'offset': parsed_args.marker, } if parsed_args.detailed: columns.extend([ 'Created At', 'Share Group ID', ]) if parsed_args.all_projects: columns.append('Project ID') share_group_snapshots = share_client.share_group_snapshots.list( search_opts=search_opts) share_group_snapshots = utils.sort_items( share_group_snapshots, parsed_args.sort, str) data = ( osc_utils.get_dict_properties(share_group_snapshot._info, columns) for share_group_snapshot in share_group_snapshots ) return (columns, data) class ListShareGroupSnapshotMembers(command.Lister): """List members for share group snapshot.""" _description = _("List members of share group snapshot") def get_parser(self, prog_name): parser = super( ListShareGroupSnapshotMembers, self).get_parser(prog_name) parser.add_argument( "share_group_snapshot", metavar="", help=_("Name or ID of the group snapshot to list members for") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share columns = ['Share ID', 'Size'] share_group_snapshot = osc_utils.find_resource( share_client.share_group_snapshots, parsed_args.share_group_snapshot) data = ( osc_utils.get_dict_properties(member, columns) for member in share_group_snapshot._info.get('members', []) ) return (columns, data) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_group_type_access.py0000664000175000017500000001425300000000000026557 0ustar00zuulzuul00000000000000# 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.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 ShareGroupTypeAccessAllow(command.Command): """Allow a project to access a share group type.""" _description = _("Allow a project to access a share group type " "(Admin only).") def get_parser(self, prog_name): parser = super(ShareGroupTypeAccessAllow, self).get_parser(prog_name) parser.add_argument( 'share_group_type', metavar="", help=_("Share group type name or ID to allow access to.") ) parser.add_argument( 'projects', metavar="", nargs="+", help=_("Project Name or ID to add share group type access for.") ) identity_common.add_project_domain_option_to_parser(parser) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share identity_client = self.app.client_manager.identity result = 0 share_group_type = apiutils.find_resource( share_client.share_group_types, parsed_args.share_group_type) for project in parsed_args.projects: try: project_obj = identity_common.find_project( identity_client, project, parsed_args.project_domain) share_client.share_group_type_access.add_project_access( share_group_type, project_obj.id) except Exception as e: result += 1 LOG.error(_( "Failed to allow access for project '%(project)s' " "to share group type with name or ID " "'%(share_group_type)s': %(e)s"), {'project': project, 'share_group_type': share_group_type, 'e': e}) if result > 0: total = len(parsed_args.projects) msg = (_("Failed to allow access to " "%(result)s of %(total)s projects") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) class ListShareGroupTypeAccess(command.Lister): """Get access list for share group type.""" _description = _("Get access list for share group type (Admin only).") def get_parser(self, prog_name): parser = super(ListShareGroupTypeAccess, self).get_parser(prog_name) parser.add_argument( 'share_group_type', metavar="", help=_("Filter results by share group type name or ID.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_group_type = apiutils.find_resource( share_client.share_group_types, parsed_args.share_group_type) if share_group_type._info.get('is_public'): raise exceptions.CommandError( 'Forbidden to get access list for public share group type.') data = share_client.share_group_type_access.list(share_group_type) columns = ['Project ID'] values = (oscutils.get_item_properties(s, columns) for s in data) return (columns, values) class ShareGroupTypeAccessDeny(command.Command): """Deny a project to access a share group type.""" _description = _("Deny a project to access a share group type " "(Admin only).") def get_parser(self, prog_name): parser = super(ShareGroupTypeAccessDeny, self).get_parser(prog_name) parser.add_argument( 'share_group_type', metavar="", help=_("Share group type name or ID to deny access from") ) parser.add_argument( 'projects', metavar="", nargs="+", help=_("Project Name(s) or ID(s) " "to deny share group type access for.") ) identity_common.add_project_domain_option_to_parser(parser) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share identity_client = self.app.client_manager.identity result = 0 share_group_type = apiutils.find_resource( share_client.share_group_types, parsed_args.share_group_type) for project in parsed_args.projects: try: project_obj = identity_common.find_project( identity_client, project, parsed_args.project_domain) share_client.share_group_type_access.remove_project_access( share_group_type, project_obj.id) except Exception as e: result += 1 LOG.error(_( "Failed to deny access for project '%(project)s' " "to share group type with name or ID " "'%(share_group_type)s': %(e)s"), {'project': project, 'share_group_type': share_group_type, 'e': e}) if result > 0: total = len(parsed_args.projects) msg = (_("Failed to deny access to " "%(result)s of %(total)s projects") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_group_types.py0000664000175000017500000002670100000000000025422 0ustar00zuulzuul00000000000000# 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 oslo_utils import strutils from manilaclient.common._i18n import _ from manilaclient.common.apiclient import utils as apiutils from manilaclient.osc import utils LOG = logging.getLogger(__name__) ATTRIBUTES = [ 'id', 'name', 'share_types', 'visibility', 'is_default', 'group_specs' ] class CreateShareGroupType(command.ShowOne): """Create new share group type.""" _description = _( "Create new share group type") log = logging.getLogger(__name__ + ".CreateShareGroupType") def get_parser(self, prog_name): parser = super(CreateShareGroupType, self).get_parser(prog_name) parser.add_argument( 'name', metavar="", default=None, help=_('Share group type name') ) parser.add_argument( "share_types", metavar="", nargs="+", default=None, help=_("List of share type names or IDs. Example:" " my-share-type-1 my-share-type-2"), ) parser.add_argument( "--group-specs", type=str, nargs='*', metavar='', default=None, help=_("Share Group type extra specs by key and value." " OPTIONAL: Default=None. Example:" " --group-specs consistent_snapshot_support=host."), ) 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 } share_types_list = [] for share_type in parsed_args.share_types: try: share_type_obj = apiutils.find_resource( share_client.share_types, share_type) share_types_list.append(share_type_obj.name) except Exception as e: msg = LOG.error(_("Failed to find the share type with " "name or ID '%(share_type)s': %(e)s"), {'share_type': share_type, 'e': e}) raise exceptions.CommandError(msg) kwargs['share_types'] = share_types_list if parsed_args.public: kwargs['is_public'] = strutils.bool_from_string( parsed_args.public, default=True) group_specs = {} if parsed_args.group_specs: for item in parsed_args.group_specs: group_specs = utils.extract_group_specs(group_specs, [item]) kwargs['group_specs'] = group_specs share_group_type = share_client.share_group_types.create(**kwargs) formatter = parsed_args.formatter formatted_group_type = utils.format_share_group_type( share_group_type, formatter) return (ATTRIBUTES, oscutils.get_dict_properties( formatted_group_type, ATTRIBUTES)) class DeleteShareGroupType(command.Command): """Delete a share group type.""" _description = _("Delete a share group type") log = logging.getLogger(__name__ + ".DeleteShareGroupType") def get_parser(self, prog_name): parser = super(DeleteShareGroupType, self).get_parser(prog_name) parser.add_argument( 'share_group_types', metavar="", nargs="+", help=_("Name or ID of the share group type(s) to delete") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for share_group_type in parsed_args.share_group_types: try: share_group_type_obj = apiutils.find_resource( share_client.share_group_types, share_group_type) share_client.share_group_types.delete(share_group_type_obj) except Exception as e: result += 1 LOG.error(_( "Failed to delete share group type with " "name or ID '%(share_group_type)s': %(e)s"), {'share_group_type': share_group_type, 'e': e}) if result > 0: total = len(parsed_args.share_group_types) msg = (_("%(result)s of %(total)s share group types failed " "to delete.") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) class ListShareGroupType(command.Lister): """List Share Group Types.""" _description = _("List share types") log = logging.getLogger(__name__ + ".ListShareGroupType") def get_parser(self, prog_name): parser = super(ListShareGroupType, self).get_parser(prog_name) parser.add_argument( '--all', action='store_true', default=False, help=_('Display all share group types whether public or private. ' 'Default=False. (Admin only)'), ) parser.add_argument( '--group-specs', type=str, nargs='*', metavar='', default=None, help=_('Filter share group types with group specs (key=value).'), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share search_opts = {} if parsed_args.group_specs: search_opts = { 'group_specs': utils.extract_group_specs( extra_specs={}, specs_to_add=parsed_args.group_specs) } formatter = parsed_args.formatter share_group_types = share_client.share_group_types.list( search_opts=search_opts, show_all=parsed_args.all) formatted_types = [] for share_group_type in share_group_types: formatted_types.append(utils.format_share_group_type( share_group_type, formatter)) column_headers = utils.format_column_headers(ATTRIBUTES) values = (oscutils.get_dict_properties( sgt, ATTRIBUTES) for sgt in formatted_types) return (column_headers, values) class ShowShareGroupType(command.ShowOne): """Show Share Group Types.""" _description = _("Show share group types") log = logging.getLogger(__name__ + ".ShowShareGroupType") def get_parser(self, prog_name): parser = super(ShowShareGroupType, self).get_parser(prog_name) parser.add_argument( 'share_group_type', metavar="", help=_("Name or ID of the share group type to show") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_group_type = apiutils.find_resource( share_client.share_group_types, parsed_args.share_group_type) share_group_type_obj = share_client.share_group_types.get( share_group_type) formatter = parsed_args.formatter formatted_group_type = utils.format_share_group_type( share_group_type_obj, formatter) return (ATTRIBUTES, oscutils.get_dict_properties( formatted_group_type, ATTRIBUTES)) class SetShareGroupType(command.Command): """Set share type properties.""" _description = _("Set share group type properties") log = logging.getLogger(__name__ + ".SetShareGroupType") def get_parser(self, prog_name): parser = super(SetShareGroupType, self).get_parser(prog_name) parser.add_argument( 'share_group_type', metavar="", help=_("Name or ID of the share group type to modify") ) parser.add_argument( "--group-specs", type=str, nargs='*', metavar='', default=None, help=_("Extra specs key and value of share group type that will be" " used for share type creation. OPTIONAL: Default=None." " Example: --group-specs consistent-snapshot-support=True"), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share try: share_group_type_obj = apiutils.find_resource( share_client.share_group_types, parsed_args.share_group_type) except Exception as e: msg = LOG.error(_( "Failed to find the share group type with " "name or ID '%(share_group_type)s': %(e)s"), {'share_group_type': parsed_args.share_group_type, 'e': e}) raise exceptions.CommandError(msg) kwargs = {} if kwargs: share_group_type_obj.set_keys(**kwargs) if parsed_args.group_specs: group_specs = utils.extract_group_specs( extra_specs={}, specs_to_add=parsed_args.group_specs) try: share_group_type_obj.set_keys(group_specs) except Exception as e: raise exceptions.CommandError( "Failed to set share group type key: %s" % e) class UnsetShareGroupType(command.Command): """Unset share group type extra specs.""" _description = _("Unset share group type extra specs") log = logging.getLogger(__name__ + ".UnsetShareGroupType") def get_parser(self, prog_name): parser = super(UnsetShareGroupType, self).get_parser(prog_name) parser.add_argument( 'share_group_type', metavar="", help=_("Name or ID of the share grouptype to modify") ) parser.add_argument( 'group_specs', metavar='', nargs='+', help=_('Remove group specs from this share group type'), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share try: share_group_type_obj = apiutils.find_resource( share_client.share_group_types, parsed_args.share_group_type) except Exception as e: msg = LOG.error(_( "Failed to find the share group type with " "name or ID '%(share_group_type)s': %(e)s"), {'share_group_type': parsed_args.share_group_type, 'e': e}) raise exceptions.CommandError(msg) if parsed_args.group_specs: try: share_group_type_obj.unset_keys(parsed_args.group_specs) except Exception as e: raise exceptions.CommandError( "Failed to remove share type group extra spec: %s" % e) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_groups.py0000664000175000017500000004445200000000000024364 0ustar00zuulzuul00000000000000# 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 parseractions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils from manilaclient import api_versions from manilaclient.common._i18n import _ LOG = logging.getLogger(__name__) class CreateShareGroup(command.ShowOne): """Create new share group.""" _description = _( "Create new share group") def get_parser(self, prog_name): parser = super(CreateShareGroup, self).get_parser(prog_name) parser.add_argument( '--name', metavar="", default=None, help=_('Share group name') ) parser.add_argument( "--description", metavar="", default=None, help=_("Share group description."), ) parser.add_argument( "--share-types", metavar="", nargs="+", default=[], help=_("Name or ID of share type(s)."), ) parser.add_argument( "--share-group-type", metavar="", default=None, help=_("Share group type name or ID of the share " "group to be created."), ) parser.add_argument( "--share-network", metavar="", default=False, help=_("Specify share network name or id"), ) parser.add_argument( "--source-share-group-snapshot", metavar="", default=False, help=_("Share group snapshot name or ID to create " "the share group from."), ) parser.add_argument( "--availability-zone", metavar='', default=None, help=_("Optional availability zone in which group " "should be created"), ) parser.add_argument( "--wait", action='store_true', default=False, help=_('Wait for share group creation') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_types = [] for share_type in parsed_args.share_types: share_types.append( osc_utils.find_resource( share_client.share_types, share_type, ) ) share_group_type = None if parsed_args.share_group_type: share_group_type = osc_utils.find_resource( share_client.share_group_types, parsed_args.share_group_type).id share_network = None if parsed_args.share_network: share_network = osc_utils.find_resource( share_client.share_networks, parsed_args.share_network).id source_share_group_snapshot = None if parsed_args.source_share_group_snapshot: source_share_group_snapshot = osc_utils.find_resource( share_client.share_group_snapshots, parsed_args.source_share_group_snapshot).id body = { 'name': parsed_args.name, 'description': parsed_args.description, 'share_types': share_types, 'share_group_type': share_group_type, 'share_network': share_network, 'source_share_group_snapshot': source_share_group_snapshot, 'availability_zone': parsed_args.availability_zone } share_group = share_client.share_groups.create(**body) if parsed_args.wait: if not osc_utils.wait_for_status( status_f=share_client.share_groups.get, res_id=share_group.id, success_status=['available'] ): LOG.error(_("ERROR: Share group is in error state.")) share_group = osc_utils.find_resource( share_client.share_groups, share_group.id) printable_share_group = share_group._info printable_share_group.pop('links', None) if printable_share_group.get('share_types'): if parsed_args.formatter == 'table': printable_share_group['share_types'] = ( "\n".join(printable_share_group['share_types']) ) return self.dict2columns(printable_share_group) class DeleteShareGroup(command.Command): """Delete one or more share groups.""" _description = _("Delete one or more share groups") def get_parser(self, prog_name): parser = super(DeleteShareGroup, self).get_parser(prog_name) parser.add_argument( "share_group", metavar="", nargs="+", help=_("Name or ID of the share group(s) to delete") ) parser.add_argument( "--force", action='store_true', default=False, help=_("Attempt to force delete the share group (Default=False) " "(Admin only).") ) parser.add_argument( "--wait", action='store_true', default=False, help=_("Wait for share group to delete") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for share_group in parsed_args.share_group: try: share_group_obj = osc_utils.find_resource( share_client.share_groups, share_group) share_client.share_groups.delete( share_group_obj, force=parsed_args.force) if parsed_args.wait: if not osc_utils.wait_for_delete( manager=share_client.share_groups, res_id=share_group_obj.id): result += 1 except Exception as e: result += 1 LOG.error(_( "Failed to delete share group with " "name or ID '%(share_group)s': %(e)s"), {'share_group': share_group, 'e': e}) if result > 0: total = len(parsed_args.share_group) msg = (_("%(result)s of %(total)s share groups failed " "to delete.") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) class ListShareGroup(command.Lister): """List share groups.""" _description = _("List share groups") def get_parser(self, prog_name): parser = super(ListShareGroup, self).get_parser(prog_name) parser.add_argument( "--all-projects", action='store_true', default=False, help=_("Display share groups from all projects (Admin only).") ) parser.add_argument( "--name", metavar="", default=None, help=_("Filter results by name.") ) parser.add_argument( "--description", metavar="", default=None, help=_("Filter results by description. Available " "only for microversion >= 2.36.") ) parser.add_argument( "--status", metavar="", default=None, help=_("Filter results by status.") ) parser.add_argument( "--share-server", metavar="", default=None, help=_("Filter results by share server ID.") ) parser.add_argument( "--share-group-type", metavar="", default=None, help=_("Filter results by a share group type ID " "or name that was used for share group " "creation. ") ) parser.add_argument( "--snapshot", metavar="", default=None, help=_("Filter results by share group snapshot " "name or ID that was used to create the " "share group. ") ) parser.add_argument( "--host", metavar="", default=None, help=_("Filter results by host.") ) parser.add_argument( "--share-network", metavar="", default=None, help=_("Filter results by share-network name or " "ID. ") ) parser.add_argument( "--project", metavar="", default=None, help=_("Filter results by project name or ID. Useful with " "set key '--all-projects'. ") ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( "--limit", metavar="", type=int, default=None, action=parseractions.NonNegativeAction, help=_("Limit the number of share groups returned") ) parser.add_argument( "--marker", metavar="", help=_("The last share group ID of the previous page") ) 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( "--name~", metavar="", default=None, help=_("Filter results matching a share group " "name pattern. Available only for " "microversion >= 2.36. ") ) parser.add_argument( "--description~", metavar="", default=None, help=_("Filter results matching a share group " "description pattern. Available only for " "microversion >= 2.36. ") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share identity_client = self.app.client_manager.identity share_server_id = None if parsed_args.share_server: share_server_id = osc_utils.find_resource( share_client.share_servers, parsed_args.share_server).id share_group_type = None if parsed_args.share_group_type: share_group_type = osc_utils.find_resource( share_client.share_group_types, parsed_args.share_group_type).id snapshot = None if parsed_args.snapshot: snapshot = apiutils.find_resource( share_client.share_snapshots, parsed_args.snapshot).id share_network = None if parsed_args.share_network: share_network = osc_utils.find_resource( share_client.share_networks, parsed_args.share_network).id project_id = None if parsed_args.project: project_id = identity_common.find_project( identity_client, parsed_args.project, parsed_args.project_domain).id columns = [ 'ID', 'Name', 'Status', 'Description', ] search_opts = { 'all_tenants': parsed_args.all_projects, 'name': parsed_args.name, 'status': parsed_args.status, 'share_server_id': share_server_id, 'share_group_type': share_group_type, 'snapshot': snapshot, 'host': parsed_args.host, 'share_network': share_network, 'project_id': project_id, 'limit': parsed_args.limit, 'offset': parsed_args.marker, } if share_client.api_version >= api_versions.APIVersion("2.36"): search_opts['name~'] = getattr(parsed_args, 'name~') search_opts['description~'] = getattr(parsed_args, 'description~') search_opts['description'] = parsed_args.description elif (parsed_args.description or getattr(parsed_args, 'name~') or getattr(parsed_args, 'description~')): raise exceptions.CommandError( "Pattern based filtering (name~, description~ and description)" " is only available with manila API version >= 2.36") if parsed_args.all_projects: columns.append('Project ID') share_groups = share_client.share_groups.list(search_opts=search_opts) data = (osc_utils.get_dict_properties( share_group._info, columns) for share_group in share_groups) return (columns, data) class ShowShareGroup(command.ShowOne): """Show share group.""" _description = _("Show details about a share group") def get_parser(self, prog_name): parser = super(ShowShareGroup, self).get_parser(prog_name) parser.add_argument( "share_group", metavar="", help=_("Name or ID of the share group.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_group = osc_utils.find_resource( share_client.share_groups, parsed_args.share_group) printable_share_group = share_group._info printable_share_group.pop('links', None) if printable_share_group.get('share_types'): if parsed_args.formatter == 'table': printable_share_group['share_types'] = "\n".join( printable_share_group['share_types']) return self.dict2columns(printable_share_group) class SetShareGroup(command.Command): """Set share group.""" _description = _("Explicitly set share group status") def get_parser(self, prog_name): parser = super(SetShareGroup, self).get_parser(prog_name) parser.add_argument( 'share_group', metavar="", help=_('Name or ID of the share group to update.') ) parser.add_argument( '--name', metavar="", default=None, help=_('New name for the share group. (Default=None)') ) parser.add_argument( '--description', metavar='', default=None, help=_('Share group description. (Default=None)') ) parser.add_argument( '--status', metavar='', default=None, help=_('Explicitly update the status of a share group (Admin ' 'only). Examples include: available, error, creating, ' 'deleting, error_deleting.') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 share_group = osc_utils.find_resource( share_client.share_groups, parsed_args.share_group) kwargs = {} if parsed_args.name is not None: kwargs['name'] = parsed_args.name if parsed_args.description is not None: kwargs['description'] = parsed_args.description if kwargs: try: share_client.share_groups.update( share_group.id, **kwargs ) except Exception as e: LOG.error(_("Failed to update share group name " "or description: %s"), e) result += 1 if parsed_args.status: try: share_group.reset_state(parsed_args.status) except Exception as e: LOG.error(_( "Failed to set status for the share group: %s"), e) result += 1 if result > 0: raise exceptions.CommandError(_("One or more of the " "set operations failed")) class UnsetShareGroup(command.Command): """Unset a share group property.""" _description = _("Unset a share group property") def get_parser(self, prog_name): parser = super(UnsetShareGroup, self).get_parser(prog_name) parser.add_argument( 'share_group', metavar="", help=_("Name or ID of the share group " "to set a property for.") ) parser.add_argument( "--name", action='store_true', help=_("Unset share group name."), ) parser.add_argument( "--description", action='store_true', help=_("Unset share group description."), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_group = osc_utils.find_resource( share_client.share_groups, parsed_args.share_group) kwargs = {} if parsed_args.name: kwargs['name'] = None if parsed_args.description: kwargs['description'] = None if kwargs: try: share_client.share_groups.update( share_group, **kwargs ) except Exception as e: raise exceptions.CommandError(_( "Failed to unset share_group name " "or description : %s" % e)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_instance_export_locations.py0000664000175000017500000000602100000000000030313 0ustar00zuulzuul00000000000000# Copyright 2021 NetApp, 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 osc_lib.command import command from osc_lib import utils as osc_utils from manilaclient.common._i18n import _ class ShareInstanceListExportLocation(command.Lister): """List share instance export locations.""" _description = _("List share instance export locations") def get_parser(self, prog_name): parser = super( ShareInstanceListExportLocation, self).get_parser(prog_name) parser.add_argument( "instance", metavar="", help=_("ID of the share instance.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share instance = osc_utils.find_resource( share_client.share_instances, parsed_args.instance) export_locations = share_client.share_instance_export_locations.list( instance, search_opts=None) columns = [ 'ID', 'Path', 'Is Admin Only', 'Preferred', ] data = ( osc_utils.get_dict_properties(export_location._info, columns) for export_location in export_locations ) return (columns, data) class ShareInstanceShowExportLocation(command.ShowOne): """Display the export location for a share instance.""" _description = _( "Show export location for a share instance.") def get_parser(self, prog_name): parser = super( ShareInstanceShowExportLocation, self).get_parser(prog_name) parser.add_argument( "instance", metavar="", help=_("Name or ID of the share instance") ) parser.add_argument( "export_location", metavar="", help=_("ID of the share instance export location.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_instance = osc_utils.find_resource( share_client.share_instances, parsed_args.instance) share_instance_export_locations = ( share_client.share_instance_export_locations.get( share_instance.id, parsed_args.export_location) ) data = share_instance_export_locations._info data.pop('links', None) return self.dict2columns(data) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_instances.py0000664000175000017500000001673500000000000025037 0ustar00zuulzuul00000000000000# 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 osc_utils from manilaclient import api_versions from manilaclient.common._i18n import _ from manilaclient.common.apiclient import utils as apiutils from manilaclient.common import cliutils LOG = logging.getLogger(__name__) class ShareInstanceDelete(command.Command): """Forces the deletion of the share instance.""" _description = _("Forces the deletion of a share instance") def get_parser(self, prog_name): parser = super(ShareInstanceDelete, self).get_parser(prog_name) parser.add_argument( 'instance', metavar="", nargs="+", help=_('ID of the share instance to delete.') ) parser.add_argument( "--wait", action='store_true', default=False, help=_("Wait for share instance deletion.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share number_of_deletion_failures = 0 for instance in parsed_args.instance: try: share_instance = apiutils.find_resource( share_client.share_instances, instance) share_client.share_instances.force_delete(share_instance) if parsed_args.wait: if not osc_utils.wait_for_delete( manager=share_client.share_instances, res_id=share_instance.id): number_of_deletion_failures += 1 except Exception as e: number_of_deletion_failures += 1 LOG.error(_( "Failed to delete a share instance with " "ID '%(instance)s': %(e)s"), {'instance': instance, 'e': e}) if number_of_deletion_failures > 0: msg = (_("%(number_of_deletion_failures)s of " "%(total_of_instances)s instances failed " "to delete.") % { 'number_of_deletion_failures': number_of_deletion_failures, 'total_of_instances': len(parsed_args.instance)}) raise exceptions.CommandError(msg) class ShareInstanceList(command.Lister): """List share instances.""" _description = _("List share instances") def get_parser(self, prog_name): parser = super(ShareInstanceList, self).get_parser(prog_name) parser.add_argument( "--share", metavar="", default=None, help=_("Name or ID of the share to list instances for.") ) parser.add_argument( "--export-location", metavar="", default=None, help=_("Export location to list instances for.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share instances = [] kwargs = {} if parsed_args.share: # Check if the share exists share = osc_utils.find_resource( share_client.shares, parsed_args.share) instances = share_client.shares.list_instances(share) else: if share_client.api_version < api_versions.APIVersion("2.35"): if parsed_args.export_location: raise exceptions.CommandError( "Filtering by export location is only " "available with manila API version >= 2.35") else: if parsed_args.export_location: kwargs['export_location'] = parsed_args.export_location instances = share_client.share_instances.list(**kwargs) columns = [ 'ID', 'Share ID', 'Host', 'Status', 'Availability Zone', 'Share Network ID', 'Share Server ID', 'Share Type ID', ] data = (osc_utils.get_dict_properties( instance._info, columns) for instance in instances) return (columns, data) class ShareInstanceSet(command.Command): """Set share instance""" _description = _("Explicitly reset share instance status") def get_parser(self, prog_name): parser = super(ShareInstanceSet, self).get_parser(prog_name) parser.add_argument( "instance", metavar="", help=_("Instance to be modified.") ) parser.add_argument( "--status", metavar="", help=_('Indicate which state to assign the instance. Options are: ' 'available, error, creating, deleting,' 'error_deleting, migrating, migrating_to, server_migrating.' ) ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share instance = osc_utils.find_resource( share_client.share_instances, parsed_args.instance) if parsed_args.status: try: share_client.share_instances.reset_state( instance, parsed_args.status ) except Exception as e: LOG.error(_( "Failed to set status '%(status)s': %(exception)s"), {'status': parsed_args.status, 'exception': e}) raise exceptions.CommandError(_("Set operation failed")) if not instance or not parsed_args.status: raise exceptions.CommandError(_( "Nothing to set. Please define a '--status'.")) class ShareInstanceShow(command.ShowOne): """Show share instance.""" _description = _("Show share instance") def get_parser(self, prog_name): parser = super(ShareInstanceShow, self).get_parser(prog_name) parser.add_argument( "instance", metavar="", help=_("ID of the share instance.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share instance = osc_utils.find_resource( share_client.share_instances, parsed_args.instance) export_locations = share_client.share_instance_export_locations.list( instance) instance._info['export_locations'] = [] for export_location in export_locations: export_location._info.pop('links', None) instance._info['export_locations'].append(export_location._info) if parsed_args.formatter == 'table': instance._info['export_locations'] = ( cliutils.convert_dict_list_to_string( instance._info['export_locations'])) instance._info.pop('links', None) return self.dict2columns(instance._info) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_limits.py0000664000175000017500000000423200000000000024336 0ustar00zuulzuul00000000000000# Copyright 2021 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. from osc_lib.command import command from osc_lib import utils as oscutils from manilaclient.common._i18n import _ class ShareLimitsShow(command.Lister): """Show a list of share limits for a user.""" _description = _("Show a list of share limits for a user.") def get_parser(self, prog_name): parser = super(ShareLimitsShow, self).get_parser(prog_name) limit_type_group = parser.add_mutually_exclusive_group(required=True) limit_type_group.add_argument( '--absolute', action='store_true', default=False, help=_('Get the absolute limits for the user') ) limit_type_group.add_argument( '--rate', action='store_true', default=False, help=_('Get the API rate limits for the user') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share # limit_type = 'absolute' if parsed_args.rate: # limit_type = 'rate' columns = [ "Verb", "Regex", "URI", "Value", "Remaining", "Unit", "Next Available", ] data = list(share_client.limits.get().rate) else: columns = [ 'Name', 'Value', ] data = list(share_client.limits.get().absolute) return (columns, (oscutils.get_item_properties(s, columns) for s in data)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_network_subnets.py0000664000175000017500000003137000000000000026274 0ustar00zuulzuul00000000000000# 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 operator import xor 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 import api_versions from manilaclient.common._i18n import _ LOG = logging.getLogger(__name__) class CreateShareNetworkSubnet(command.ShowOne): """Create a share network subnet.""" _description = _("Create a share network subnet") def get_parser(self, prog_name): parser = super(CreateShareNetworkSubnet, self).get_parser(prog_name) parser.add_argument( "share_network", metavar="", help=_("Share network name or ID.") ) parser.add_argument( "--neutron-net-id", metavar="", default=None, help=_("Neutron network ID. Used to set up network for share " "servers (optional). Should be defined together with " "'--neutron-subnet-id'.") ) parser.add_argument( "--neutron-subnet-id", metavar="", default=None, help=_("Neutron subnet ID. Used to set up network for share " "servers (optional). Should be defined together with " "'--neutron-net-id' to which this subnet belongs to. ") ) parser.add_argument( "--availability-zone", metavar="", default=None, 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.") ) parser.add_argument( '--check-only', default=False, action='store_true', help=_("Run a dry-run of a share network subnet create. " "Available only for microversion >= 2.70.") ) parser.add_argument( '--restart-check', default=False, action='store_true', help=_("Restart a dry-run of a share network subnet create. " "Helpful when check results are stale. " "Available only for microversion >= 2.70.") ) parser.add_argument( "--property", metavar="", default={}, action=parseractions.KeyValueAction, help=_("Set a property to this share network subnet " "(repeat option to set multiple properties). " "Available only for microversion >= 2.78."), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share # check and restart check during create is only available from 2.70. if (parsed_args.check_only and share_client.api_version < api_versions.APIVersion("2.70")): raise exceptions.CommandError( "Check only can be specified only with manila API " "version >= 2.70.") if (parsed_args.restart_check and share_client.api_version < api_versions.APIVersion("2.70")): raise exceptions.CommandError( "Restart check can be specified only with manila API " "version >= 2.70.") if (parsed_args.property and share_client.api_version < api_versions.APIVersion("2.78")): raise exceptions.CommandError( "Property can be specified only with manila API " "version >= 2.78.") if xor(bool(parsed_args.neutron_net_id), bool(parsed_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_id = oscutils.find_resource( share_client.share_networks, parsed_args.share_network).id if parsed_args.check_only or parsed_args.restart_check: if parsed_args.property: raise exceptions.CommandError( "Property cannot be specified with check operation.") subnet_create_check = ( share_client.share_networks.share_network_subnet_create_check( neutron_net_id=parsed_args.neutron_net_id, neutron_subnet_id=parsed_args.neutron_subnet_id, availability_zone=parsed_args.availability_zone, reset_operation=parsed_args.restart_check, share_network_id=share_network_id) ) subnet_data = subnet_create_check[1] else: share_network_subnet = share_client.share_network_subnets.create( neutron_net_id=parsed_args.neutron_net_id, neutron_subnet_id=parsed_args.neutron_subnet_id, availability_zone=parsed_args.availability_zone, share_network_id=share_network_id, metadata=parsed_args.property ) subnet_data = share_network_subnet._info return self.dict2columns(subnet_data) class DeleteShareNetworkSubnet(command.Command): """Delete a share network subnet.""" _description = _("Delete a share network subnet") def get_parser(self, prog_name): parser = super(DeleteShareNetworkSubnet, self).get_parser(prog_name) parser.add_argument( "share_network", metavar="", help=_("Share network name or ID.") ) parser.add_argument( "share_network_subnet", metavar="", nargs="+", help=_("ID(s) of share network subnet(s) to be deleted.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 share_network_id = oscutils.find_resource( share_client.share_networks, parsed_args.share_network).id for subnet in parsed_args.share_network_subnet: try: share_client.share_network_subnets.delete( share_network_id, subnet) except Exception as e: result += 1 LOG.error(f"Failed to delete share network subnet with " f"ID {subnet}: {e}") if result > 0: total = len(parsed_args.share_network_subnet) raise exceptions.CommandError( f"{result} of {total} share network subnets failed to be " f"deleted.") class ShowShareNetworkSubnet(command.ShowOne): """Show share network subnet.""" _description = _("Show share network subnet") def get_parser(self, prog_name): parser = super(ShowShareNetworkSubnet, self).get_parser(prog_name) parser.add_argument( "share_network", metavar="", help=_("Share network name or ID.") ) parser.add_argument( "share_network_subnet", metavar="", help=_("ID of share network subnet to show.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_network_id = oscutils.find_resource( share_client.share_networks, parsed_args.share_network).id share_network_subnet = share_client.share_network_subnets.get( share_network_id, parsed_args.share_network_subnet) data = share_network_subnet._info # Special mapping for columns to make the output easier to read: # 'metadata' --> 'properties' data.update( { 'properties': format_columns.DictColumn(data.pop('metadata', {})), }, ) return self.dict2columns(data) class SetShareNetworkSubnet(command.Command): """Set share network subnet properties.""" _description = _("Set share network subnet properties") def get_parser(self, prog_name): parser = super(SetShareNetworkSubnet, self).get_parser(prog_name) parser.add_argument( "share_network", metavar="", help=_("Share network name or ID.") ) parser.add_argument( "share_network_subnet", metavar="", help=_("ID of share network subnet to set a property.") ) parser.add_argument( "--property", metavar="", default={}, action=parseractions.KeyValueAction, help=_("Set a property to this share network subnet " "(repeat option to set multiple properties). " "Available only for microversion >= 2.78."), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share if (parsed_args.property and share_client.api_version < api_versions.APIVersion("2.78")): raise exceptions.CommandError( "Property can be specified only with manila API " "version >= 2.78.") share_network_id = oscutils.find_resource( share_client.share_networks, parsed_args.share_network).id if parsed_args.property: try: share_client.share_network_subnets.set_metadata( share_network_id, parsed_args.property, subresource=parsed_args.share_network_subnet) except Exception as e: raise exceptions.CommandError(_( "Failed to set subnet property '%(properties)s': %(e)s") % {'properties': parsed_args.property, 'e': e}) class UnsetShareNetworkSubnet(command.Command): """Unset a share network subnet property.""" _description = _("Unset a share network subnet property") def get_parser(self, prog_name): parser = super(UnsetShareNetworkSubnet, self).get_parser(prog_name) parser.add_argument( "share_network", metavar="", help=_("Share network name or ID.") ) parser.add_argument( "share_network_subnet", metavar="", help=_("ID of share network subnet to set a property.") ) parser.add_argument( '--property', metavar='', action='append', help=_("Remove a property from share network subnet " "(repeat option to remove multiple properties). " "Available only for microversion >= 2.78."), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share if (parsed_args.property and share_client.api_version < api_versions.APIVersion("2.78")): raise exceptions.CommandError( "Property can be specified only with manila API " "version >= 2.78.") share_network_id = oscutils.find_resource( share_client.share_networks, parsed_args.share_network).id if parsed_args.property: result = 0 for key in parsed_args.property: try: share_client.share_network_subnets.delete_metadata( share_network_id, [key], subresource=parsed_args.share_network_subnet) except Exception as e: result += 1 LOG.error("Failed to unset subnet property " "'%(key)s': %(e)s", {'key': key, 'e': e}) if result > 0: total = len(parsed_args.property) raise exceptions.CommandError( f"{result} of {total} subnet properties failed to be " f"unset.") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_networks.py0000664000175000017500000006620100000000000024715 0ustar00zuulzuul00000000000000# Copyright 2021 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.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 import cliutils LOG = logging.getLogger(__name__) class ListShareNetwork(command.Lister): _description = _("List share networks") def get_parser(self, prog_name): parser = super(ListShareNetwork, self).get_parser(prog_name) parser.add_argument( '--name', metavar="", help=_('Filter share networks by name') ) parser.add_argument( '--name~', metavar="", help=_('Filter share networks by name-pattern. Available only ' 'for microversion >= 2.36.') ) parser.add_argument( '--description', metavar="", help=_('Filter share networks by description. Available ' 'only for microversion >= 2.36') ) parser.add_argument( '--description~', metavar="", help=_('Filter share networks by description-pattern. Available ' 'only for microversion >= 2.36.') ) parser.add_argument( '--all-projects', action='store_true', default=False, help=_('Include all projects (admin only)'), ) parser.add_argument( '--project', metavar='', help=_('Filter share networks by project (name or ID) ' '(admin only)') ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--created-since', metavar='', help=_('Filter share networks by date they were created after. ' 'The date can be in the format yyyy-mm-dd.') ) parser.add_argument( '--created-before', metavar='', help=_('Filter share networks by date they were created before. ' 'The date can be in the format yyyy-mm-dd.') ) parser.add_argument( '--security-service', metavar='', help=_('Filter share networks by the name or ID of a security ' 'service attached to the network.') ) parser.add_argument( '--neutron-net-id', metavar='', help=_('Filter share networks by the ID of a neutron network.') ) parser.add_argument( '--neutron-subnet-id', metavar='', help=_('Filter share networks by the ID of a neutron sub network.') ) parser.add_argument( '--network-type', metavar='', help=_('Filter share networks by the type of network. Examples ' 'include "flat", "vlan", "vxlan", "geneve", etc.') ) parser.add_argument( '--segmentation-id', metavar='', help=_('Filter share networks by the segmentation ID of network. ' 'Relevant only for segmented networks such as "vlan", ' '"vxlan", "geneve", etc.') ) parser.add_argument( '--cidr', metavar='', help=_('Filter share networks by the CIDR of network.') ) parser.add_argument( '--ip-version', metavar='4/6', choices=['4', '6'], help=_('Filter share networks by the IP Version of the network, ' 'either 4 or 6.') ) parser.add_argument( '--detail', action='store_true', default=False, help=_("List share networks with details") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share identity_client = self.app.client_manager.identity columns = ['ID', 'Name'] if parsed_args.detail: columns.extend([ 'Status', 'Created At', 'Updated At', 'Description', ]) project_id = None if parsed_args.project: project_id = identity_common.find_project( identity_client, parsed_args.project, parsed_args.project_domain).id # set value of 'all_tenants' when using project option all_tenants = bool(parsed_args.project) or parsed_args.all_projects if parsed_args.all_projects: columns.append('Project ID') search_opts = { 'all_tenants': all_tenants, 'project_id': project_id, 'name': parsed_args.name, 'created_since': parsed_args.created_since, 'created_before': parsed_args.created_before, 'neutron_net_id': parsed_args.neutron_net_id, 'neutron_subnet_id': parsed_args.neutron_subnet_id, 'network_type': parsed_args.network_type, 'segmentation_id': parsed_args.segmentation_id, 'cidr': parsed_args.cidr, 'ip_version': parsed_args.ip_version, 'security_service': parsed_args.security_service, } if share_client.api_version >= api_versions.APIVersion("2.36"): search_opts['name~'] = getattr(parsed_args, 'name~') search_opts['description~'] = getattr(parsed_args, 'description~') search_opts['description'] = parsed_args.description elif (parsed_args.description or getattr(parsed_args, 'name~') or getattr(parsed_args, 'description~')): raise exceptions.CommandError( "Pattern based filtering (name~, description~ and description)" " is only available with manila API version >= 2.36") data = share_client.share_networks.list(search_opts=search_opts) return ( columns, (oscutils.get_item_properties(s, columns) for s in data) ) class ShowShareNetwork(command.ShowOne): """Display a share network""" _description = _("Show details about a share network") def get_parser(self, prog_name): parser = super(ShowShareNetwork, self).get_parser(prog_name) parser.add_argument( "share_network", metavar="", help=_("Name or ID of the share network to display") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_network = oscutils.find_resource( share_client.share_networks, parsed_args.share_network) data = share_network._info # Special mapping for columns to make the output easier to read: # 'metadata' --> 'properties' for ss in data['share_network_subnets']: ss.update( { 'properties': format_columns.DictColumn(ss.pop('metadata', {})), }, ) # Add security services information security_services = share_client.security_services.list( search_opts={'share_network_id': share_network.id}, detailed=False) data['security_services'] = [ { 'security_service_name': ss.name, 'security_service_id': ss.id, } for ss in security_services ] if parsed_args.formatter == 'table': data['share_network_subnets'] = ( cliutils.convert_dict_list_to_string( data['share_network_subnets']) ) data['security_services'] = cliutils.convert_dict_list_to_string( data['security_services'] ) data.pop('links', None) return self.dict2columns(data) class CreateShareNetwork(command.ShowOne): """Create a share network.""" _description = _("Create a share network") def get_parser(self, prog_name): parser = super(CreateShareNetwork, self).get_parser(prog_name) parser.add_argument( "--name", metavar="", help=_("Add a name to the share network (Optional)") ) parser.add_argument( "--description", metavar="", default=None, help=_("Add a description to the share network (Optional).") ) parser.add_argument( "--neutron-net-id", metavar="", default=None, help=_("ID of the neutron network that must be associated with " "the share network (Optional). The network specified will " "be associated with the 'default' share network subnet, " "unless 'availability-zone' is also specified.") ) parser.add_argument( "--neutron-subnet-id", metavar="", default=None, help=_("ID of the neutron sub-network that must be associated " "with the share network (Optional). The subnet specified " "will be associated with the 'default' share network " "subnet, unless 'availability-zone' is also specified.") ) parser.add_argument( "--availability-zone", metavar="", default=None, help=_("Name or ID of the avalilability zone to assign the " "specified network subnet parameters to. Must be used " "in conjunction with 'neutron-net-id' and " "'neutron-subnet-id'. Do not specify this parameter if " "the network must be available across all availability " "zones ('default' share network subnet). Available " "only for microversion >= 2.51.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share availability_zone = None if (parsed_args.availability_zone and share_client.api_version < api_versions.APIVersion("2.51")): raise exceptions.CommandError( "Availability zone can be specified only with manila API " "version >= 2.51") elif parsed_args.availability_zone: availability_zone = parsed_args.availability_zone share_network = share_client.share_networks.create( name=parsed_args.name, description=parsed_args.description, neutron_net_id=parsed_args.neutron_net_id, neutron_subnet_id=parsed_args.neutron_subnet_id, availability_zone=availability_zone, ) share_network_data = share_network._info share_network_data.pop('links', None) if parsed_args.formatter == 'table': share_network_data['share_network_subnets'] = ( cliutils.convert_dict_list_to_string( share_network_data['share_network_subnets']) ) return self.dict2columns(share_network_data) class DeleteShareNetwork(command.Command): """Delete one or more share networks""" _description = _("Delete one or more share networks") def get_parser(self, prog_name): parser = super(DeleteShareNetwork, self).get_parser(prog_name) parser.add_argument( "share_network", metavar="", nargs="+", help=_("Name or ID of the share network(s) to delete") ) parser.add_argument( "--wait", action='store_true', default=False, help=_("Wait for the share network(s) to be deleted") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for share_network in parsed_args.share_network: try: share_network_obj = oscutils.find_resource( share_client.share_networks, share_network) share_client.share_networks.delete( share_network_obj) if parsed_args.wait: if not oscutils.wait_for_delete( manager=share_client.share_networks, res_id=share_network_obj.id): result += 1 except Exception as e: result += 1 LOG.error(f"Failed to delete share network with " f"name or ID {share_network}: {e}") if result > 0: total = len(parsed_args.share_network) msg = f"{result} of {total} share networks failed to be deleted." raise exceptions.CommandError(msg) class SetShareNetwork(command.Command): """Set share network properties.""" _description = _("Set share network properties") def get_parser(self, prog_name): parser = super(SetShareNetwork, self).get_parser(prog_name) parser.add_argument( "share_network", metavar="", help=_('Name or ID of the share network to set a property for') ) parser.add_argument( "--name", metavar="", default=None, help=_("Set a new name to the share network.") ) parser.add_argument( "--description", metavar="", default=None, help=_("Set a new description to the share network.") ) parser.add_argument( "--status", metavar="", choices=['active', 'error', 'network_change'], help=_("Assign a status to the share network (Admin only). " "Options include : active, error, network_change. " "Available only for microversion >= 2.63.") ) parser.add_argument( '--neutron-net-id', metavar='', help=_("Update the neutron network associated with the default " "share network subnet. If a default share network subnet " "is not present or if the share network is in use, setting " "this will fail.") ) parser.add_argument( '--neutron-subnet-id', metavar='', help=_("Update the neutron subnetwork associated with the default " "share network subnet. If a default share network subnet " "is not present or if the share network is in use, setting " "this will fail.") ) parser.add_argument( '--current-security-service', metavar='', help=_("Name or ID of a security service that is currently " "associated with a share network that must be replaced. " "Replacing a security service is only available for " "microversions >= 2.63.") ) parser.add_argument( '--new-security-service', metavar='', help=_("Name or ID of a security service that must be associated " "with the share network. When replacing a security " "service, the current security service must also be " "provided with the '--current-security-service' option. " "Replacing a security service is only available for " "microversions >= 2.63.") ) parser.add_argument( '--check-only', action='store_true', help=_("Run a dry-run of a security service replacement. " "Available only for microversion >=2.63") ) parser.add_argument( '--restart-check', action='store_true', help=_("Restart a dry-run of a security service " "replacement. Helpful when check results are " "stale. Available only for microversion >=2.63.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 share_network = oscutils.find_resource(share_client.share_networks, parsed_args.share_network) kwargs = {} # some args are only for newer API micro-version if share_client.api_version < api_versions.APIVersion("2.63"): new_args = ('status', 'check_only', 'restart_check', 'current_security_service') invalid_args = [ arg for arg in new_args if getattr(parsed_args, arg) is not None ] raise exceptions.CommandError( f"Use of {' '.join(invalid_args)} is only available for API " f"microversions >= 2.63.") # If "--check-only" and "--restart-check" are used, a new security # service option must be supplied if parsed_args.check_only or parsed_args.restart_check: if not parsed_args.new_security_service: raise exceptions.CommandError( _("Must provide new security service with --check-only " "and --restart-check.")) if parsed_args.name is not None: kwargs['name'] = parsed_args.name if parsed_args.description is not None: kwargs['description'] = parsed_args.description if parsed_args.neutron_net_id is not None: kwargs['neutron_net_id'] = parsed_args.neutron_net_id if parsed_args.neutron_subnet_id is not None: kwargs['neutron_subnet_id'] = parsed_args.neutron_subnet_id if kwargs: try: share_client.share_networks.update( share_network, **kwargs ) except Exception as e: result += 1 LOG.error(f"Failed to set share network properties " f"{kwargs}: {e}") if parsed_args.status: try: share_client.share_networks.reset_state( share_network, parsed_args.status ) except Exception as e: result += 1 LOG.error(f"Failed to update share network status to " f"{parsed_args.status}: {e}") new_security_service = current_security_service = None if parsed_args.new_security_service: new_security_service = oscutils.find_resource( share_client.security_services, parsed_args.new_security_service) if parsed_args.current_security_service: current_security_service = oscutils.find_resource( share_client.security_services, parsed_args.current_security_service) if new_security_service and not current_security_service: try: if parsed_args.check_only: check_result = share_client.share_networks\ .add_security_service_check( share_network, new_security_service, reset_operation=parsed_args.restart_check) is_compatible = check_result[1].get('compatible') # NOTE(gouthamr): We're logging to the console here, # because there's no need to print useful # information beyond the result. Use of error # logging is a hack, since other kinds of logs may # not print to console by default. if is_compatible is None: LOG.error("Security service check has been " "successfully initiated, please retry " "after some time.") elif is_compatible: LOG.error( f"Security service " f"{parsed_args.new_security_service} can " f"be added to share network " f"{parsed_args.share_network}.") else: LOG.error( f"Security service " f"{parsed_args.new_security_service} cannot " f"be added to share network " f"{parsed_args.share_network}.") else: share_client.share_networks.add_security_service( share_network, new_security_service) except Exception as e: result += 1 LOG.error(f"Failed to add security service " f"{parsed_args.new_security_service} to " f"share network {parsed_args.share_network}: {e}") if new_security_service and current_security_service: try: if parsed_args.check_only: check_result = share_client.share_networks.\ update_share_network_security_service_check( share_network, current_security_service, new_security_service, reset_operation=parsed_args.restart_check) is_compatible = check_result[1].get('compatible') # NOTE(gouthamr): We're logging to the console here, # because there's no need to print useful # information beyond the result. Use of error # logging is a hack, since other kinds of logs may # not print to console by default. if is_compatible is None: LOG.error("Security service check has been " "successfully initiated, please retry " "after some time.") elif is_compatible: LOG.error( f"Security service " f"{parsed_args.current_security_service} can " f"be replaced with security service " f"{parsed_args.new_security_service} on share " f"network {parsed_args.share_network}.") else: LOG.error( f"Security service " f"{parsed_args.current_security_service} cannot " f"be replaced with security service " f"{parsed_args.new_security_service} on share " f"network {parsed_args.share_network}.") else: share_client.share_networks.\ update_share_network_security_service( share_network, current_security_service, new_security_service) except Exception as e: result += 1 LOG.error(f"Failed to update security service " f"{parsed_args.current_security_service} on " f"share network " f"{parsed_args.share_network}: {e}") if result > 0: raise exceptions.CommandError(_("One or more of the " "set operations failed")) class UnsetShareNetwork(command.Command): """Unset a share network property.""" _description = _("Unset a share network property") def get_parser(self, prog_name): parser = super(UnsetShareNetwork, self).get_parser(prog_name) parser.add_argument( "share_network", metavar="", help=_("Name or ID of the share network to unset a property of") ) parser.add_argument( "--name", action='store_true', help=_("Unset share network name."), ) parser.add_argument( "--description", action='store_true', help=_("Unset share network description."), ) parser.add_argument( "--security-service", metavar="", help=_("Disassociate a security service from the share network. " "This is only possible with unused share networks."), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_network = oscutils.find_resource( share_client.share_networks, parsed_args.share_network) security_service = None if parsed_args.security_service: security_service = oscutils.find_resource( share_client.security_services, parsed_args.security_service) result = 0 kwargs = {} if parsed_args.name: # the SDK unsets name if it is an empty string kwargs['name'] = '' if parsed_args.description: # the SDK unsets description if it is an empty string kwargs['description'] = '' if kwargs: try: share_client.share_networks.update( share_network, **kwargs ) except Exception as e: result += 1 LOG.error(f"Failed to unset share network properties " f"{kwargs}: {e}") if security_service: try: share_client.share_networks.remove_security_service( share_network, security_service) except Exception as e: result += 1 LOG.error(f"Failed to dissociate security service" f"{parsed_args.security_service} from " f"{parsed_args.share_network}: {e}") if result > 0: raise exceptions.CommandError(_("One or more of the " "unset operations failed")) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_pools.py0000664000175000017500000000706400000000000024177 0ustar00zuulzuul00000000000000# 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.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils from manilaclient import api_versions from manilaclient.common._i18n import _ from manilaclient.osc import utils class ListSharePools(command.Lister): """List all backend storage pools known to the scheduler (Admin only).""" _description = _( "List all backend storage pools known to the scheduler (Admin only).") def get_parser(self, prog_name): parser = super(ListSharePools, self).get_parser(prog_name) parser.add_argument( "--host", metavar="", default=None, help=_("Filter results by host name. " "Regular expressions are supported.") ) parser.add_argument( "--backend", metavar="", default=None, help=_("Filter results by backend name. " "Regular expressions are supported.") ) parser.add_argument( "--pool", metavar="", default=None, help=_("Filter results by pool name. " "Regular expressions are supported.") ) parser.add_argument( "--detail", action='store_true', default=False, help=_("Show detailed information about pools.") ) parser.add_argument( "--share-type", metavar="", default=None, help=_("Filter results by share type name or ID. " "Available only for microversion >= 2.23") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_type = None if parsed_args.share_type: if share_client.api_version >= api_versions.APIVersion("2.23"): share_type = osc_utils.find_resource( share_client.share_types, parsed_args.share_type).id else: raise exceptions.CommandError(_( "Filtering results by share type is only available with " "manila API version >= 2.23")) search_opts = { 'host': parsed_args.host, 'backend': parsed_args.backend, 'pool': parsed_args.pool, 'share_type': share_type, } pools = share_client.pools.list( detailed=parsed_args.detail, search_opts=search_opts) columns = ["Name", "Host", "Backend", "Pool"] if parsed_args.detail: columns.append("Capabilities") if parsed_args.formatter == 'table': for pool in pools: pool._info.update({ 'capabilities': utils.format_properties( pool.capabilities)}) data = (osc_utils.get_dict_properties( pool._info, columns) for pool in pools) return (columns, data) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_replica_export_locations.py0000664000175000017500000000541700000000000030136 0ustar00zuulzuul00000000000000# 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.command import command from osc_lib import utils as osc_utils from manilaclient.common._i18n import _ class ShareReplicaListExportLocation(command.Lister): """List export locations of a share replica.""" _description = _("List export locations of a share replica.") def get_parser(self, prog_name): parser = super( ShareReplicaListExportLocation, self).get_parser(prog_name) parser.add_argument( "replica", metavar="", help=_("ID of the share replica.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share columns = [ 'ID', 'Availability Zone', 'Replica State', 'Preferred', 'Path', ] replica = osc_utils.find_resource( share_client.share_replicas, parsed_args.replica) export_locations = share_client.share_replica_export_locations.list( replica) data = (osc_utils.get_dict_properties( location._info, columns) for location in export_locations) return (columns, data) class ShareReplicaShowExportLocation(command.ShowOne): """Show details of a share replica's export location.""" _description = _("Show details of a share replica's export location.") def get_parser(self, prog_name): parser = super( ShareReplicaShowExportLocation, self).get_parser(prog_name) parser.add_argument( "replica", metavar="", help=_("ID of the share replica.") ) parser.add_argument( "export_location", metavar="", help=_("ID of the share replica export location.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share replica = osc_utils.find_resource( share_client.share_replicas, parsed_args.replica) export_location = share_client.share_replica_export_locations.get( replica, parsed_args.export_location) return self.dict2columns(export_location._info) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_replicas.py0000664000175000017500000003403700000000000024645 0ustar00zuulzuul00000000000000# 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 osc_utils from manilaclient import api_versions from manilaclient.common._i18n import _ from manilaclient.common import cliutils from manilaclient.osc import utils LOG = logging.getLogger(__name__) class CreateShareReplica(command.ShowOne): """Create a share replica.""" _description = _("Create a replica of the given share") def get_parser(self, prog_name): parser = super(CreateShareReplica, self).get_parser(prog_name) parser.add_argument( "share", metavar="", help=_("Name or ID of the share to replicate.") ) parser.add_argument( '--availability-zone', metavar='', default=None, help=_('Availability zone in which the replica should be created.') ) parser.add_argument( '--wait', action='store_true', default=False, help=_('Wait for replica creation') ) parser.add_argument( "--scheduler-hint", metavar="", default={}, action=parseractions.KeyValueAction, help=_("Scheduler hint for the share replica as key=value pairs, " "Supported key is only_host. Available for microversion " ">= 2.67."), ) parser.add_argument( '--share-network', metavar='', default=None, help=_('Optional network info ID or name. Available for ' 'microversion >= 2.72') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share = osc_utils.find_resource(share_client.shares, parsed_args.share) scheduler_hints = {} if parsed_args.scheduler_hint: if share_client.api_version < api_versions.APIVersion("2.67"): raise exceptions.CommandError(_( "arg '--scheduler_hint' is available only starting with " "API microversion '2.67'.")) hints = utils.extract_key_value_options(parsed_args.scheduler_hint) if 'only_host' not in hints.keys() or len(hints) > 1: raise exceptions.CommandError( "The only valid key supported with the --scheduler-hint " "argument is 'only_host'.") scheduler_hints['only_host'] = hints.get('only_host') body = { 'share': share, 'availability_zone': parsed_args.availability_zone, } if scheduler_hints: body['scheduler_hints'] = scheduler_hints share_network_id = None if parsed_args.share_network: if share_client.api_version < api_versions.APIVersion("2.72"): raise exceptions.CommandError( "'share-network' option is available only starting " "with '2.72' API microversion.") share_network_id = osc_utils.find_resource( share_client.share_networks, parsed_args.share_network).id body['share_network'] = share_network_id share_replica = share_client.share_replicas.create(**body) if parsed_args.wait: if not osc_utils.wait_for_status( status_f=share_client.share_replicas.get, res_id=share_replica.id, success_status=['available'] ): LOG.error(_("ERROR: Share replica is in error state.")) share_replica = osc_utils.find_resource( share_client.share_replicas, share_replica.id) return self.dict2columns(share_replica._info) class DeleteShareReplica(command.Command): """Delete one or more share replicas.""" _description = _("Delete one or more share replicas") def get_parser(self, prog_name): parser = super(DeleteShareReplica, self).get_parser(prog_name) parser.add_argument( "replica", metavar="", nargs="+", help=_("Name or ID of the replica(s) to delete") ) parser.add_argument( "--force", action='store_true', default=False, help=_("Attempt to force delete a replica on its backend. " "Using this option will purge the replica from Manila " "even if it is not cleaned up on the backend. ") ) parser.add_argument( "--wait", action='store_true', default=False, help=_("Wait for share replica deletion") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for replica in parsed_args.replica: try: replica_obj = osc_utils.find_resource( share_client.share_replicas, replica) share_client.share_replicas.delete( replica_obj, force=parsed_args.force) if parsed_args.wait: if not osc_utils.wait_for_delete( manager=share_client.share_replicas, res_id=replica_obj.id): result += 1 except Exception as e: result += 1 LOG.error(_( "Failed to delete a share replica with " "name or ID '%(replica)s': %(e)s"), {'replica': replica, 'e': e}) if result > 0: total = len(parsed_args.replica) msg = (_("%(result)s of %(total)s replicas failed " "to delete.") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) class ListShareReplica(command.Lister): """List share replicas.""" _description = _("List share replicas") def get_parser(self, prog_name): parser = super(ListShareReplica, self).get_parser(prog_name) parser.add_argument( "--share", metavar="", default=None, help=_("Name or ID of the share to list replicas for.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share = None if parsed_args.share: share = osc_utils.find_resource( share_client.shares, parsed_args.share) replicas = share_client.share_replicas.list(share=share) columns = [ 'id', 'status', 'replica_state', 'share_id', 'host', 'availability_zone', 'updated_at', ] column_headers = utils.format_column_headers(columns) data = (osc_utils.get_dict_properties( replica._info, columns) for replica in replicas) return (column_headers, data) class ShowShareReplica(command.ShowOne): """Show share replica.""" _description = _("Show details about a replica") def get_parser(self, prog_name): parser = super(ShowShareReplica, self).get_parser(prog_name) parser.add_argument( "replica", metavar="", help=_("ID of the share replica. Available only for " "microversion >= 2.47. ") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share replica = share_client.share_replicas.get( parsed_args.replica) replica_export_locations = ( share_client.share_replica_export_locations.list( share_replica=replica)) replica._info['export_locations'] = [] for element_location in replica_export_locations: element_location._info.pop('links', None) replica._info['export_locations'].append( element_location._info) if parsed_args.formatter == 'table': replica._info['export_locations'] = ( cliutils.convert_dict_list_to_string( replica._info['export_locations'])) replica._info.pop('links', None) return self.dict2columns(replica._info) class SetShareReplica(command.Command): """Set share replica""" _description = _("Explicitly set share replica status and/or " "replica-state") def get_parser(self, prog_name): parser = super(SetShareReplica, self).get_parser(prog_name) parser.add_argument( "replica", metavar="", help=_("ID of the share replica to modify.") ) parser.add_argument( "--replica-state", metavar="", choices=['in_sync', 'out_of_sync', 'active', 'error'], help=_("Indicate which replica_state to assign the replica. " "Options include in_sync, out_of_sync, active and error.") ) parser.add_argument( "--status", metavar="", choices=['available', 'error', 'creating', 'deleting', 'error_deleting'], help=_("Indicate which status to assign the replica. Options " "include available, error, creating, deleting and " "error_deleting.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 replica = osc_utils.find_resource( share_client.share_replicas, parsed_args.replica) if parsed_args.replica_state: try: share_client.share_replicas.reset_replica_state( replica, parsed_args.replica_state ) except Exception as e: result += 1 LOG.error(_( "Failed to set replica_state " "'%(replica_state)s': %(exception)s"), {'replica_state': parsed_args.replica_state, 'exception': e}) if parsed_args.status: try: share_client.share_replicas.reset_state( replica, parsed_args.status ) except Exception as e: result += 1 LOG.error(_( "Failed to set status '%(status)s': %(exception)s"), {'status': parsed_args.status, 'exception': e}) if not parsed_args.replica_state and not parsed_args.status: raise exceptions.CommandError(_( "Nothing to set. Please define " "either '--replica_state' or '--status'.")) if result > 0: raise exceptions.CommandError(_("One or more of the " "set operations failed")) class PromoteShareReplica(command.Command): """Promote share replica""" _description = _("Promote specified replica to 'active' " "replica_state.") def get_parser(self, prog_name): parser = super(PromoteShareReplica, self).get_parser(prog_name) parser.add_argument( "replica", metavar="", help=_("ID of the share replica.") ) parser.add_argument( '--quiesce-wait-time', metavar='', default=None, help=_('Quiesce wait time in seconds. Available for ' 'microversion >= 2.75') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share replica = osc_utils.find_resource( share_client.share_replicas, parsed_args.replica) args = [ replica, ] if parsed_args.quiesce_wait_time: if share_client.api_version < api_versions.APIVersion("2.75"): raise exceptions.CommandError( "'quiesce-wait-time' option is available only starting " "with '2.75' API microversion.") args += [parsed_args.quiesce_wait_time] try: share_client.share_replicas.promote(*args) except Exception as e: raise exceptions.CommandError(_( "Failed to promote replica to 'active': %s" % (e))) class ResyncShareReplica(command.Command): """Resync share replica""" _description = _("Attempt to update the share replica with its " "'active' mirror.") def get_parser(self, prog_name): parser = super(ResyncShareReplica, self).get_parser(prog_name) parser.add_argument( "replica", metavar="", help=_("ID of the share replica to resync.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share replica = osc_utils.find_resource( share_client.share_replicas, parsed_args.replica) try: share_client.share_replicas.resync(replica) except Exception as e: raise exceptions.CommandError(_( "Failed to resync share replica: %s" % (e))) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_servers.py0000664000175000017500000005653200000000000024540 0ustar00zuulzuul00000000000000# 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 parseractions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils from manilaclient import api_versions from manilaclient.common._i18n import _ from manilaclient.common.apiclient import utils as apiutils from manilaclient.common import cliutils from manilaclient.common import constants LOG = logging.getLogger(__name__) class DeleteShareServer(command.Command): """Delete one or more share servers (Admin only)""" _description = _( "Delete one or more share servers") def get_parser(self, prog_name): parser = super(DeleteShareServer, self).get_parser(prog_name) parser.add_argument( "share_servers", metavar="", nargs="+", help=_("ID(s) of the server(s) to delete") ) parser.add_argument( "--wait", action='store_true', default=False, help=_("Wait for share server deletion.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for server in parsed_args.share_servers: try: server_obj = osc_utils.find_resource( share_client.share_servers, server) share_client.share_servers.delete(server_obj) if parsed_args.wait: if not osc_utils.wait_for_delete( manager=share_client.share_servers, res_id=server_obj.id): result += 1 except Exception as e: result += 1 LOG.error(_( "Failed to delete a share server with " "ID '%(server)s': %(e)s"), {'server': server, 'e': e}) if result > 0: total = len(parsed_args.share_servers) msg = f'Failed to delete {result} servers out of {total}.' raise exceptions.CommandError(_(msg)) class ShowShareServer(command.ShowOne): """Show share server (Admin only).""" _description = _("Show details about a share server (Admin only).") def get_parser(self, prog_name): parser = super(ShowShareServer, self).get_parser(prog_name) parser.add_argument( "share_server", metavar="", help=_("ID of share server.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_server = osc_utils.find_resource( share_client.share_servers, parsed_args.share_server) # 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"] share_server._info.pop('links', None) return self.dict2columns(share_server._info) class ListShareServer(command.Lister): """List all share servers (Admin only).""" _description = _("List all share servers (Admin only).") def get_parser(self, prog_name): parser = super(ListShareServer, self).get_parser(prog_name) parser.add_argument( '--host', metavar='', default=None, help=_('Filter results by name of host.'), ) parser.add_argument( '--status', metavar="", default=None, help=_('Filter results by status.') ) parser.add_argument( '--share-network', metavar='', default=None, help=_('Filter results by share network name or ID.'), ) parser.add_argument( '--project', metavar='', default=None, help=_('Filter results by project name or ID.') ) parser.add_argument( '--share-network-subnet', metavar='', type=str, default=None, help=_("Filter results by share network subnet that the " "share server's network allocation exists within. " "Available for microversion >= 2.51 (Optional, " "Default=None)") ) identity_common.add_project_domain_option_to_parser(parser) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share identity_client = self.app.client_manager.identity project_id = None if parsed_args.project: project_id = identity_common.find_project( identity_client, parsed_args.project, parsed_args.project_domain).id if (parsed_args.share_network_subnet and share_client.api_version < api_versions.APIVersion("2.51")): raise exceptions.CommandError( "Share network subnet can be specified only with manila API " "version >= 2.51" ) columns = [ 'ID', 'Host', 'Status', 'Share Network ID', 'Project ID', ] search_opts = { 'status': parsed_args.status, 'host': parsed_args.host, 'project_id': project_id, } if parsed_args.share_network: share_network_id = osc_utils.find_resource( share_client.share_networks, parsed_args.share_network).id search_opts['share_network'] = share_network_id if parsed_args.share_network_subnet: search_opts['share_network_subnet_id'] = ( parsed_args.share_network_subnet) share_servers = share_client.share_servers.list( search_opts=search_opts) data = (osc_utils.get_dict_properties( share_server._info, columns) for share_server in share_servers) return (columns, data) class AdoptShareServer(command.ShowOne): """Adopt share server not handled by Manila (Admin only).""" _description = _("Adopt share server not handled by Manila (Admin only).") def get_parser(self, prog_name): parser = super(AdoptShareServer, self).get_parser(prog_name) parser.add_argument( 'host', metavar='', type=str, help=_('Backend name as "@".') ) parser.add_argument( "share_network", metavar="", help=_("Share network where share server has network " "allocations in.") ) parser.add_argument( 'identifier', metavar='', type=str, help=_("A driver-specific share server identifier required " "by the driver to manage the share server.") ) parser.add_argument( '--driver-options', metavar='', action=parseractions.KeyValueAction, default={}, help=_("One or more driver-specific key=value pairs that may be " "necessary to manage the share server (Optional, " "Default=None).") ) parser.add_argument( '--share-network-subnet', type=str, metavar='', default=None, 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)." ) parser.add_argument( "--wait", action='store_true', help=_("Wait until share server is adopted") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_network = None if parsed_args.share_network: share_network = osc_utils.find_resource( share_client.share_networks, parsed_args.share_network).id share_network_subnet = None if (parsed_args.share_network_subnet and share_client.api_version < api_versions.APIVersion("2.51")): raise exceptions.CommandError( "Share network subnet can be specified only with manila API " "version >= 2.51" ) elif parsed_args.share_network_subnet: share_network_subnet = share_client.share_network_subnets.get( share_network, parsed_args.share_network_subnet).id share_server = share_client.share_servers.manage( host=parsed_args.host, share_network_id=share_network, identifier=parsed_args.identifier, driver_options=parsed_args.driver_options, share_network_subnet_id=share_network_subnet ) if parsed_args.wait: if not osc_utils.wait_for_status( status_f=share_client.share_servers.get, res_id=share_server.id, success_status=['active'], error_status=['manage_error', 'error'] ): LOG.error(_("ERROR: Share server is in error state.")) share_server = osc_utils.find_resource(share_client.share_servers, share_server.id) share_server._info.pop('links', None) # All 'backend_details' data already present as separated strings, # so remove big dict from view. share_server._info.pop("backend_details", None) return self.dict2columns(share_server._info) class AbandonShareServer(command.Command): """Remove one or more share servers (Admin only).""" _description = _("Remove one or more share server(s) (Admin only).") def get_parser(self, prog_name): parser = super(AbandonShareServer, self).get_parser(prog_name) parser.add_argument( "share_server", metavar="", nargs='+', help=_("ID of the server(s) to be abandoned.") ) parser.add_argument( "--force", action='store_true', default=False, help=_("Enforces the unmanage share server operation, even " "if the backend driver does not support it.") ) parser.add_argument( "--wait", action='store_true', default=False, help=_("Wait until share server is abandoned") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for server in parsed_args.share_server: try: server_obj = osc_utils.find_resource( share_client.share_servers, server) kwargs = {} if parsed_args.force: kwargs['force'] = parsed_args.force share_client.share_servers.unmanage( server_obj, **kwargs) if parsed_args.wait: if not osc_utils.wait_for_delete( manager=share_client.share_servers, res_id=server_obj.id): result += 1 except Exception as e: result += 1 LOG.error(_( "Failed to abandon share server with " "ID '%(server)s': %(e)s"), {'server': server, 'e': e}) if result > 0: total = len(parsed_args.share_server) msg = f'Failed to abandon {result} of {total} servers.' raise exceptions.CommandError(_(msg)) class SetShareServer(command.Command): """Set share server properties.""" _description = _("Set share server properties (Admin only).") def get_parser(self, prog_name): parser = super(SetShareServer, self).get_parser(prog_name) allowed_update_choices = [ 'unmanage_starting', 'server_migrating_to', 'error', 'unmanage_error', 'manage_error', 'inactive', 'active', 'server_migrating', 'manage_starting', 'deleting', 'network_change'] allowed_update_choices_str = ', '.join(allowed_update_choices) parser.add_argument( "share_server", metavar="", help=_("ID of the share server to modify.") ) parser.add_argument( "--status", metavar="", required=False, default=constants.STATUS_ACTIVE, help=_("Assign a status to the share server. Options " "include: %s. If no state is " "provided, active will be " "used." % allowed_update_choices_str) ) parser.add_argument( '--task-state', metavar="", required=False, default=None, help=_("Indicate which task state to assign the share server. " "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. ") ) return parser def take_action(self, parsed_args): if not parsed_args.status and not parsed_args.task_state: msg = (_("A status or a task state should be provided for this " "command.")) LOG.error(msg) raise exceptions.CommandError(msg) share_client = self.app.client_manager.share share_server = osc_utils.find_resource( share_client.share_servers, parsed_args.share_server) if parsed_args.status: try: share_client.share_servers.reset_state( share_server, parsed_args.status ) except Exception as e: msg = (_( "Failed to set status '%(status)s': %(exception)s"), {'status': parsed_args.status, 'exception': e}) LOG.error(msg) raise exceptions.CommandError(msg) if parsed_args.task_state: if share_client.api_version < api_versions.APIVersion("2.57"): raise exceptions.CommandError( "Setting the state of a share server is only available " "with manila API version >= 2.57") else: result = 0 try: share_client.share_servers.reset_task_state( share_server, parsed_args.task_state) except Exception as e: LOG.error(_("Failed to update share server task state " "%s"), e) result += 1 if result > 0: raise exceptions.CommandError(_("One or more of the " "reset operations failed")) class ShareServerMigrationCancel(command.Command): """Attempts to cancel migration for a given share server :param share_server: either share_server object or text with its ID. """ _description = _("Cancels migration of a given share server when copying") def get_parser(self, prog_name): parser = super(ShareServerMigrationCancel, self).get_parser(prog_name) parser.add_argument( 'share_server', metavar='', help=_('ID of share server to cancel migration.') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_server = osc_utils.find_resource( share_client.share_servers, parsed_args.share_server) if share_client.api_version >= api_versions.APIVersion("2.57"): share_server.migration_cancel() else: raise exceptions.CommandError( "Share Server Migration cancel is only available " "with manila API version >= 2.57") class ShareServerMigrationComplete(command.Command): """Completes migration for a given share server (Admin only, Experimental). """ _description = _("Completes migration for a given share server") def get_parser(self, prog_name): parser = super(ShareServerMigrationComplete, self).get_parser( prog_name) parser.add_argument( 'share_server', metavar='', help=_('ID of share server to complete migration.') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_server = osc_utils.find_resource( share_client.share_servers, parsed_args.share_server) if share_client.api_version >= api_versions.APIVersion("2.57"): share_server.migration_complete() else: raise exceptions.CommandError( "Share Server Migration complete is only available " "with manila API version >= 2.57") class ShareServerMigrationShow(command.ShowOne): """Obtains progress of share migration for a given share server. (Admin only, Experimental). :param share_server: either share_server object or text with its ID. """ _description = _( "Gets migration progress of a given share server when copying") def get_parser(self, prog_name): parser = super(ShareServerMigrationShow, self).get_parser(prog_name) parser.add_argument( 'share_server', metavar='', help='ID of share server to show migration progress for.' ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share if share_client.api_version >= api_versions.APIVersion("2.57"): share_server = osc_utils.find_resource( share_client.share_servers, parsed_args.share_server) result = share_server.migration_get_progress() return self.dict2columns(result) else: raise exceptions.CommandError( "Share Server Migration show is only available " "with manila API version >= 2.57") class ShareServerMigrationStart(command.ShowOne): """Migrates share server to a new host (Admin only, Experimental).""" _description = _("Migrates share server to a new host.") def get_parser(self, prog_name): parser = super(ShareServerMigrationStart, self).get_parser(prog_name) parser.add_argument( 'share_server', metavar='', help=_('ID of share server to start migration.') ) parser.add_argument( 'host', metavar='', help=_("Destination to migrate the share server to. Use " "the format '@'.") ) parser.add_argument( '--preserve-snapshots', metavar='', choices=['True', 'False'], required=True, help=_("Set to True if snapshots must be preserved at " "the migration destination.") ) parser.add_argument( '--writable', metavar='', choices=['True', 'False'], required=True, help=_("Enforces migration to keep all its shares writable " "while contents are being moved.") ) parser.add_argument( '--nondisruptive', metavar='', choices=['True', 'False'], required=True, help=_("Enforces migration to be nondisruptive.") ) parser.add_argument( '--new-share-network', metavar='', required=False, default=None, help=_('Specify a new share network for the share server. Do not ' 'specify this parameter if the migrating share server has ' 'to be retained within its current share network.',) ) parser.add_argument( '--check-only', action='store_true', default=False, help=_("Run a dry-run of the share server migration. ") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_server = osc_utils.find_resource( share_client.share_servers, parsed_args.share_server) if share_client.api_version >= api_versions.APIVersion("2.57"): new_share_net_id = None result = None if parsed_args.new_share_network: new_share_net_id = apiutils.find_resource( share_client.share_networks, parsed_args.new_share_network).id if parsed_args.check_only: result = share_server.migration_check( parsed_args.host, parsed_args.writable, parsed_args.nondisruptive, parsed_args.preserve_snapshots, new_share_net_id ) if result: if parsed_args.formatter == 'table': for k, v in result.items(): if isinstance(v, dict): capabilities_list = [v] dict_values = cliutils.convert_dict_list_to_string( capabilities_list ) result[k] = dict_values return self.dict2columns(result) else: share_server.migration_start(parsed_args.host, parsed_args.writable, parsed_args.nondisruptive, parsed_args.preserve_snapshots, new_share_net_id) return ({}, {}) else: raise exceptions.CommandError( "Share Server Migration is only available " "with manila API version >= 2.57") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_snapshot_instance_export_locations.py0000664000175000017500000000625400000000000032242 0ustar00zuulzuul00000000000000# 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.command import command from osc_lib import utils from manilaclient.common._i18n import _ from manilaclient.common.apiclient import utils as apiutils class ShareSnapshotInstanceExportLocationList(command.Lister): """List export locations from a share snapshot instance.""" _description = _("List export locations from a share snapshot instance.") def get_parser(self, prog_name): parser = ( super(ShareSnapshotInstanceExportLocationList, self).get_parser( prog_name)) parser.add_argument( "instance", metavar="", help=_("Name or ID of the share instance.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share snapshot_instance = apiutils.find_resource( share_client.share_snapshot_instances, parsed_args.instance) share_snapshot_instance_export_locations = ( share_client.share_snapshot_instance_export_locations.list( snapshot_instance=snapshot_instance)) columns = ["ID", "Path", "Is Admin only"] return ( columns, (utils.get_item_properties(s, columns) for s in share_snapshot_instance_export_locations)) class ShareSnapshotInstanceExportLocationShow(command.ShowOne): """Show export location of the share snapshot instance.""" _description = _("Show export location of the share snapshot instance.") def get_parser(self, prog_name): parser = ( super(ShareSnapshotInstanceExportLocationShow, self).get_parser( prog_name)) parser.add_argument( 'snapshot_instance', metavar='', help=_('ID of the share snapshot instance.') ) parser.add_argument( 'export_location', metavar='', help=_('ID of the share snapshot instance export location.') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share snapshot_instance = apiutils.find_resource( share_client.share_snapshot_instances, parsed_args.snapshot_instance) share_snapshot_instance_export_location = ( share_client.share_snapshot_instance_export_locations.get( parsed_args.export_location, snapshot_instance=snapshot_instance)) share_snapshot_instance_export_location._info.pop('links', None) return self.dict2columns(share_snapshot_instance_export_location._info) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_snapshot_instances.py0000664000175000017500000001163200000000000026745 0ustar00zuulzuul00000000000000# 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.command import command from osc_lib import exceptions from osc_lib import utils from manilaclient.common._i18n import _ from manilaclient.common import cliutils class ListShareSnapshotInstance(command.Lister): """List all share snapshot instances.""" _description = _("List all share snapshot instances") def get_parser(self, prog_name): parser = super(ListShareSnapshotInstance, self).get_parser( prog_name) parser.add_argument( "--snapshot", metavar="", default=None, help=_("Filter results by share snapshot ID.") ) parser.add_argument( "--detailed", action="store_true", help=_("Show detailed information about snapshot instances. ") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share snapshot = (share_client.share_snapshots.get(parsed_args.snapshot) if parsed_args.snapshot else None) share_snapshot_instances = share_client.share_snapshot_instances.list( detailed=parsed_args.detailed, snapshot=snapshot, ) list_of_keys = ['ID', 'Snapshot ID', 'Status'] if (parsed_args.detailed): list_of_keys += ['Created At', 'Updated At', 'Share ID', 'Share Instance ID', 'Progress', 'Provider Location'] return (list_of_keys, (utils.get_item_properties (s, list_of_keys) for s in share_snapshot_instances)) class ShowShareSnapshotInstance(command.ShowOne): """Show details about a share snapshot instance.""" _description = _("Show details about a share snapshot instance.") def get_parser(self, prog_name): parser = super(ShowShareSnapshotInstance, self).get_parser( prog_name) parser.add_argument( "snapshot_instance", metavar="", help=_("ID of the share snapshot instance.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share snapshot_instance = share_client.share_snapshot_instances.get( parsed_args.snapshot_instance) snapshot_instance_export_locations = ( share_client.share_snapshot_instance_export_locations.list( snapshot_instance=snapshot_instance)) snapshot_instance._info['export_locations'] = [] for element_location in snapshot_instance_export_locations: element_location._info.pop('links', None) snapshot_instance._info['export_locations'].append( element_location._info) if parsed_args.formatter == 'table': snapshot_instance._info['export_locations'] = ( cliutils.convert_dict_list_to_string( snapshot_instance._info['export_locations'])) snapshot_instance._info.pop('links', None) return self.dict2columns(snapshot_instance._info) class SetShareSnapshotInstance(command.Command): """Explicitly update the state of a share snapshot instance.""" _description = _("Explicitly update the state of a share snapshot " "instance.") def get_parser(self, prog_name): parser = super(SetShareSnapshotInstance, self).get_parser( prog_name) parser.add_argument( "snapshot_instance", metavar="", help=_("ID of the share snapshot instance to update.") ) parser.add_argument( '--status', metavar='', default='available', choices=['available', 'error', 'creating', 'deleting', 'error_deleting'], help=_('Indicate state to update the snapshot instance to. ' 'Default is available.') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share try: share_client.share_snapshot_instances.reset_state( parsed_args.snapshot_instance, parsed_args.status) except Exception as e: msg = _( "Failed to update share snapshot instance status: %s" % e) raise exceptions.CommandError(msg) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_snapshots.py0000664000175000017500000007311000000000000025060 0ustar00zuulzuul00000000000000# 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 format_columns from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from manilaclient import api_versions from manilaclient.common._i18n import _ from manilaclient.common import cliutils from manilaclient.osc import utils as oscutils LOG = logging.getLogger(__name__) class CreateShareSnapshot(command.ShowOne): """Create a share snapshot.""" _description = _( "Create a snapshot of the given share") def get_parser(self, prog_name): parser = super(CreateShareSnapshot, self).get_parser(prog_name) parser.add_argument( "share", metavar="", help=_("Name or ID of the share to create snapshot of") ) parser.add_argument( "--force", action='store_true', default=False, help=_("Optional flag to indicate whether to snapshot " "a share even if it's busy. (Default=False)") ) parser.add_argument( "--name", metavar="", default=None, help=_("Add a name to the snapshot (Optional).") ) parser.add_argument( "--description", metavar="", default=None, help=_("Add a description to the snapshot (Optional).") ) parser.add_argument( '--wait', action='store_true', default=False, help=_('Wait for share snapshot creation') ) parser.add_argument( "--property", metavar="", default={}, action=parseractions.KeyValueAction, help=_("Set a property to this snapshot " "(repeat option to set multiple properties)." "Available only for microversion >= 2.73"), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share = utils.find_resource(share_client.shares, parsed_args.share) if share_client.api_version >= api_versions.APIVersion("2.73"): property = parsed_args.property or {} elif parsed_args.property: raise exceptions.CommandError( "Setting metadtaa is only available with manila API version " ">= 2.73") share_snapshot = share_client.share_snapshots.create( share=share, force=parsed_args.force, name=parsed_args.name or None, description=parsed_args.description or None, metadata=property ) if parsed_args.wait: if not utils.wait_for_status( status_f=share_client.share_snapshots.get, res_id=share_snapshot.id, success_status=['available'] ): LOG.error(_("ERROR: Share snapshot is in error state.")) share_snapshot = utils.find_resource( share_client.share_snapshots, share_snapshot.id) share_snapshot._info.pop('links', None) return self.dict2columns(share_snapshot._info) class DeleteShareSnapshot(command.Command): """Delete one or more share snapshots""" _description = _( "Delete one or more share snapshots") def get_parser(self, prog_name): parser = super(DeleteShareSnapshot, self).get_parser(prog_name) parser.add_argument( "snapshot", metavar="", nargs="+", help=_("Name or ID of the snapshot(s) to delete") ) parser.add_argument( "--force", action='store_true', default=False, help=_("Delete the snapshot(s) ignoring the current state.") ) parser.add_argument( "--wait", action='store_true', default=False, help=_("Wait for share snapshot deletion") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for snapshot in parsed_args.snapshot: try: snapshot_obj = utils.find_resource( share_client.share_snapshots, snapshot) if parsed_args.force: share_client.share_snapshots.force_delete( snapshot_obj) else: share_client.share_snapshots.delete( snapshot_obj) if parsed_args.wait: if not utils.wait_for_delete( manager=share_client.share_snapshots, res_id=snapshot_obj.id): result += 1 except Exception as e: result += 1 LOG.error(_( "Failed to delete snapshot with " "name or ID '%(snapshot)s': %(e)s"), {'snapshot': snapshot, 'e': e}) if result > 0: total = len(parsed_args.snapshot) msg = (_("%(result)s of %(total)s snapshots failed " "to delete.") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) class ShowShareSnapshot(command.ShowOne): """Display a share snapshot""" _description = _( "Show details about a share snapshot") def get_parser(self, prog_name): parser = super(ShowShareSnapshot, self).get_parser(prog_name) parser.add_argument( "snapshot", metavar="", help=_("Name or ID of the snapshot to display") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_snapshot = utils.find_resource( share_client.share_snapshots, parsed_args.snapshot) export_locations = ( share_client.share_snapshot_export_locations.list( share_snapshot)) locations = [] for location in export_locations: location._info.pop('links', None) locations.append(location._info) if parsed_args.formatter == 'table': locations = cliutils.convert_dict_list_to_string( locations) data = share_snapshot._info data['export_locations'] = locations # Special mapping for columns to make the output easier to read: # 'metadata' --> 'properties' data.update( { 'properties': format_columns.DictColumn(data.pop('metadata', {})), }, ) data.pop('links', None) return self.dict2columns(data) class SetShareSnapshot(command.Command): """Set share snapshot properties.""" _description = _("Set share snapshot properties") def get_parser(self, prog_name): parser = super(SetShareSnapshot, self).get_parser(prog_name) parser.add_argument( "snapshot", metavar="", help=_('Name or ID of the snapshot to set a property for') ) parser.add_argument( "--name", metavar="", default=None, help=_("Set a name to the snapshot.") ) parser.add_argument( "--description", metavar="", default=None, help=_("Set a description to the snapshot.") ) parser.add_argument( "--status", metavar="", choices=['available', 'error', 'creating', 'deleting', 'manage_starting', 'manage_error', 'unmanage_starting', 'unmanage_error', 'error_deleting'], help=_("Assign a status to the snapshot (Admin only). " "Options include : available, error, creating, " "deleting, manage_starting, manage_error, " "unmanage_starting, unmanage_error, error_deleting.") ) parser.add_argument( "--property", metavar="", default={}, action=parseractions.KeyValueAction, help=_("Set a property to this snapshot " "(repeat option to set multiple properties)"), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 share_snapshot = utils.find_resource( share_client.share_snapshots, parsed_args.snapshot) 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 try: share_client.share_snapshots.update( share_snapshot, **kwargs ) except Exception as e: result += 1 LOG.error(_( "Failed to set share snapshot properties " "'%(properties)s': %(exception)s"), {'properties': kwargs, 'exception': e}) if parsed_args.status: try: share_client.share_snapshots.reset_state( share_snapshot, parsed_args.status ) except Exception as e: result += 1 LOG.error(_( "Failed to update snapshot status to " "'%(status)s': %(e)s"), {'status': parsed_args.status, 'e': e}) if parsed_args.property: try: share_snapshot.set_metadata(parsed_args.property) except Exception as e: LOG.error(_("Failed to set share snapshot properties " "'%(properties)s': %(exception)s"), {'properties': parsed_args.property, 'exception': e}) result += 1 if result > 0: raise exceptions.CommandError(_("One or more of the " "set operations failed")) class UnsetShareSnapshot(command.Command): """Unset a share snapshot property.""" _description = _("Unset a share snapshot property") def get_parser(self, prog_name): parser = super(UnsetShareSnapshot, self).get_parser(prog_name) parser.add_argument( "snapshot", metavar="", help=_("Name or ID of the snapshot to set a property for") ) parser.add_argument( "--name", action='store_true', help=_("Unset snapshot name."), ) parser.add_argument( "--description", action='store_true', help=_("Unset snapshot description."), ) parser.add_argument( '--property', metavar='', action='append', help=_('Remove a property from snapshot ' '(repeat option to remove multiple properties)'), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_snapshot = utils.find_resource( share_client.share_snapshots, parsed_args.snapshot) kwargs = {} if parsed_args.name: kwargs['display_name'] = None if parsed_args.description: kwargs['display_description'] = None if kwargs: try: share_client.share_snapshots.update( share_snapshot, **kwargs ) except Exception as e: raise exceptions.CommandError(_( "Failed to unset snapshot display name " "or display description : %s" % e)) if parsed_args.property: for key in parsed_args.property: try: share_snapshot.delete_metadata([key]) except Exception as e: raise exceptions.CommandError(_( "Failed to unset snapshot property " "'%(key)s': %(e)s"), {'key': key, 'e': e}) class ListShareSnapshot(command.Lister): """List snapshots.""" _description = _("List snapshots") def get_parser(self, prog_name): parser = super(ListShareSnapshot, self).get_parser(prog_name) parser.add_argument( "--all-projects", action='store_true', default=False, help=_("Display snapshots from all projects (Admin only).") ) parser.add_argument( "--name", metavar="", default=None, help=_("Filter results by name.") ) parser.add_argument( '--description', metavar="", default=None, help=_("Filter results by description. Available only for " "microversion >= 2.36.") ) parser.add_argument( '--status', metavar="", default=None, help=_('Filter results by status') ) parser.add_argument( '--share', metavar='', default=None, help=_('Name or ID of a share to filter results by.') ) parser.add_argument( '--usage', metavar='', default=None, choices=['used', 'unused'], help=_("Option to filter snapshots by usage.") ) parser.add_argument( "--limit", metavar="", type=int, default=None, action=parseractions.NonNegativeAction, help=_("Limit the number of snapshots returned") ) parser.add_argument( "--marker", metavar="", help=_("The last share ID of the previous page") ) 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( "--name~", metavar="", default=None, help=_("Filter results matching a share snapshot name pattern. " "Available only for microversion >= 2.36.") ) parser.add_argument( '--description~', metavar="", default=None, help=_("Filter results matching a share snapshot description " "pattern. Available only for microversion >= 2.36.") ) parser.add_argument( '--detail', action='store_true', default=False, help=_("List share snapshots with details") ) parser.add_argument( '--property', metavar='', action=parseractions.KeyValueAction, help=_('Filter snapshots having a given metadata key=value ' 'property. (repeat option to filter by multiple ' 'properties)'), ) parser.add_argument( '--count', action='store_true', default=False, help=_("The total count of share snapshots before pagination is " "applied. This parameter is useful when applying " "pagination parameters '--limit' and '--offset'. Available " "only for microversion >= 2.79.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_id = None if parsed_args.share: share_id = utils.find_resource(share_client.shares, parsed_args.share).id columns = ['ID', 'Name'] search_opts = { 'offset': parsed_args.marker, 'limit': parsed_args.limit, 'all_tenants': parsed_args.all_projects, 'name': parsed_args.name, 'status': parsed_args.status, 'share_id': share_id, 'usage': parsed_args.usage, 'metadata': oscutils.extract_key_value_options( parsed_args.property), } if share_client.api_version >= api_versions.APIVersion("2.36"): search_opts['name~'] = getattr(parsed_args, 'name~') search_opts['description~'] = getattr(parsed_args, 'description~') search_opts['description'] = parsed_args.description elif (parsed_args.description or getattr(parsed_args, 'name~') or getattr(parsed_args, 'description~')): raise exceptions.CommandError( "Pattern based filtering (name~, description~ and description)" " is only available with manila API version >= 2.36") if parsed_args.count: if share_client.api_version < api_versions.APIVersion("2.79"): raise exceptions.CommandError( "Displaying total number of share snapshots is only " "available with manila API version >= 2.79") if parsed_args.formatter != 'table': raise exceptions.CommandError( "Count can only be printed when using '--format table'") if parsed_args.detail: columns.extend([ 'Status', 'Description', 'Created At', 'Size', 'Share ID', 'Share Proto', 'Share Size', 'User ID' ]) if parsed_args.all_projects: columns.append('Project ID') total_count = 0 if parsed_args.count: search_opts['with_count'] = True snapshots, total_count = share_client.share_snapshots.list( search_opts=search_opts) else: snapshots = share_client.share_snapshots.list( search_opts=search_opts) snapshots = utils.sort_items(snapshots, parsed_args.sort, str) if parsed_args.count: print("Total number of snapshots: %s" % total_count) return (columns, (utils.get_item_properties(s, columns) for s in snapshots)) class AdoptShareSnapshot(command.ShowOne): """Adopt a share snapshot not handled by Manila (Admin only).""" _description = _("Adopt a share snapshot") def get_parser(self, prog_name): parser = super(AdoptShareSnapshot, self).get_parser(prog_name) parser.add_argument( "share", metavar="", help=_("Name or ID of the share that owns the snapshot " "to be adopted.") ) parser.add_argument( "provider_location", metavar="", help=_("Provider location of the snapshot on the backend.") ) parser.add_argument( "--name", metavar="", default=None, help=_("Optional snapshot name (Default=None).") ) parser.add_argument( "--description", metavar="", default=None, help=_("Optional snapshot description (Default=None).") ) parser.add_argument( "--driver-option", metavar="", default={}, action=parseractions.KeyValueAction, help=_( "Set driver options as key=value pairs." "(repeat option to set multiple key=value pairs)") ) parser.add_argument( "--wait", action='store_true', help=_("Wait until share snapshot is adopted") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share = utils.find_resource(share_client.shares, parsed_args.share) snapshot = share_client.share_snapshots.manage( share=share, provider_location=parsed_args.provider_location, driver_options=parsed_args.driver_option, name=parsed_args.name, description=parsed_args.description ) if parsed_args.wait: if not utils.wait_for_status( status_f=share_client.share_snapshots.get, res_id=snapshot.id, success_status=['available'], error_status=['manage_error', 'error'] ): LOG.error(_("ERROR: Share snapshot is in error state.")) snapshot = utils.find_resource(share_client.share_snapshots, snapshot.id) snapshot._info.pop('links', None) return self.dict2columns(snapshot._info) class AbandonShareSnapshot(command.Command): """Abandon one or more share snapshots (Admin only).""" _description = _("Abandon share snapshot(s)") def get_parser(self, prog_name): parser = super(AbandonShareSnapshot, self).get_parser(prog_name) parser.add_argument( "snapshot", metavar="", nargs='+', help=_("Name or ID of the snapshot(s) to be abandoned.") ) parser.add_argument( "--wait", action='store_true', help=_("Wait until share snapshot is abandoned") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for snapshot in parsed_args.snapshot: snapshot_obj = utils.find_resource( share_client.share_snapshots, snapshot) try: share_client.share_snapshots.unmanage(snapshot_obj) if parsed_args.wait: if not utils.wait_for_delete( manager=share_client.share_snapshots, res_id=snapshot_obj.id): result += 1 except Exception as e: result += 1 LOG.error(_( "Failed to abandon share snapshot with " "name or ID '%(snapshot)s': %(e)s"), {'snapshot': snapshot, 'e': e}) if result > 0: total = len(parsed_args.snapshot) msg = (_("%(result)s of %(total)s snapshots failed " "to abandon.") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) class ShareSnapshotAccessAllow(command.ShowOne): """Allow read only access to a snapshot.""" _description = _("Allow access to a snapshot") def get_parser(self, prog_name): parser = super(ShareSnapshotAccessAllow, self).get_parser(prog_name) parser.add_argument( "snapshot", metavar="", help=_("Name or ID of the snapshot") ) 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.') ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share snapshot_obj = utils.find_resource( share_client.share_snapshots, parsed_args.snapshot) try: snapshot_access = share_client.share_snapshots.allow( snapshot=snapshot_obj, access_type=parsed_args.access_type, access_to=parsed_args.access_to ) return self.dict2columns(snapshot_access) except Exception as e: raise exceptions.CommandError( "Failed to create access to share snapshot " "'%s': %s" % (snapshot_obj, e)) class ShareSnapshotAccessDeny(command.Command): """Delete access to a snapshot""" _description = _( "Delete access to a snapshot") def get_parser(self, prog_name): parser = super(ShareSnapshotAccessDeny, self).get_parser(prog_name) parser.add_argument( "snapshot", metavar="", help=_("Name or ID of the share snapshot to deny access to.") ) parser.add_argument( "id", metavar="", nargs="+", help=_("ID(s) of the access rule(s) to be deleted.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 snapshot_obj = utils.find_resource( share_client.share_snapshots, parsed_args.snapshot) for access_id in parsed_args.id: try: share_client.share_snapshots.deny( snapshot=snapshot_obj, id=access_id ) except Exception as e: result += 1 LOG.error(_( "Failed to delete access to share snapshot " "for an access rule with ID '%(access)s': %(e)s"), {'access': access_id, 'e': e}) if result > 0: total = len(parsed_args.id) msg = (_("Failed to delete access to a share snapshot for " "%(result)s out of %(total)s access rule ID's ") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) class ShareSnapshotAccessList(command.Lister): """Show access list for a snapshot""" _description = _( "Show access list for a snapshot") def get_parser(self, prog_name): parser = super(ShareSnapshotAccessList, self).get_parser(prog_name) parser.add_argument( "snapshot", metavar="", help=_("Name or ID of the share snapshot to show access list for.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share snapshot_obj = utils.find_resource( share_client.share_snapshots, parsed_args.snapshot) access_rules = share_client.share_snapshots.access_list( snapshot_obj) columns = [ "ID", "Access Type", "Access To", "State" ] return (columns, (utils.get_item_properties(s, columns) for s in access_rules)) class ShareSnapshotListExportLocation(command.Lister): """List export locations of a given snapshot""" _description = _( "List export locations of a given snapshot") def get_parser(self, prog_name): parser = super(ShareSnapshotListExportLocation, self).get_parser( prog_name) parser.add_argument( "snapshot", metavar="", help=_("Name or ID of the share snapshot.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share snapshot_obj = utils.find_resource( share_client.share_snapshots, parsed_args.snapshot) export_locations = share_client.share_snapshot_export_locations.list( snapshot=snapshot_obj) columns = ["ID", "Path"] return ( columns, (utils.get_item_properties(s, columns) for s in export_locations)) class ShareSnapshotShowExportLocation(command.ShowOne): """Show export location of the share snapshot""" _description = _( "Show export location of the share snapshot") def get_parser(self, prog_name): parser = super(ShareSnapshotShowExportLocation, self).get_parser( prog_name) parser.add_argument( "snapshot", metavar="", help=_("Name or ID of the share snapshot.") ) parser.add_argument( "export_location", metavar="", help=_("ID of the share snapshot export location.") ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share snapshot_obj = utils.find_resource( share_client.share_snapshots, parsed_args.snapshot) export_location = share_client.share_snapshot_export_locations.get( export_location=parsed_args.export_location, snapshot=snapshot_obj) return self.dict2columns(export_location._info) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_transfers.py0000664000175000017500000002171500000000000025051 0ustar00zuulzuul00000000000000# 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 osc_utils from manilaclient.common._i18n import _ from manilaclient.common.apiclient import utils as apiutils from manilaclient.common import constants LOG = logging.getLogger(__name__) TRANSFER_DETAIL_ATTRIBUTES = [ 'id', 'created_at', 'name', 'resource_type', 'resource_id', 'source_project_id', 'destination_project_id', 'accepted', 'expires_at' ] TRANSFER_SUMMARY_ATTRIBUTES = [ 'id', 'name', 'resource_type', 'resource_id', ] class CreateShareTransfer(command.ShowOne): """Create a new share transfer.""" _description = _("Create a new share transfer") def get_parser(self, prog_name): parser = super(CreateShareTransfer, self).get_parser(prog_name) parser.add_argument( 'share', metavar='', help='Name or ID of share to transfer.') parser.add_argument( '--name', metavar='', default=None, help='Transfer name. Default=None.') return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share = osc_utils.find_resource(share_client.shares, parsed_args.share) transfer = share_client.transfers.create( share.id, name=parsed_args.name) transfer._info.pop('links', None) return self.dict2columns(transfer._info) class DeleteShareTransfer(command.Command): """Remove one or more transfers.""" _description = _("Remove one or more transfers") def get_parser(self, prog_name): parser = super(DeleteShareTransfer, self).get_parser(prog_name) parser.add_argument( 'transfer', metavar='', nargs='+', help='Name(s) or ID(s) of the transfer(s).') return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share failure_count = 0 for transfer in parsed_args.transfer: try: transfer_obj = apiutils.find_resource( share_client.transfers, transfer) share_client.transfers.delete(transfer_obj.id) except Exception as e: failure_count += 1 LOG.error(_( "Failed to delete %(transfer)s: %(e)s"), {'transfer': transfer, 'e': e}) if failure_count > 0: raise exceptions.CommandError(_( "Unable to delete some or all of the specified transfers.")) class ListShareTransfer(command.Lister): """Lists all transfers.""" _description = _("Lists all transfers") def get_parser(self, prog_name): parser = super(ListShareTransfer, self).get_parser(prog_name) parser.add_argument( '--all-projects', action='store_true', help=_("Shows details for all tenants. (Admin only).") ) parser.add_argument( '--name', metavar='', default=None, help='Filter share transfers by name. Default=None.') parser.add_argument( '--id', metavar='', default=None, help='Filter share transfers by ID. Default=None.') parser.add_argument( '--resource-type', '--resource_type', metavar='', default=None, help='Filter share transfers by resource type, ' 'which can be share. Default=None.') parser.add_argument( '--resource-id', '--resource_id', metavar='', default=None, help='Filter share transfers by resource ID. Default=None.') parser.add_argument( '--source-project-id', '--source_project_id', metavar='', default=None, help='Filter share transfers by ID of the Project that ' 'initiated the transfer. Default=None.') parser.add_argument( '--limit', metavar='', type=int, default=None, help='Maximum number of transfer records to ' 'return. (Default=None)') parser.add_argument( '--offset', metavar="", default=None, help='Start position of transfer records listing.') parser.add_argument( '--sort-key', '--sort_key', metavar='', type=str, default=None, help='Key to be sorted, available keys are %(keys)s. ' 'Default=None.' % {'keys': constants.SHARE_TRANSFER_SORT_KEY_VALUES}) parser.add_argument( '--sort-dir', '--sort_dir', metavar='', type=str, default=None, help='Sort direction, available values are %(values)s. ' 'OPTIONAL: Default=None.' % { 'values': constants.SORT_DIR_VALUES}) parser.add_argument( '--detailed', dest='detailed', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help="Show detailed information about filtered share transfers.") return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share columns = [ 'ID', 'Name', 'Resource Type', 'Resource Id' ] if parsed_args.detailed: columns.extend(['Created At', 'Source Project Id', 'Destination Project Id', 'Accepted', 'Expires At']) search_opts = { 'all_tenants': parsed_args.all_projects, 'id': parsed_args.id, 'name': parsed_args.name, 'limit': parsed_args.limit, 'offset': parsed_args.offset, 'resource_type': parsed_args.resource_type, 'resource_id': parsed_args.resource_id, 'source_project_id': parsed_args.source_project_id, } transfers = share_client.transfers.list( detailed=parsed_args.detailed, search_opts=search_opts, sort_key=parsed_args.sort_key, sort_dir=parsed_args.sort_dir) return (columns, (osc_utils.get_item_properties (m, columns) for m in transfers)) class ShowShareTransfer(command.ShowOne): """Show details about a share transfer.""" _description = _("Show details about a share transfer") def get_parser(self, prog_name): parser = super(ShowShareTransfer, self).get_parser(prog_name) parser.add_argument( 'transfer', metavar='', help=_('Name or ID of transfer to show.')) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share transfer = apiutils.find_resource( share_client.transfers, parsed_args.transfer) return (TRANSFER_DETAIL_ATTRIBUTES, osc_utils.get_dict_properties( transfer._info, TRANSFER_DETAIL_ATTRIBUTES)) class AcceptShareTransfer(command.Command): """Accepts a share transfer.""" _description = _("Accepts a share transfer") def get_parser(self, prog_name): parser = super(AcceptShareTransfer, self).get_parser(prog_name) parser.add_argument( 'transfer', metavar='', help='ID of transfer to accept.') parser.add_argument( 'auth_key', metavar='', help='Authentication key of transfer to accept.') parser.add_argument( '--clear-rules', '--clear_rules', dest='clear_rules', action='store_true', default=False, help="Whether manila should clean up the access rules after the " "transfer is complete. (Default=False)") return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_client.transfers.accept( parsed_args.transfer, parsed_args.auth_key, clear_access_rules=parsed_args.clear_rules) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_type_access.py0000664000175000017500000000760700000000000025350 0ustar00zuulzuul00000000000000# 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.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 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/osc/v2/share_types.py0000664000175000017500000003737200000000000024214 0ustar00zuulzuul00000000000000# 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 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, formatter='table'): # 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) if formatter == 'table': 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), } ) else: share_type._info.update( { 'visibility': visibility, 'required_extra_specs': share_type.required_extra_specs, 'optional_extra_specs': optional_extra_specs, } ) return share_type class CreateShareType(command.ShowOne): """Create new share type.""" _description = _( "Create new share type") 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" % str(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, parsed_args.formatter) return (ATTRIBUTES, oscutils.get_dict_properties( formatted_type._info, ATTRIBUTES)) class DeleteShareType(command.Command): """Delete a share type.""" _description = _("Delete a share type") 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") 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") 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") 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.'), ) 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) formatted_types = [] for share_type in share_types: formatted_types.append(format_share_type(share_type, parsed_args.formatter)) values = (oscutils.get_dict_properties( s._info, ATTRIBUTES) for s in formatted_types) columns = utils.format_column_headers(ATTRIBUTES) 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, parsed_args.formatter) return (ATTRIBUTES, oscutils.get_dict_properties( formatted_type._info, ATTRIBUTES)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/shell.py0000664000175000017500000007714600000000000021665 0ustar00zuulzuul00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2014 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Command-line interface to the OpenStack Manila API. """ import argparse import csv import glob from importlib import util as importlib_util import itertools import logging import os import pkgutil import sys from oslo_utils import importutils from manilaclient import api_versions from manilaclient import client from manilaclient.common import cliutils from manilaclient.common import constants from manilaclient import exceptions as exc import manilaclient.extension from manilaclient.v2 import shell as shell_v2 DEFAULT_OS_SHARE_API_VERSION = api_versions.MAX_VERSION DEFAULT_MANILA_ENDPOINT_TYPE = 'publicURL' DEFAULT_MAJOR_OS_SHARE_API_VERSION = "2" V1_MAJOR_VERSION = '1' V2_MAJOR_VERSION = '2' try: osprofiler_profiler = importutils.try_import("osprofiler.profiler") except Exception: pass logger = logging.getLogger(__name__) class AllowOnlyOneAliasAtATimeAction(argparse.Action): """Allows only one alias of argument to be used at a time.""" def __call__(self, parser, namespace, values, option_string=None): # NOTE(vponomaryov): this method is redefinition of # argparse.Action.__call__ interface if not hasattr(self, 'calls'): self.calls = {} if self.dest not in self.calls: self.calls[self.dest] = set() local_values = sorted(values) if isinstance(values, list) else values self.calls[self.dest].add(str(local_values)) if len(self.calls[self.dest]) == 1: setattr(namespace, self.dest, local_values) else: msg = "Only one alias is allowed at a time." raise argparse.ArgumentError(self, msg) class ManilaClientArgumentParser(argparse.ArgumentParser): def __init__(self, *args, **kwargs): super(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) parser.add_argument('--os-key', metavar='', default=cliutils.env('OS_KEY'), help='Defaults to env[OS_KEY].') parser.add_argument('--os_key', help=argparse.SUPPRESS) if osprofiler_profiler: parser.add_argument('--profile', metavar='HMAC_KEY', default=cliutils.env('OS_PROFILE'), help='HMAC key to use for encrypting ' 'context data for performance profiling ' 'of operation. This key needs to match the ' 'one configured on the manila api server. ' 'Without key the profiling will not be ' 'triggered even if osprofiler is enabled ' 'on server side. Defaults to ' 'env[OS_PROFILE].') parser.set_defaults(func=self.do_help) parser.set_defaults(command='') return parser def get_subcommand_parser(self, version): parser = self.get_base_parser() self.subcommands = {} subparsers = parser.add_subparsers(metavar='') try: actions_module = { V2_MAJOR_VERSION: shell_v2, }[version] except KeyError: actions_module = shell_v2 self._find_actions(subparsers, actions_module) self._find_actions(subparsers, self) for extension in self.extensions: self._find_actions(subparsers, extension.module) self._add_bash_completion_subparser(subparsers) return parser def _discover_extensions(self, api_version): extensions = [] for name, module in itertools.chain( self._discover_via_python_path(), self._discover_via_contrib_path(api_version)): extension = manilaclient.extension.Extension(name, module) extensions.append(extension) return extensions def _discover_via_python_path(self): for (module_loader, name, ispkg) in pkgutil.iter_modules(): if name.endswith('python_manilaclient_ext'): if not hasattr(module_loader, 'load_module'): # Python 2.6 compat: actually get an ImpImporter obj module_loader = module_loader.find_module(name) module = module_loader.load_module(name) yield name, module def _load_module(self, name, path): module_spec = importlib_util.spec_from_file_location( name, path ) module = importlib_util.module_from_spec(module_spec) module_spec.loader.exec_module(module) return module def _discover_via_contrib_path(self, api_version): module_path = os.path.dirname(os.path.abspath(__file__)) version_str = 'v' + api_version.get_major_version() ext_path = os.path.join(module_path, version_str, 'contrib') ext_glob = os.path.join(ext_path, "*.py") for ext_path in glob.iglob(ext_glob): name = os.path.basename(ext_path)[:-3] if name == "__init__": continue module = self._load_module(name, ext_path) yield name, module def _add_bash_completion_subparser(self, subparsers): subparser = subparsers.add_parser( 'bash_completion', add_help=False, formatter_class=OpenStackHelpFormatter) self.subcommands['bash_completion'] = subparser subparser.set_defaults(func=self.do_bash_completion) def _find_actions(self, subparsers, actions_module): for attr in (a for a in dir(actions_module) if a.startswith('do_')): # I prefer to be hypen-separated instead of underscores. command = attr[3:].replace('_', '-') callback = getattr(actions_module, attr) desc = callback.__doc__ or '' help = desc.strip() arguments = getattr(callback, 'arguments', []) subparser = subparsers.add_parser( command, help=help, description=desc, add_help=False, formatter_class=OpenStackHelpFormatter) subparser.add_argument('-h', '--help', action='help', help=argparse.SUPPRESS,) self.subcommands[command] = subparser for (args, kwargs) in arguments: subparser.add_argument(*args, **kwargs) subparser.set_defaults(func=callback) def setup_debugging(self, debug): if not debug: return streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s" logging.basicConfig(level=logging.DEBUG, format=streamformat) logging.getLogger('requests.packages.urllib3.connectionpool' ).setLevel(logging.WARNING) logging.getLogger('keystoneauth1.session').setLevel(logging.WARNING) def _build_subcommands_and_extensions(self, os_api_version, argv, options): self.extensions = self._discover_extensions(os_api_version) self._run_extension_hooks('__pre_parse_args__') self.parser = self.get_subcommand_parser( os_api_version.get_major_version()) if argv and len(argv) > 1 and '--help' in argv: argv = [x for x in argv if x != '--help'] if argv[0] in self.subcommands: self.subcommands[argv[0]].print_help() return False if options.help or not argv: self.parser.print_help() return False args = self.parser.parse_args(argv) self._run_extension_hooks('__post_parse_args__', args) return args def main(self, argv): # Parse args once to find version and debug settings parser = self.get_base_parser() (options, args) = parser.parse_known_args(argv) self.setup_debugging(options.debug) os_api_version = self._validate_input_api_version(options) # build available subcommands based on version args = self._build_subcommands_and_extensions(os_api_version, argv, options) if not args: return 0 # Short-circuit and deal with help right away. if args.func == self.do_help: self.do_help(args) return 0 elif args.func == self.do_bash_completion: self.do_bash_completion(args) return 0 if not options.os_share_api_version: api_version = api_versions.get_api_version( DEFAULT_MAJOR_OS_SHARE_API_VERSION) else: api_version = api_versions.get_api_version( options.os_share_api_version) major_version_string = str(api_version.ver_major) os_service_type = args.service_type if not os_service_type: os_service_type = constants.SERVICE_TYPES[major_version_string] os_endpoint_type = args.endpoint_type or DEFAULT_MANILA_ENDPOINT_TYPE cert = args.os_cert or None if cert and args.os_key: cert = cert, args.os_key client_args = dict( username=args.os_username, password=args.os_password, project_name=args.os_project_name or args.os_tenant_name, auth_url=args.os_auth_url, insecure=args.insecure, region_name=args.os_region_name, tenant_id=args.os_project_id or args.os_tenant_id, endpoint_type=os_endpoint_type, extensions=self.extensions, service_type=os_service_type, service_name=args.service_name, retries=options.retries, http_log_debug=args.debug, cacert=args.os_cacert, use_keyring=args.os_cache, force_new_token=args.os_reset_cache, user_id=args.os_user_id, user_domain_id=args.os_user_domain_id, user_domain_name=args.os_user_domain_name, project_domain_id=args.os_project_domain_id, project_domain_name=args.os_project_domain_name, cert=cert, input_auth_token=args.os_token, service_catalog_url=args.bypass_url, ) # Handle deprecated parameters if args.share_service_name: client_args['share_service_name'] = args.share_service_name self._validate_required_options( args.os_tenant_name, args.os_tenant_id, args.os_project_name, args.os_project_id, args.os_token, args.bypass_url, client_args['auth_url']) # This client is needed to discover the server api version. temp_client = client.Client(manilaclient.API_MAX_VERSION, **client_args) self.cs, discovered_version = self._discover_client(temp_client, os_api_version, os_endpoint_type, os_service_type, client_args) args = self._build_subcommands_and_extensions(discovered_version, argv, options) profile = osprofiler_profiler and options.profile if profile: osprofiler_profiler.init(options.profile) try: decoder_path = ( '%s/%s' % (os.path.dirname(os.path.abspath(__file__)), 'osc/v2/data/manila.csv') ) with open(decoder_path) as f: decoder_data = { r['manila command']: r['openstack command'] for r in csv.DictReader(f, skipinitialspace=True) } except Exception: # this is fine decoder_data = {} deprecation_message = ("manila CLI is deprecated and will be removed " "in the future. Use openstack CLI instead.") cmd = args.func.__name__.lstrip('do_').replace("_", "-") if decoder_data and cmd in decoder_data: deprecation_message = " ".join([ deprecation_message, "The equivalent command is \" openstack", f"{decoder_data[cmd]}", "\"" ]) print(deprecation_message, file=sys.stderr) args.func(self.cs, args) if profile: trace_id = osprofiler_profiler.get().get_base_id() print("Profiling trace ID: %s" % trace_id) print("To display trace use next command:\n" "osprofiler trace show --html %s " % trace_id) def _discover_client(self, current_client, os_api_version, os_endpoint_type, os_service_type, client_args): if os_api_version == manilaclient.API_DEPRECATED_VERSION: discovered_version = manilaclient.API_DEPRECATED_VERSION os_service_type = constants.V1_SERVICE_TYPE else: discovered_version = api_versions.discover_version( current_client, os_api_version ) if not os_endpoint_type: os_endpoint_type = DEFAULT_MANILA_ENDPOINT_TYPE if not os_service_type: os_service_type = self._discover_service_type(discovered_version) if (discovered_version != manilaclient.API_MAX_VERSION or os_service_type != constants.V1_SERVICE_TYPE or os_endpoint_type != DEFAULT_MANILA_ENDPOINT_TYPE): client_args['version'] = discovered_version client_args['service_type'] = os_service_type client_args['endpoint_type'] = os_endpoint_type return (client.Client(discovered_version, **client_args), discovered_version) else: return current_client, discovered_version def _discover_service_type(self, discovered_version): major_version = discovered_version.get_major_version() service_type = constants.SERVICE_TYPES[major_version] return service_type def _validate_input_api_version(self, options): if not options.os_share_api_version: api_version = manilaclient.API_MAX_VERSION else: api_version = api_versions.get_api_version( options.os_share_api_version) return api_version def _validate_required_options(self, tenant_name, tenant_id, project_name, project_id, token, service_catalog_url, auth_url): if token and not service_catalog_url: raise exc.CommandError( "bypass_url missing: When specifying a token the bypass_url " "must be set via --bypass-url or env[OS_MANILA_BYPASS_URL]") if service_catalog_url and not token: raise exc.CommandError( "Token missing: When specifying a bypass_url a token must be " "set via --os-token or env[OS_TOKEN]") if token and service_catalog_url: return if not (tenant_name or tenant_id or project_name or project_id): raise exc.CommandError( "You must provide a tenant_name, tenant_id, " "project_id or project_name (with " "project_domain_name or project_domain_id) via " "--os-tenant-name or env[OS_TENANT_NAME], " "--os-tenant-id or env[OS_TENANT_ID], " "--os-project-id or env[OS_PROJECT_ID], " "--os-project-name or env[OS_PROJECT_NAME], " "--os-project-domain-id or env[OS_PROJECT_DOMAIN_ID] and " "--os-project-domain-name or env[OS_PROJECT_DOMAIN_NAME]." ) if not auth_url: raise exc.CommandError( "You must provide an auth url " "via either --os-auth-url or env[OS_AUTH_URL]") def _run_extension_hooks(self, hook_type, *args, **kwargs): """Run hooks for all registered extensions.""" for extension in self.extensions: extension.run_hooks(hook_type, *args, **kwargs) def do_bash_completion(self, args): """Print arguments for bash_completion. Prints all of the commands and options to stdout so that the manila.bash_completion script doesn't have to hard code them. """ commands = set() options = set() for sc_str, sc in list(self.subcommands.items()): commands.add(sc_str) for option in sc._optionals._option_string_actions: options.add(option) commands.remove('bash-completion') commands.remove('bash_completion') print(' '.join(commands | options)) @cliutils.arg('command', metavar='', nargs='?', help='Display help for ') def do_help(self, args): """Display help about this program or one of its subcommands.""" if args.command: if args.command in self.subcommands: self.subcommands[args.command].print_help() else: raise exc.CommandError("'%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: OpenStackManilaShell().main(sys.argv[1:]) except KeyboardInterrupt: print("... terminating manila client", file=sys.stderr) sys.exit(130) except Exception as e: logger.debug(e, exc_info=1) print("ERROR: %s" % str(e), file=sys.stderr) sys.exit(1) if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5652697 python-manilaclient-4.8.0/manilaclient/tests/0000775000175000017500000000000000000000000021327 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/__init__.py0000664000175000017500000000000000000000000023426 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5732718 python-manilaclient-4.8.0/manilaclient/tests/functional/0000775000175000017500000000000000000000000023471 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/__init__.py0000664000175000017500000000000000000000000025570 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/base.py0000664000175000017500000005176400000000000024772 0ustar00zuulzuul00000000000000# 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, use_wait_option=False, cleanup_in_class=False, wait_for_creation=True, microversion=None): client = client or cls.get_admin_client() data = { 'share_protocol': share_protocol or client.share_protocol, 'size': size or 1, 'name': name, 'description': description, 'public': public, 'snapshot': snapshot, 'metadata': metadata or {}, 'microversion': microversion, 'wait': use_wait_option, } share_type = share_type or CONF.share_type share_network = share_network or cls._determine_share_network_to_use( client, share_type, microversion=microversion) data['share_type'] = share_type data['share_network'] = share_network share = client.create_share(**data) resource = { "type": "share", "id": share["id"], "client": client, "microversion": microversion, } if cleanup_in_class: cls.class_resources.insert(0, resource) else: cls.method_resources.insert(0, resource) if wait_for_creation and not use_wait_option: client.wait_for_resource_status(share['id'], constants.STATUS_AVAILABLE) return share @classmethod def delete_share(cls, shares_to_delete, share_group_id=None, wait=False, client=None, microversion=None): client = client or cls.get_admin_client() client.delete_share(shares_to_delete, share_group_id=share_group_id, wait=wait, microversion=microversion) @classmethod def soft_delete_share(cls, shares_to_soft_delete, client=None, microversion=None): client = client or cls.get_admin_client() client.soft_delete_share(shares_to_soft_delete, microversion=microversion) @classmethod def restore_share(cls, shares_to_restore, client=None, microversion=None): client = client or cls.get_admin_client() client.restore_share(shares_to_restore, microversion=microversion) @classmethod def create_share_transfer(cls, share_id, name=None, client=None, microversion=None): client = client or cls.get_admin_client() return client.create_share_transfer(share_id, name=name, microversion=microversion) @classmethod def delete_share_transfer(cls, transfer, client=None, microversion=None): client = client or cls.get_admin_client() client.delete_share_transfer(transfer, microversion=microversion) @classmethod def get_share_transfer(cls, transfer, client=None, microversion=None): client = client or cls.get_admin_client() return client.get_share_transfer(transfer, microversion=microversion) @classmethod def list_share_transfer(cls, client=None, microversion=None): client = client or cls.get_admin_client() return client.list_share_transfer(microversion=microversion) @classmethod def accept_share_transfer(cls, transfer, auth_key, client=None, microversion=None): client = client or cls.get_admin_client() client.accept_share_transfer(transfer, auth_key, microversion=microversion) @classmethod def _determine_share_network_to_use(cls, client, share_type, microversion=None): """Determine what share network we need from the share type.""" # Get share type, determine if we need the share network share_type = client.get_share_type(share_type, microversion=microversion) dhss_pattern = re.compile('driver_handles_share_servers : ([a-zA-Z]+)') dhss = dhss_pattern.search(share_type['required_extra_specs']).group(1) return client.share_network if dhss.lower() == 'true' else None @classmethod def create_security_service(cls, type='ldap', name=None, description=None, dns_ip=None, ou=None, server=None, domain=None, user=None, password=None, default_ad_site=None, client=None, cleanup_in_class=False, microversion=None): if client is None: client = cls.get_admin_client() data = { 'type': type, 'name': name, 'description': description, 'user': user, 'password': password, 'server': server, 'domain': domain, 'dns_ip': dns_ip, 'ou': ou, 'microversion': microversion, 'default_ad_site': default_ad_site, } ss = client.create_security_service(**data) resource = { "type": "share", "id": ss["id"], "client": client, "microversion": microversion, } if cleanup_in_class: cls.class_resources.insert(0, resource) else: cls.method_resources.insert(0, resource) return ss @classmethod def create_snapshot(cls, share, name=None, description=None, force=False, client=None, wait_for_creation=True, cleanup_in_class=False, microversion=None): if client is None: client = cls.get_admin_client() data = { 'share': share, 'name': name, 'description': description, 'force': force, 'microversion': microversion, } snapshot = client.create_snapshot(**data) resource = { "type": "snapshot", "id": snapshot["id"], "client": client, "microversion": microversion, } if cleanup_in_class: cls.class_resources.insert(0, resource) else: cls.method_resources.insert(0, resource) if wait_for_creation: client.wait_for_snapshot_status(snapshot['id'], 'available') return snapshot @classmethod def create_message(cls, client=None, wait_for_creation=True, cleanup_in_class=False, microversion=None): """Trigger a 'no valid host' situation to generate a message.""" if client is None: client = cls.get_admin_client() extra_specs = { 'vendor_name': 'foobar', } share_type_name = data_utils.rand_name("share-type") cls.create_share_type( name=share_type_name, extra_specs=extra_specs, driver_handles_share_servers=False, client=client, cleanup_in_class=cleanup_in_class, microversion=microversion) share_name = data_utils.rand_name("share") share = cls.create_share( name=share_name, share_type=share_type_name, cleanup_in_class=cleanup_in_class, microversion=microversion, wait_for_creation=False, client=client) client.wait_for_resource_status(share['id'], constants.STATUS_ERROR) message = client.wait_for_message(share['id']) resource = { "type": "message", "id": message["ID"], "client": client, "microversion": microversion, } if cleanup_in_class: cls.class_resources.insert(0, resource) else: cls.method_resources.insert(0, resource) return message @classmethod def create_share_replica(cls, share_id, client=None, wait_for_creation=True, cleanup_in_class=False, availability_zone=None, share_network=None, microversion=None): client = client or cls.get_user_client() share_replica = client.create_share_replica( share_id, availability_zone=availability_zone, share_network=share_network, microversion=microversion) if wait_for_creation: share_replica = client.wait_for_share_replica_status( share_replica['id']) resource = { "type": "share_replica", "id": share_replica["id"], "client": client, "microversion": microversion, } if cleanup_in_class: cls.class_resources.insert(0, resource) else: cls.method_resources.insert(0, resource) return share_replica ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/client.py0000664000175000017500000025772100000000000025337 0ustar00zuulzuul00000000000000# Copyright 2014 Mirantis Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import ast import re import time from oslo_utils import strutils from tempest.lib.cli import base from tempest.lib.cli import output_parser from tempest.lib.common.utils import data_utils from tempest.lib import exceptions as tempest_lib_exc from manilaclient.common import constants from manilaclient import config from manilaclient.tests.functional import exceptions from manilaclient.tests.functional import utils CONF = config.CONF MESSAGE = 'message' SHARE = 'share' SHARE_TYPE = 'share_type' SHARE_NETWORK = 'share_network' SHARE_NETWORK_SUBNET = 'share_network_subnet' SHARE_SERVER = 'share_server' SNAPSHOT = 'snapshot' SHARE_REPLICA = 'share_replica' TRANSFER = 'transfer' def not_found_wrapper(f): def wrapped_func(self, *args, **kwargs): try: return f(self, *args, **kwargs) except tempest_lib_exc.CommandFailed as e: for regexp in (r'No (\w+) with a name or ID', r'not(.*){0,5}found'): if re.search(regexp, str(e.stderr)): # Raise appropriate 'NotFound' error raise tempest_lib_exc.NotFound() raise return wrapped_func def forbidden_wrapper(f): def wrapped_func(self, *args, **kwargs): try: return f(self, *args, **kwargs) except tempest_lib_exc.CommandFailed as e: if re.search('HTTP 403', str(e.stderr)): # Raise appropriate 'Forbidden' error. raise tempest_lib_exc.Forbidden() raise return wrapped_func class ManilaCLIClient(base.CLIClient): def __init__(self, *args, **kwargs): super(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 elif res_type == TRANSFER: func = self.is_share_transfer_deleted else: raise exceptions.InvalidResource(message=res_type) end_loop_time = time.time() + timeout deleted = func(res_id, microversion=microversion, **kwargs) while not (deleted or time.time() > end_loop_time): time.sleep(interval) deleted = func(res_id, microversion=microversion, **kwargs) if not deleted: raise exceptions.ResourceReleaseFailed( res_type=res_type, res_id=res_id) def list_availability_zones(self, columns=None, microversion=None): """List availability zones. :param columns: comma separated string of columns. Example, "--columns id,name" :param microversion: API microversion that should be used. """ cmd = 'availability-zone-list' if columns is not None: cmd += ' --columns ' + columns azs_raw = self.manila(cmd, microversion=microversion) azs = output_parser.listing(azs_raw) return azs # Share types def create_share_type(self, name=None, driver_handles_share_servers=True, snapshot_support=None, create_share_from_snapshot=None, revert_to_snapshot=None, mount_snapshot=None, is_public=True, microversion=None, extra_specs=None, description=None): """Creates share type. :param name: text -- name of share type to use, if not set then autogenerated will be used :param description: text -- description of share type to use. Default is None. :param driver_handles_share_servers: bool/str -- boolean or its string alias. Default is True. :param snapshot_support: bool/str -- boolean or its string alias. Default is None. :param is_public: bool/str -- boolean or its string alias. Default is True. :param extra_specs: -- dictionary of extra specs Default is None. :param create_share_from_snapshot: -- boolean or its string alias. Default is None. :param revert_to_snapshot: -- boolean or its string alias. Default is None. :param mount_snapshot: -- boolean or its string alias. Default is None. """ if name is None: name = data_utils.rand_name('manilaclient_functional_test') dhss = driver_handles_share_servers if not isinstance(dhss, str): dhss = str(dhss) if not isinstance(is_public, str): is_public = str(is_public) cmd = ('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, str): snapshot_support = str(snapshot_support) cmd += " --snapshot-support " + snapshot_support if create_share_from_snapshot is not None: if not isinstance(create_share_from_snapshot, str): create_share_from_snapshot = str(create_share_from_snapshot) cmd += (" --create-share-from-snapshot-support " + create_share_from_snapshot) if revert_to_snapshot is not None: if not isinstance(revert_to_snapshot, str): revert_to_snapshot = str(revert_to_snapshot) cmd += (" --revert-to-snapshot-support " + revert_to_snapshot) if mount_snapshot is not None: if not isinstance(mount_snapshot, str): mount_snapshot = str(mount_snapshot) cmd += (" --mount-snapshot-support " + mount_snapshot) if extra_specs is not None: extra_spec_str = '' for k, v in extra_specs.items(): if not isinstance(v, str): extra_specs[k] = str(v) extra_spec_str += "{}='{}' ".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, str): is_public = str(is_public) cmd += " --is_public " + is_public if description: cmd += " --description " + description elif description == "": cmd += ' --description "" ' if name: cmd += " --name " + name share_type_raw = self.manila(cmd, microversion=microversion) share_type = utils.details(share_type_raw) return share_type @not_found_wrapper def delete_share_type(self, share_type, microversion=None): """Deletes share type by its Name or ID.""" return self.manila( '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, str): raise exceptions.InvalidData( 'Provided wrong parameter for translation.') while not param[0:2] == '--': param = '-' + param return param.replace('_', '-') def list_share_networks(self, all_tenants=False, filters=None, columns=None, microversion=None): """List share networks. :param all_tenants: bool -- whether to list share-networks that belong only to current project or for all tenants. :param filters: dict -- filters for listing of share networks. Example, input: {'project_id': 'foo'} {'-project_id': 'foo'} {'--project_id': 'foo'} {'project-id': 'foo'} will be transformed to filter parameter "--project-id=foo" :param columns: comma separated string of columns. Example, "--columns id" """ cmd = 'share-network-list ' if columns is not None: cmd += ' --columns ' + columns if all_tenants: cmd += ' --all-tenants ' if filters and isinstance(filters, dict): for k, v in filters.items(): cmd += '%(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 share_network_reset_state(self, id=None, state=None, microversion=None): cmd = 'share-network-reset-state %s ' % id if state: cmd += '--state %s' % state share_network_raw = self.manila(cmd, microversion=microversion) share_network = utils.listing(share_network_raw) return share_network def share_network_security_service_add( self, share_network_id, security_service_id, microversion=None): cmd = ('share-network-security-service-add %(network_id)s ' '%(service_id)s' % {'network_id': share_network_id, 'service_id': security_service_id}) self.manila(cmd, microversion=microversion) def share_network_security_service_update( self, share_network_id, current_security_service_id, new_security_service_id, microversion=None): cmd = ( 'share-network-security-service-update %(network_id)s ' '%(current_service_id)s %(new_security_service_id)s' % { 'network_id': share_network_id, 'current_service_id': current_security_service_id, 'new_security_service_id': new_security_service_id}) self.manila(cmd, microversion=microversion) def share_network_security_service_add_check( self, share_network_id, security_service_id, reset=False, microversion=None): cmd = ( 'share-network-security-service-add-check %(network_id)s ' '%(security_service_id)s' % { 'network_id': share_network_id, 'security_service_id': security_service_id}) if reset: cmd += '--reset %s' % reset return output_parser.details( self.manila(cmd, microversion=microversion)) def share_network_security_service_update_check( self, share_network_id, current_security_service_id, new_security_service_id, reset=False, microversion=None): cmd = ( 'share-network-security-service-update-check %(network_id)s ' '%(current_security_service_id)s %(new_security_service_id)s ' % { 'network_id': share_network_id, 'current_security_service_id': current_security_service_id, 'new_security_service_id': new_security_service_id}) if reset: cmd += '--reset %s' % reset return output_parser.details( self.manila(cmd, microversion=microversion)) def share_network_security_service_list( self, share_network_id, microversion=None): cmd = ('share-network-security-service-list %s' % share_network_id) share_networks_raw = self.manila(cmd, microversion=microversion) network_services = utils.listing(share_networks_raw) return network_services def is_share_network_deleted(self, share_network, microversion=None): """Says whether share network is deleted or not. :param share_network: text -- Name or ID of share network """ share_types = self.list_share_networks(True, microversion=microversion) for list_element in share_types: if share_network in (list_element['id'], list_element['name']): return False return True def wait_for_share_network_deletion(self, share_network, microversion=None): """Wait for share network deletion by its Name or ID. :param share_network: text -- Name or ID of share network """ self.wait_for_resource_deletion( SHARE_NETWORK, res_id=share_network, interval=2, timeout=6, microversion=microversion) def share_network_subnet_create_check( self, share_network_id, neutron_net_id=None, neutron_subnet_id=None, availability_zone=None, reset=False, microversion=None): params = self._combine_share_network_subnet_data( neutron_net_id=neutron_net_id, neutron_subnet_id=neutron_subnet_id, availability_zone=availability_zone) cmd = ( 'share-network-subnet-create-check %(network_id)s ' '%(params)s' % { 'network_id': share_network_id, 'params': params}) if reset: cmd += '--reset %s' % reset return output_parser.details( self.manila(cmd, microversion=microversion)) # Share Network Subnets def _combine_share_network_subnet_data(self, neutron_net_id=None, neutron_subnet_id=None, availability_zone=None): """Combines params for share network subnet 'create' operation. :returns: text -- set of CLI parameters """ data = dict() if neutron_net_id is not None: data['--neutron_net_id'] = neutron_net_id if neutron_subnet_id is not None: data['--neutron_subnet_id'] = neutron_subnet_id if availability_zone is not None: data['--availability_zone'] = availability_zone cmd = '' for key, value in data.items(): cmd += "%(k)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, wait=False, microversion=None): """Creates a share. :param share_protocol: str -- share protocol of a share. :param size: int/str -- desired size of a share. :param share_network: str -- Name or ID of share network to use. :param share_type: str -- Name or ID of share type to use. :param name: str -- desired name of new share. :param description: str -- desired description of new share. :param public: bool -- should a share be public or not. Default is False. :param snapshot: str -- Name or ID of a snapshot to use as source. :param metadata: dict -- key-value data to provide with share creation. :param wait: bool - the client must wait for "available" state :param microversion: str -- API microversion that should be used. """ cmd = '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 if wait: cmd += '--wait ' share_raw = self.manila(cmd, microversion=microversion) share = output_parser.details(share_raw) return share @not_found_wrapper def get_share(self, share, microversion=None): """Returns a share by its Name or ID.""" share_raw = self.manila('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, share_group_id=None, wait=False, microversion=None): """Deletes share[s] by Names or IDs. :param shares: either str or list of str that can be either Name or ID of a share(s) that should be deleted. :param share_group_id: a common share group ID for the shares being deleted :param wait: bool -- whether to wait for the shares to be deleted """ if not isinstance(shares, list): shares = [shares] cmd = 'delete ' for share in shares: cmd += '%s ' % share if share_group_id: cmd += '--share-group-id %s ' % share_group_id if wait: cmd += '--wait ' return self.manila(cmd, microversion=microversion) @not_found_wrapper @forbidden_wrapper def soft_delete_share(self, shares, microversion=None): """Soft Delete share[s] by Names or IDs. :param shares: either str or list of str that can be either Name or ID of a share(s) that should be soft deleted. """ if not isinstance(shares, list): shares = [shares] cmd = 'soft-delete ' for share in shares: cmd += '%s ' % share return self.manila(cmd, microversion=microversion) @not_found_wrapper @forbidden_wrapper def restore_share(self, shares, microversion=None): """Restore share[s] by Names or IDs. :param shares: either str or list of str that can be either Name or ID of a share(s) that should be soft deleted. """ if not isinstance(shares, list): shares = [shares] cmd = 'restore ' for share in shares: cmd += '%s ' % share return self.manila(cmd, microversion=microversion) def create_share_transfer(self, share_id, name=None, microversion=None): """Create a share transfer. :param share_id: ID of share. ":param name: name of transfer. """ cmd = 'share-transfer-create %s ' % share_id if name: cmd += '--name %s' % name transfer_raw = self.manila(cmd, microversion=microversion) transfer = output_parser.details(transfer_raw) return transfer def delete_share_transfer(self, transfer, microversion=None): """Delete a share transfer. :param transfer: ID or name of share transfer. """ cmd = 'share-transfer-delete %s ' % transfer self.manila(cmd, microversion=microversion) def get_share_transfer(self, transfer, microversion=None): """Get a share transfer. :param transfer: ID or name of share transfer. """ cmd = 'share-transfer-show %s ' % transfer transfer_raw = self.manila(cmd, microversion=microversion) transfer = output_parser.details(transfer_raw) return transfer def list_share_transfer(self, microversion=None): """Get a share transfer.""" cmd = 'share-transfer-list ' transfer_raw = self.manila(cmd, microversion=microversion) transfers = utils.listing(transfer_raw) return transfers def accept_share_transfer(self, transfer, auth_key, microversion=None): """Accept a share transfer. :param transfer: ID or name of share transfer. """ cmd = 'share-transfer-accept %s %s' % (transfer, auth_key) self.manila(cmd, microversion=microversion) def list_shares(self, all_tenants=False, is_soft_deleted=False, filters=None, columns=None, is_public=False, microversion=None): """List shares. :param all_tenants: bool -- whether to list shares that belong only to current project or for all tenants. :param is_soft_deleted: bool -- whether to list shares that has been soft deleted to recycle bin. :param filters: dict -- filters for listing of shares. Example, input: {'project_id': 'foo'} {-'project_id': 'foo'} {--'project_id': 'foo'} {'project-id': 'foo'} will be transformed to filter parameter "--project-id=foo" :param columns: comma separated string of columns. Example, "--columns Name,Size" :param is_public: bool -- should list public shares or not. Default is False. :param microversion: str -- the request api version. """ cmd = 'list ' if all_tenants: cmd += '--all-tenants ' if is_public: cmd += '--public ' if is_soft_deleted: cmd += '--soft-deleted ' if filters and isinstance(filters, dict): for k, v in filters.items(): cmd += '%(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 is_share_transfer_deleted(self, transfer, microversion=None): """Says whether transfer is deleted or not. :param transfer: str -- Name or ID of transfer """ try: self.get_transfer(transfer, microversion=microversion) return False except tempest_lib_exc.NotFound: return True def wait_for_transfer_deletion(self, transfer, microversion=None): """Wait for transfer deletion by its Name or ID. :param transfer: str -- Name or ID of transfer. """ self.wait_for_resource_deletion( SHARE, res_id=transfer, interval=5, timeout=300, microversion=microversion) def wait_for_share_soft_deletion(self, share_id, microversion=None): body = self.get_share(share_id, microversion=microversion) is_soft_deleted = body['is_soft_deleted'] start = int(time.time()) while is_soft_deleted == "False": time.sleep(self.build_interval) body = self.get_share(share_id, microversion=microversion) is_soft_deleted = body['is_soft_deleted'] if is_soft_deleted == "True": return if int(time.time()) - start >= self.build_timeout: message = ("Share %(share_id)s failed to be soft deleted " "within the required time %(build_timeout)s." % {"share_id": share_id, "build_timeout": self.build_timeout}) raise tempest_lib_exc.TimeoutException(message) def wait_for_share_restore(self, share_id, microversion=None): body = self.get_share(share_id, microversion=microversion) is_soft_deleted = body['is_soft_deleted'] start = int(time.time()) while is_soft_deleted == "True": time.sleep(self.build_interval) body = self.get_share(share_id, microversion=microversion) is_soft_deleted = body['is_soft_deleted'] if is_soft_deleted == "False": return if int(time.time()) - start >= self.build_timeout: message = ("Share %(share_id)s failed to be restored " "within the required time %(build_timeout)s." % {"share_id": share_id, "build_timeout": self.build_timeout}) raise tempest_lib_exc.TimeoutException(message) def wait_for_resource_status(self, resource_id, status, microversion=None, resource_type="share"): """Waits for a share to reach a given status.""" get_func = getattr(self, 'get_' + resource_type) body = get_func(resource_id, microversion=microversion) share_status = body['status'] start = int(time.time()) while share_status != status: time.sleep(self.build_interval) body = get_func(resource_id, microversion=microversion) share_status = body['status'] if share_status == status: return elif 'error' in share_status.lower(): raise exceptions.ShareBuildErrorException(share=resource_id) if int(time.time()) - start >= self.build_timeout: message = ("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': str(statuses), }) raise tempest_lib_exc.TimeoutException(message) return share @not_found_wrapper def _set_share_metadata(self, share, data, update_all=False, microversion=None): """Sets a share metadata. :param share: str -- Name or ID of a share. :param data: dict -- key-value pairs to set as metadata. :param update_all: bool -- if set True then all keys except provided will be deleted. """ if not (isinstance(data, dict) and data): msg = ('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, default_ad_site=None, microversion=None): """Creates security service. :param type: security service type (ldap, kerberos or active_directory) :param name: desired name of new security service. :param description: desired description of new security service. :param dns_ip: DNS IP address inside tenant's network. :param ou: security service organizational unit :param server: security service IP address or hostname. :param domain: security service domain. :param user: user of the new security service. :param password: password used by user. :param default_ad_site: default AD site """ cmd = '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, default_ad_site=default_ad_site) ss_raw = self.manila(cmd, microversion=microversion) security_service = output_parser.details(ss_raw) return security_service @not_found_wrapper def update_security_service(self, security_service, name=None, description=None, dns_ip=None, ou=None, server=None, domain=None, user=None, password=None, default_ad_site=None, microversion=None): cmd = '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, default_ad_site=default_ad_site) return output_parser.details( self.manila(cmd, microversion=microversion)) def _combine_security_service_data(self, name=None, description=None, dns_ip=None, ou=None, server=None, domain=None, user=None, password=None, default_ad_site=None): data = '' if name is not None: data += '--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 if default_ad_site is not None: data += '--default-ad-site %s ' % default_ad_site return data @not_found_wrapper def list_share_export_locations(self, share, columns=None, microversion=None): """List share export locations. :param share: str -- Name or ID of a share. :param columns: str -- comma separated string of columns. Example, "--columns uuid,path". :param microversion: API microversion to be used for request. """ cmd = "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'] def share_server_migration_check(self, server_id, dest_host, writable, nondisruptive, preserve_snapshots, new_share_network=None): cmd = ('share-server-migration-check %(server_id)s %(host)s ' '--writable %(writable)s --nondisruptive %(nondisruptive)s ' '--preserve-snapshots %(preserve_snapshots)s') % { 'server_id': server_id, 'host': dest_host, 'writable': writable, 'nondisruptive': nondisruptive, 'preserve_snapshots': preserve_snapshots, } if new_share_network: cmd += ' --new-share-network %s' % new_share_network result = self.manila(cmd) return output_parser.details(result) def share_server_migration_start(self, server_id, dest_host, writable=False, nondisruptive=False, preserve_snapshots=False, new_share_network=None): cmd = ('share-server-migration-start %(server_id)s %(host)s ' '--writable %(writable)s --nondisruptive %(nondisruptive)s ' '--preserve-snapshots %(preserve_snapshots)s') % { 'server_id': server_id, 'host': dest_host, 'writable': writable, 'nondisruptive': nondisruptive, 'preserve_snapshots': preserve_snapshots, } if new_share_network: cmd += ' --new-share-network %s' % new_share_network return self.manila(cmd) def share_server_migration_complete(self, server_id): return self.manila('share-server-migration-complete %s' % server_id) def share_server_migration_cancel(self, server_id): return self.manila('share-server-migration-cancel %s' % server_id) def share_server_migration_get_progress(self, server_id): result = self.manila('share-server-migration-get-progress %s' % server_id) return output_parser.details(result) def wait_for_server_migration_task_state(self, share_server_id, dest_host, task_state_to_wait, microversion=None): """Waits for a certain server task state. """ statuses = ((task_state_to_wait,) if not isinstance(task_state_to_wait, (tuple, list, set)) else task_state_to_wait) server = self.get_share_server(share_server=share_server_id, microversion=microversion) start = int(time.time()) while server['task_state'] not in statuses: time.sleep(self.build_interval) server = self.get_share_server(share_server=share_server_id, microversion=microversion) if server['task_state'] in statuses: return server elif server['task_state'] == constants.TASK_STATE_MIGRATION_ERROR: raise exceptions.ShareServerMigrationException( server_id=server['id']) elif int(time.time()) - start >= self.build_timeout: message = ('Server %(share_server_id)s failed to reach the ' 'status in %(status)s while migrating from host ' '%(src)s to host %(dest)s within the required time ' '%(timeout)s.' % { 'src': server['host'], 'dest': dest_host, 'share_server_id': server['id'], 'timeout': self.build_timeout, 'status': str(statuses), }) raise tempest_lib_exc.TimeoutException(message) # user messages def wait_for_message(self, resource_id): """Waits until a message for a resource with given id exists""" start = int(time.time()) message = None while not message: time.sleep(self.build_interval) for msg in self.list_messages(): if msg['Resource ID'] == resource_id: return msg if int(time.time()) - start >= self.build_timeout: message = ('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, availability_zone=None, share_network=None, microversion=None): """Create a share replica. :param share: str -- Name or ID of a share to create a replica of """ cmd = "share-replica-create %s" % share if availability_zone is not None: cmd += " --availability_zone " + availability_zone if share_network is not None: cmd += " --share_network " + share_network replica = self.manila(cmd, microversion=microversion) return output_parser.details(replica) @not_found_wrapper def get_share_replica(self, replica, microversion=None): cmd = "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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/exceptions.py0000664000175000017500000000442200000000000026226 0ustar00zuulzuul00000000000000# 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.") class ShareServerMigrationException(exceptions.TempestException): message = ("Share server %(server_id)s failed to migrate.") ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.577273 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/0000775000175000017500000000000000000000000024255 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/__init__.py0000664000175000017500000000000000000000000026354 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/base.py0000664000175000017500000004223700000000000025551 0ustar00zuulzuul00000000000000# 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 json 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""" @classmethod def get_admin_client(cls): admin_client = base.CLIClient( username=CONF.admin_username, password=CONF.admin_password, tenant_name=CONF.admin_tenant_name, uri=CONF.admin_auth_url, cli_dir=CONF.manila_exec_dir, insecure=CONF.insecure, 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 ) return admin_client @classmethod def get_user_client(cls): user_client = base.CLIClient( username=CONF.username, password=CONF.password, tenant_name=CONF.tenant_name, uri=CONF.auth_url, cli_dir=CONF.manila_exec_dir, insecure=CONF.insecure, 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 ) return user_client @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 self.admin_client 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']] = str(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, client=None): """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) if client is None: client = self.admin_client return client.openstack(action, flags=flags, params=params, fail_ok=fail_ok, merge_stderr=merge_stderr) def listing_result(self, object_name, command, client=None): """Returns output for the given command as list of dictionaries""" output = self.openstack(object_name, params=command, client=client) result = self.parser.listing(output) return result def dict_result(self, object_name, command, client=None): """Returns output for the given command as dictionary""" output = self.openstack(object_name, params=command, client=client) 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, client=None, wait=None, wait_for_status='available'): 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 if wait: cmd = cmd + ' --wait' share_object = self.dict_result('share', cmd, client=client) self._wait_for_object_status( 'share', share_object['id'], wait_for_status) if add_cleanup: self.addCleanup( self.openstack, 'share delete %s --wait' % share_object['id'] ) return share_object def list_pools(self, backend=None, host=None, pool=None, detail=False): cmd = 'pool list ' if backend: cmd += f'--backend {backend} ' if pool: cmd += f'--pool {pool} ' if host: cmd += f'--host {host} ' if detail: cmd += '--detail' pools = self.listing_result('share', cmd) return pools def create_share_type(self, name=None, dhss=False, description=None, snapshot_support=None, create_share_from_snapshot_support=None, revert_to_snapshot_support=False, mount_snapshot_support=False, extra_specs={}, public=True, add_cleanup=True, client=None, formatter=None): name = name or data_utils.rand_name('autotest_share_type_name') cmd = (f'create {name} {dhss} --public {public}') if description: cmd += f' --description {description}' if snapshot_support: cmd += f' --snapshot-support {snapshot_support}' if create_share_from_snapshot_support: cmd += (' --create-share-from-snapshot-support ' f'{create_share_from_snapshot_support}') if revert_to_snapshot_support: cmd += (' --revert-to-snapshot-support ' f' {revert_to_snapshot_support}') if mount_snapshot_support: cmd += f' --mount-snapshot-support {mount_snapshot_support}' if extra_specs: specs = ' --extra-specs' for key, value in extra_specs.items(): specs += f' {key}={value}' cmd += specs if formatter == 'json': cmd = f'share type {cmd} -f {formatter} ' share_type = json.loads(self.openstack(cmd, client=client)) else: share_type = self.dict_result('share type', cmd, client=client) if add_cleanup: self.addCleanup( self.openstack, f'share type delete {share_type["id"]}' ) return share_type def list_services(self, host=None, status=None, state=None, zone=None): cmd = 'service list ' if host: cmd += f'--host {host} ' if status: cmd += f'--status {status} ' if state: cmd += f'--state {state} ' if zone: cmd += f'--zone {zone} ' services = self.listing_result('share', cmd) return services def create_share_access_rule(self, share, access_type, access_to, properties=None, access_level=None, wait=False, lock_visibility=False, lock_deletion=False, lock_reason=None, add_cleanup=False): cmd = f'access create {share} {access_type} {access_to} ' if access_level: cmd += f'--access-level {access_level} ' if properties: cmd += f'--properties {properties} ' if wait: cmd += '--wait ' if lock_visibility: cmd += '--lock-visibility ' if lock_deletion: cmd += '--lock-deletion ' if lock_reason: cmd += f'--lock-reason {lock_reason}' access_rule = self.dict_result('share', cmd) return access_rule def get_share_export_locations(self, share): cmd = (f'export location list {share}') export_locations = json.loads(self.openstack(f'share {cmd} -f json')) return export_locations def create_snapshot(self, share, name=None, description=None, wait=True, force=None, add_cleanup=True, client=None): name = name or data_utils.rand_name('autotest_snapshot_name') cmd = (f'snapshot create {share} --name {name} ') if description: cmd += f' --description {description}' if wait: cmd += ' --wait' if force: cmd += ' --force' snapshot_object = self.dict_result('share', cmd, client=client) if add_cleanup: self.addCleanup( self.openstack, f'share snapshot delete {snapshot_object["id"]} --wait') return snapshot_object def create_share_transfer(self, share, name=None, client=None): name = name or data_utils.rand_name('autotest_share_transfer_name') cmd = (f'transfer create {share} --name {name} ') transfer_object = self.dict_result('share', cmd, client=client) return transfer_object def create_share_network(self, neutron_net_id=None, neutron_subnet_id=None, name=None, description=None, availability_zone=None, add_cleanup=True): name = name or data_utils.rand_name('autotest_share_network_name') cmd = (f'network create --name {name} --description {description}') if neutron_net_id: cmd = cmd + f' --neutron-net-id {neutron_net_id}' if neutron_subnet_id: cmd = cmd + f' --neutron-subnet-id {neutron_subnet_id}' if availability_zone: cmd = cmd + f' --availability-zone {availability_zone}' share_network_obj = self.dict_result('share', cmd) self._wait_for_object_status( 'share network', share_network_obj['id'], 'active') if add_cleanup: self.addCleanup( self.openstack, f'share network delete {share_network_obj["id"]}' ) return share_network_obj def create_share_replica(self, share, availability_zone=None, share_network=None, wait=None, add_cleanup=True): cmd = (f'replica create {share}') if availability_zone: cmd = cmd + f' --availability-zone {availability_zone}' if wait: cmd = cmd + ' --wait' if share_network: cmd = cmd + ' --share-network %s' % share_network replica_object = self.dict_result('share', cmd) self._wait_for_object_status( 'share replica', replica_object['id'], 'available') if add_cleanup: self.addCleanup( self.openstack, f'share replica delete {replica_object["id"]} --wait' ) return replica_object def get_share_replica_export_locations(self, replica): cmd = (f'replica export location list {replica}') export_locations = self.listing_result('share', cmd) return export_locations def create_share_group_type(self, name=None, share_types=None, group_specs=None, public=True, add_cleanup=True): name = name or data_utils.rand_name('autotest_share_group_types_name') share_types = share_types or 'None' cmd = (f'group type create ' f'{name} ' f'{share_types} ') if group_specs: cmd = cmd + f' --group-specs {group_specs} ' if not public: cmd = cmd + f' --public {public} ' share_object = self.dict_result('share', cmd) if add_cleanup: self.addCleanup( self.openstack, 'share group type delete %s' % share_object['id']) return share_object def share_group_type_access_create(self, group_type, project): cmd = (f'group type access create ' f'{group_type} ' f'{project} ') self.dict_result('share', cmd) def share_group_type_access_delete(self, group_type, access_id): cmd = (f'group type access delete ' f'{group_type} ' f'{access_id} ') self.dict_result('share', cmd) def check_create_network_subnet(self, share_network, neutron_net_id=None, neutron_subnet_id=None, availability_zone=None, restart_check=None): cmd = f'network subnet create {share_network} --check-only' if neutron_net_id: cmd += f' --neutron-net-id {neutron_net_id}' if neutron_subnet_id: cmd += f' --neutron-subnet-id {neutron_subnet_id}' if availability_zone: cmd += f' --availability-zone {availability_zone}' if restart_check: cmd += ' --restart-check' check_result = self.dict_result('share', cmd) return check_result def create_resource_lock(self, resource_id, resource_type='share', resource_action='delete', lock_reason=None, add_cleanup=True, client=None): cmd = f'lock create {resource_id} {resource_type}' cmd += f' --resource-action {resource_action}' if lock_reason: cmd += f' --reason "{lock_reason}"' lock = self.dict_result('share', cmd, client=client) if add_cleanup: self.addCleanup(self.openstack, 'share lock delete %s' % lock['id'], client=client) return lock def create_backup(self, share_id, name=None, description=None, backup_options=None, add_cleanup=True): name = name or data_utils.rand_name('autotest_backup_name') cmd = (f'backup create {share_id} ') if name: cmd += f' --name {name}' if description: cmd += f' --description {description}' if backup_options: options = ' --backup-options' for key, value in backup_options.items(): options += f' {key}={value}' cmd += options backup_object = self.dict_result('share', cmd) self._wait_for_object_status( 'share backup', backup_object['id'], 'available') if add_cleanup: self.addCleanup( self.openstack, f'share backup delete {backup_object["id"]} --wait') return backup_object ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_availability_zones.py0000664000175000017500000000172400000000000031562 0ustar00zuulzuul00000000000000# 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 AvailabilityZonesCLITest(base.OSCClientTestBase): def test_openstack_share_availability_zones_list(self): azs = self.listing_result('share', 'availability zone list') self.assertTableStruct(azs, [ 'Id', 'Name', 'Created At', 'Updated At' ]) self.assertTrue(len(azs) > 0) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_messages.py0000664000175000017500000001001600000000000027473 0ustar00zuulzuul00000000000000# 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 MessagesCLITest(base.OSCClientTestBase): def setUp(self): super(MessagesCLITest, self).setUp() # cause a user message by using an invalid share type for provisioning st = self.create_share_type(extra_specs={'fake_key': 'fake_value'}) self.share = self.create_share(share_type=st['name'], wait_for_status='error', client=self.user_client) def test_list_and_show_messages(self): # Get all messages messages = self.listing_result( 'share', 'message list', client=self.user_client) # We must have at least one message self.assertTrue(len(messages) > 0) self.assertTableStruct(messages, [ 'ID', 'Resource Type', 'Resource ID', 'Action ID', 'User Message', 'Detail ID', 'Created At', ]) # grab the message we created message = [msg for msg in messages if msg['Resource ID'] == self.share['id']] self.assertEqual(1, len(message)) show_message = self.dict_result('share', f'message show {message[0]["ID"]}') self.addCleanup(self.openstack, f'share message delete {show_message["id"]}') self.assertEqual(message[0]['ID'], show_message['id']) expected_keys = ( 'id', 'action_id', 'resource_id', 'detail_id', 'resource_type', 'created_at', 'expires_at', 'message_level', 'user_message', 'request_id', ) for key in expected_keys: self.assertIn(key, show_message) # filtering by dates since = show_message['created_at'] before = show_message['expires_at'] filtered_messages = self.listing_result( 'share', f'message list --since {since} --before {before}', client=self.user_client) self.assertTrue(len(filtered_messages) > 0) self.assertIn(show_message['id'], [m['ID'] for m in filtered_messages]) # filtering by message level filtered_messages = self.listing_result( 'share', f'message list --message-level {show_message["message_level"]}', client=self.user_client) self.assertTrue(len(filtered_messages) > 0) self.assertIn(show_message['id'], [m['ID'] for m in filtered_messages]) # filtering by Resource ID filtered_messages = self.listing_result( 'share', f'message list --resource-id {self.share["id"]}', client=self.user_client) self.assertEqual(1, len(filtered_messages)) self.assertEqual(show_message['resource_id'], self.share["id"]) def test_delete_message(self): messages = self.listing_result( 'share', 'message list', client=self.user_client) message = [msg for msg in messages if msg['Resource ID'] == self.share['id']] self.assertEqual(1, len(message)) message = message[0] self.openstack(f'share message delete {message["ID"]}') messages = self.listing_result( 'share', 'message list', client=self.user_client) messages = [msg for msg in messages if msg['ID'] == message["ID"]] self.assertEqual(0, len(messages)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_resource_locks.py0000664000175000017500000001564400000000000030722 0ustar00zuulzuul00000000000000# 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 as lib_exc from manilaclient.tests.functional.osc import base from manilaclient.tests.functional import utils LOCK_DETAIL_ATTRIBUTES = [ 'ID', 'Resource Id', 'Resource Type', 'Resource Action', 'Lock Context', 'User Id', 'Project Id', 'Created At', 'Updated At', 'Lock Reason', ] LOCK_SUMMARY_ATTRIBUTES = [ 'ID', 'Resource Id', 'Resource Type', 'Resource Action', ] @utils.skip_if_microversion_not_supported('2.82') class ResourceLockTests(base.OSCClientTestBase): """Lock CLI test cases""" def setUp(self): super(ResourceLockTests, self).setUp() self.share_type = self.create_share_type( name=data_utils.rand_name('lock_tests_type')) self.share = self.create_share(share_type=self.share_type['id']) def test_lock_create_show_use_delete(self): """Create a deletion lock on share, view it, try it and remove.""" lock = self.create_resource_lock(self.share['id'], lock_reason='tigers rule', client=self.user_client, add_cleanup=False) client_user_id = self.openstack( "token issue -c user_id -f value", client=self.user_client ).strip() client_project_id = self.openstack( "token issue -c project_id -f value", client=self.user_client ).strip() self.assertEqual(self.share['id'], lock['resource_id']) self.assertEqual('delete', lock['resource_action']) self.assertEqual(client_user_id, lock['user_id']) self.assertEqual(client_project_id, lock['project_id']) self.assertEqual('user', lock['lock_context']) self.assertEqual('tigers rule', lock['lock_reason']) lock_show = self.dict_result("share", f"lock show {lock['id']}") self.assertEqual(lock['id'], lock_show['ID']) self.assertEqual(lock['lock_context'], lock_show['Lock Context']) # When a deletion lock exists, share deletion must fail self.assertRaises(lib_exc.CommandFailed, self.openstack, f"share delete {self.share['id']}") # delete the lock, share will be deleted in cleanup stage self.openstack(f"share lock delete {lock['id']}", client=self.user_client) self.assertRaises(lib_exc.CommandFailed, self.openstack, f"share lock show {lock['id']}") def test_lock_list_filter_paginate(self): lock_1 = self.create_resource_lock(self.share['id'], lock_reason='tigers rule', client=self.user_client) lock_2 = self.create_resource_lock(self.share['id'], lock_reason='tigers still rule', client=self.user_client) lock_3 = self.create_resource_lock(self.share['id'], lock_reason='admins rule', client=self.admin_client) locks = self.listing_result('share', f'lock list --resource {self.share["id"]}') self.assertEqual(3, len(locks)) self.assertEqual(sorted(LOCK_SUMMARY_ATTRIBUTES), sorted(locks[0].keys())) locks = self.listing_result('share', 'lock list --lock-context user ' f' --resource {self.share["id"]}') self.assertEqual(2, len(locks)) self.assertNotIn(lock_3['id'], [lock['ID'] for lock in locks]) locks = self.listing_result('share', 'lock list --lock-context user' f' --resource {self.share["id"]}' ' --sort-key created_at ' ' --sort-dir desc ' ' --limit 1') self.assertEqual(1, len(locks)) self.assertIn(lock_2['id'], [lock['ID'] for lock in locks]) self.assertNotIn(lock_1['id'], [lock['ID'] for lock in locks]) self.assertNotIn(lock_3['id'], [lock['ID'] for lock in locks]) def test_lock_set_unset_lock_reason(self): lock = self.create_resource_lock(self.share['id'], client=self.user_client) self.assertEqual('None', lock['lock_reason']) self.openstack('share lock set ' f"--lock-reason 'updated reason' {lock['id']}") lock_show = self.dict_result("share", f"lock show {lock['id']}") self.assertEqual('updated reason', lock_show['Lock Reason']) self.openstack(f"share lock unset --lock-reason {lock['id']}") lock_show = self.dict_result("share", f"lock show {lock['id']}") self.assertEqual('None', lock_show['Lock Reason']) def test_lock_restrictions(self): """A user can't update or delete a lock created by another user.""" lock = self.create_resource_lock(self.share['id'], client=self.admin_client, add_cleanup=False) self.assertEqual('admin', lock['lock_context']) self.assertRaises(lib_exc.CommandFailed, self.openstack, f"share lock set {lock['id']} " f"--reason 'i cannot do this'", client=self.user_client) self.assertRaises(lib_exc.CommandFailed, self.openstack, f"share lock unset {lock['id']} --reason", client=self.user_client) self.assertRaises(lib_exc.CommandFailed, self.openstack, f"share lock delete {lock['id']} ", client=self.user_client) self.openstack(f'share lock set ' f'--lock-reason "I can do this" ' f'{lock["id"]}', client=self.admin_client) self.openstack(f'share lock delete ' f'{lock["id"]}', client=self.admin_client) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_share_access_rules.py0000664000175000017500000002210100000000000031517 0ustar00zuulzuul00000000000000# 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 import exceptions as tempest_exc from manilaclient.tests.functional.osc import base @ddt.ddt class ShareAccessAllowTestCase(base.OSCClientTestBase): def test_share_access_allow(self): share = self.create_share() access_rule = self.create_share_access_rule( share=share['name'], access_type='ip', access_to='0.0.0.0/0', wait=True) self.assertEqual(access_rule['share_id'], share['id']) self.assertEqual(access_rule['state'], 'active') self.assertEqual(access_rule['access_type'], 'ip') self.assertEqual(access_rule['access_to'], '0.0.0.0/0') # default values self.assertEqual(access_rule['properties'], '') self.assertEqual(access_rule['access_level'], 'rw') access_rules = self.listing_result('share', f'access list {share["id"]}') self.assertIn(access_rule['id'], [item['ID'] for item in access_rules]) # create another access rule with different params access_rule = self.create_share_access_rule( share=share['name'], access_type='ip', access_to='12.34.56.78', access_level='ro', properties='foo=bar') self.assertEqual(access_rule['access_type'], 'ip') self.assertEqual(access_rule['access_to'], '12.34.56.78') self.assertEqual(access_rule['properties'], 'foo : bar') self.assertEqual(access_rule['access_level'], 'ro') @ddt.data( {'lock_visibility': True, 'lock_deletion': True, 'lock_reason': None}, {'lock_visibility': False, 'lock_deletion': True, 'lock_reason': None}, {'lock_visibility': True, 'lock_deletion': False, 'lock_reason': 'testing'}, {'lock_visibility': True, 'lock_deletion': False, 'lock_reason': 'testing'}, ) @ddt.unpack def test_share_access_allow_restrict(self, lock_visibility, lock_deletion, lock_reason): share = self.create_share() access_rule = self.create_share_access_rule( share=share['id'], access_type='ip', access_to='0.0.0.0/0', wait=True, lock_visibility=lock_visibility, lock_deletion=lock_deletion, lock_reason=lock_reason) if lock_deletion: self.assertRaises( tempest_exc.CommandFailed, self.openstack, 'share', params=f'access delete {share["id"]} {access_rule["id"]}' ) self.openstack( 'share', params=f'access delete {share["id"]} {access_rule["id"]} ' f'--unrestrict --wait') @ddt.ddt class ShareAccessDenyTestCase(base.OSCClientTestBase): @ddt.data(True, False) def test_share_access_deny(self, lock_deletion): share = self.create_share() access_rule = self.create_share_access_rule( share=share['name'], access_type='ip', access_to='0.0.0.0/0', wait=True, lock_deletion=lock_deletion) access_rules = self.listing_result('share', f'access list {share["id"]}') num_access_rules = len(access_rules) delete_params = ( f'access delete {share["name"]} {access_rule["id"]} --wait') if lock_deletion: delete_params += ' --unrestrict' self.openstack('share', params=delete_params) access_rules = self.listing_result('share', f'access list {share["id"]}') self.assertEqual(num_access_rules - 1, len(access_rules)) @ddt.data class ListShareAccessRulesTestCase(base.OSCClientTestBase): @ddt.data("2.45", "2.33", "2.21") def test_share_access_list(self, microversion): share = self.create_share() self.create_share_access_rule( share=share['name'], access_type='ip', access_to='0.0.0.0/0', wait=True) output = self.openstack( 'share', params=f'access list {share["id"]}', flags=f'--os-share-api-version {microversion}') access_rule_list = self.parser.listing(output) base_list = [ 'ID', 'Access Type', 'Access To', 'Access Level', 'State'] if microversion >= '2.33': base_list.append('Access Key') if microversion >= '2.45': base_list.extend(['Created At', 'Updated At']) self.assertTableStruct(access_rule_list, base_list) self.assertTrue(len(access_rule_list) > 0) self.create_share_access_rule( share=share['name'], access_type='ip', access_to='192.168.0.151', wait=True, properties='foo=bar') output = self.openstack( 'share', params=f'access list {share["id"]} ' f'--properties foo=bar', flags=f'--os-share-api-version {microversion}') access_rule_properties = self.parser.listing(output) self.assertEqual(1, len(access_rule_properties)) self.assertEqual(access_rule_properties['id'], access_rule_properties[0]['ID']) def test_share_access_list_with_filters(self): share = self.create_share() access_to_filter = '20.0.0.0/0' self.create_share_access_rule( share=share['name'], access_type='ip', access_to='0.0.0.0/0', wait=True) self.create_share_access_rule( share=share['name'], access_type='ip', access_to=access_to_filter, wait=True) output = self.openstack( 'share', params=f'access list {share["id"]} --access-to {access_to_filter}', flags='--os-share-api-version 2.82') access_rule_list = self.parser.listing(output) self.assertTrue(len(access_rule_list) == 1) class ShowShareAccessRulesTestCase(base.OSCClientTestBase): def test_share_access_show(self): share = self.create_share() access_rule = self.create_share_access_rule( share=share['name'], access_type='ip', access_to='0.0.0.0/0', wait=True) access_rule_show = self.dict_result( 'share', f'access show {access_rule["id"]}') self.assertEqual(access_rule_show['id'], access_rule['id']) self.assertEqual(access_rule_show['share_id'], share['id']) self.assertEqual(access_rule_show['access_level'], 'rw') self.assertEqual(access_rule_show['access_to'], '0.0.0.0/0') self.assertEqual(access_rule_show['access_type'], 'ip') self.assertEqual(access_rule_show['state'], 'active') self.assertEqual(access_rule_show['access_key'], 'None') self.assertEqual(access_rule_show['created_at'], access_rule['created_at']) self.assertEqual(access_rule_show['properties'], '') self.assertIn('updated_at', access_rule_show) class SetShareAccessTestCase(base.OSCClientTestBase): def test_set_share_access(self): share = self.create_share() access_rule = self.create_share_access_rule( share=share['name'], access_type='ip', access_to='0.0.0.0/0', wait=True) self.assertEqual(access_rule['properties'], '') self.openstack('share', params=f'access set {access_rule["id"]} ' f'--property foo=bar') access_rule = self.dict_result( 'share', f'access show {access_rule["id"]}') self.assertEqual(access_rule['properties'], 'foo : bar') class UnsetShareAccessRulesTestCase(base.OSCClientTestBase): def test_unset_share_access(self): share = self.create_share() access_rule = self.create_share_access_rule( share=share['name'], access_type='ip', access_to='192.168.0.101', wait=True, properties='foo=bar') self.openstack('share', params=f'access unset ' f'--property foo {access_rule["id"]}') access_rule_unset = self.dict_result( 'share', f'access show {access_rule["id"]}') self.assertEqual(access_rule_unset['properties'], '') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_share_backups.py0000664000175000017500000001207200000000000030502 0ustar00zuulzuul00000000000000# 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 json from manilaclient.tests.functional.osc import base class ShareBackupCLITest(base.OSCClientTestBase): """Functional tests for share backup.""" def test_share_backup_create(self): share = self.create_share() backup = self.create_backup( share_id=share['id'], name='test_backup_create', description='Description', backup_options={'dummy': True}) # fetch latest after periodic callback updates status backup = json.loads(self.openstack( f'share backup show -f json {backup["id"]}')) self.assertEqual(share["id"], backup["share_id"]) self.assertEqual('test_backup_create', backup["name"]) self.assertEqual('Description', backup["description"]) self.assertEqual('available', backup["status"]) backups_list = self.listing_result('share backup', 'list') self.assertIn(backup['id'], [item['ID'] for item in backups_list]) def test_share_backup_delete(self): share = self.create_share() backup = self.create_backup( share_id=share['id'], backup_options={'dummy': True}, add_cleanup=False) self.openstack( f'share backup delete {backup["id"]} --wait') self.check_object_deleted('share backup', backup["id"]) def test_share_backup_show(self): share = self.create_share() backup = self.create_backup( share_id=share['id'], name='test_backup_show', description='Description', backup_options={'dummy': True}) show_result = self.dict_result( 'share backup', f'show {backup["id"]}') self.assertEqual(backup["id"], show_result["id"]) self.assertEqual('test_backup_show', show_result["name"]) self.assertEqual('Description', show_result["description"]) def test_share_backup_set(self): share = self.create_share() backup = self.create_backup(share_id=share['id'], backup_options={'dummy': True}) self.openstack( f'share backup set {backup["id"]} ' f'--name test_backup_set --description Description') show_result = self.dict_result( 'share backup ', f'show {backup["id"]}') self.assertEqual(backup['id'], show_result["id"]) self.assertEqual('test_backup_set', show_result["name"]) self.assertEqual('Description', show_result["description"]) def test_share_backup_unset(self): share = self.create_share() backup = self.create_backup( share_id=share['id'], name='test_backup_unset', description='Description', backup_options={'dummy': True}) self.openstack( f'share backup unset {backup["id"]} --name --description') show_result = json.loads(self.openstack( f'share backup show -f json {backup["id"]}')) self.assertEqual(backup['id'], show_result["id"]) self.assertIsNone(show_result["name"]) self.assertIsNone(show_result["description"]) def test_share_backup_list(self): share_1 = self.create_share() share_2 = self.create_share() backup_1 = self.create_backup(share_id=share_1['id'], backup_options={'dummy': True}) backup_2 = self.create_backup(share_id=share_2['id'], backup_options={'dummy': True}) backups_list = self.listing_result( 'share backup', f'list --name {backup_2["name"]} ' ) self.assertTableStruct(backups_list, [ 'ID', 'Name', 'Share ID', 'Status' ]) self.assertEqual(1, len(backups_list)) self.assertIn(backup_2['id'], [item['ID'] for item in backups_list]) backups_list = self.listing_result( 'share backup', f'list --share {share_1["id"]} --detail' ) self.assertTableStruct(backups_list, [ 'ID', 'Name', 'Share ID', 'Status', 'Description', 'Availability Zone', 'Created At', 'Updated At', 'Size', 'Progress', 'Restore Progress', 'Host', 'Topic', ]) self.assertEqual(1, len(backups_list)) self.assertIn(backup_1['id'], [item['ID'] for item in backups_list]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_share_group_type_access.py0000664000175000017500000000671500000000000032577 0ustar00zuulzuul00000000000000# 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_serialization import jsonutils from manilaclient.tests.functional.osc import base class SharesGroupTypeAccessCLITest(base.OSCClientTestBase): def test_share_group_type_access_create(self): share_group_type_name = self.create_share_group_type( share_types='dhss_false', public=False)['name'] access_list = self.listing_result( 'share', f'group type access list {share_group_type_name}') self.assertEqual(0, len(access_list)) cmd_output = jsonutils.loads(self.openstack('token issue -f json ')) auth_project_id = cmd_output['project_id'] self.share_group_type_access_create( share_group_type_name, auth_project_id) share_group_type_access_list = self.listing_result( 'share', f'group type access list {share_group_type_name}') self.assertEqual(1, len(share_group_type_access_list)) def test_share_group_type_access_delete(self): share_group_type_name = self.create_share_group_type( share_types='dhss_false', public=False)['name'] access_list = self.listing_result( 'share', f'group type access list {share_group_type_name}') self.assertEqual(0, len(access_list)) cmd_output = jsonutils.loads(self.openstack('token issue -f json ')) auth_project_id = cmd_output['project_id'] # Create using name to ensure that we are "translating" the # name into the actual ID in the CLI self.share_group_type_access_create( share_group_type_name, auth_project_id) share_group_type_access_list = self.listing_result( 'share', f'group type access list {share_group_type_name}') self.assertEqual(1, len(share_group_type_access_list)) self.assertEqual( share_group_type_access_list[0]['Project ID'], auth_project_id) self.share_group_type_access_delete( share_group_type_name, auth_project_id) access_list = self.listing_result( 'share', f'group type access list {share_group_type_name}') self.assertEqual(0, len(access_list)) def test_share_group_type_access_list(self): share_group_type_name = self.create_share_group_type( share_types='dhss_false', public=False)['name'] access_list = self.listing_result( 'share', f'group type access list {share_group_type_name}') self.assertEqual(0, len(access_list)) cmd_output = jsonutils.loads(self.openstack('token issue -f json ')) auth_project_id = cmd_output['project_id'] self.share_group_type_access_create( share_group_type_name, auth_project_id) share_group_type_access_list = self.listing_result( 'share', f'group type access list {share_group_type_name}') self.assertEqual(1, len(share_group_type_access_list)) self.assertTableStruct(access_list, ['Project ID']) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_share_limits.py0000664000175000017500000000240100000000000030346 0ustar00zuulzuul00000000000000# Copyright 2021 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. from manilaclient.tests.functional.osc import base class ListSharePoolsTestCase(base.OSCClientTestBase): def test_limits_show_absolute(self): limits = self.listing_result('share', ' limits show --absolute') self.assertTableStruct(limits, [ 'Name', 'Value' ]) def test_limits_show_rate(self): limits = self.listing_result('share', ' limits show --rate --print-empty') self.assertTableStruct(limits, [ 'Verb', 'Regex', 'URI', 'Value', 'Remaining', 'Unit', 'Next Available' ]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_share_network_subnets.py0000664000175000017500000000261200000000000032305 0ustar00zuulzuul00000000000000# Copyright 2022 NetApp, 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.tests.functional.osc import base class ShareNetworkSubnetsCLITest(base.OSCClientTestBase): def test_openstack_share_network_create_check(self): share_network = self.create_share_network() check_result = self.check_create_network_subnet( share_network['id']) self.assertEqual('True', check_result['compatible']) self.assertEqual('{}', check_result['hosts_check_result']) def test_openstack_share_network_create_check_restart(self): share_network = self.create_share_network() check_result = self.check_create_network_subnet( share_network['id'], restart_check=True) self.assertEqual('True', check_result['compatible']) self.assertEqual('{}', check_result['hosts_check_result']) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_share_networks.py0000664000175000017500000000747000000000000030734 0ustar00zuulzuul00000000000000# 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 ShareNetworksCLITest(base.OSCClientTestBase): def test_openstack_share_network_create(self): share_network_name = 'test_create_share_network' share_network = self.create_share_network(name=share_network_name) self.assertEqual(share_network['name'], share_network_name) share_network_list = self.listing_result('share network', 'list') self.assertIn(share_network['id'], [item['ID'] for item in share_network_list]) def test_openstack_share_network_list(self): share_network = self.create_share_network() share_network_list = self.listing_result('share network', 'list') self.assertTableStruct(share_network_list, [ 'ID', 'Name', ]) self.assertIn(share_network['id'], [item['ID'] for item in share_network_list]) def test_openstack_share_network_show(self): share_network = self.create_share_network() result = self.dict_result('share network', 'show %s' % share_network['id']) self.assertEqual(share_network['id'], result['id']) listing_result = self.listing_result('share network', 'show %s' % share_network['id']) self.assertTableStruct(listing_result, [ 'Field', 'Value' ]) def test_openstack_share_network_delete(self): share_network = self.create_share_network(add_cleanup=False) share_network_list = self.listing_result('share network', 'list') self.assertIn(share_network['id'], [item['ID'] for item in share_network_list]) self.openstack('share network delete %s' % share_network['id']) self.check_object_deleted('share network', share_network['id']) share_network_list_after_delete = self.listing_result('share network', 'list') self.assertNotIn( share_network['id'], [item['ID'] for item in share_network_list_after_delete]) def test_openstack_share_network_set(self): share_network = self.create_share_network() self.openstack('share network set %s --name %s' % (share_network['id'], 'new_name')) result = self.dict_result('share network', 'show %s' % share_network['id']) self.assertEqual(share_network['id'], result['id']) self.assertEqual('new_name', result['name']) def test_openstack_share_network_unset(self): share_network = self.create_share_network(name='test_name') result1 = self.dict_result('share network', 'show %s' % share_network['id']) self.assertEqual(share_network['id'], result1['id']) self.assertEqual(share_network['name'], result1['name']) self.openstack('share network unset %s --name' % (share_network['id'])) result2 = self.dict_result('share network', 'show %s' % share_network['id']) self.assertEqual(share_network['id'], result2['id']) self.assertEqual('None', result2['name']) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_share_pools.py0000664000175000017500000000300300000000000030200 0ustar00zuulzuul00000000000000# 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 ListSharePoolsTestCase(base.OSCClientTestBase): def test_pool_list(self): pools = self.list_pools() self.assertTableStruct(pools, [ 'Name', 'Host', 'Backend', 'Pool' ]) self.assertTrue(len(pools) > 0) # Filter results first_pool = pools[0] pools = self.list_pools(backend=first_pool['Backend'], host=first_pool['Host'], pool=first_pool['Pool']) self.assertEqual(1, len(pools)) self.assertEqual(first_pool['Name'], pools[0]['Name']) def test_pool_list_detail(self): pools = self.list_pools(detail=True) self.assertTableStruct(pools, [ 'Name', 'Host', 'Backend', 'Pool', 'Capabilities' ]) self.assertTrue(len(pools) > 0) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_share_replica_export_locations.py0000664000175000017500000000507200000000000034147 0ustar00zuulzuul00000000000000# 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 json from manilaclient.tests.functional.osc import base from tempest.lib.common.utils import data_utils class ShareReplicaExportLocationsCLITest(base.OSCClientTestBase): def test_openstack_share_replica_export_location_list(self): slug = 'replica-supported' share_type = self.create_share_type( data_utils.rand_name(slug), 'False', extra_specs={ 'replication_type': 'readable'}) share = self.create_share(share_type=share_type['name']) replica = self.create_share_replica(share['id'], wait=True) rep_exp_loc_list = self.listing_result( 'share replica export location', f'list {replica["id"]}') self.assertTableStruct(rep_exp_loc_list, [ 'ID', 'Availability Zone', 'Replica State', 'Preferred', 'Path' ]) exp_loc_list = self.openstack( f'share replica show {replica["id"]} -f json') exp_loc_list = json.loads(exp_loc_list) self.assertIn(exp_loc_list.get('export_locations')[0]['id'], [item['ID'] for item in rep_exp_loc_list]) def test_openstack_share_replica_export_location_show(self): slug = 'replica-supported' share_type = self.create_share_type( data_utils.rand_name(slug), 'False', extra_specs={ 'replication_type': 'readable'}) share = self.create_share(share_type=share_type['name']) replica = self.create_share_replica(share['id'], wait=True) rep_exp_loc_obj = self.get_share_replica_export_locations( replica['id'])[0] exp_loc_list = self.openstack( f'share replica show {replica["id"]} -f json') exp_loc_list = json.loads(exp_loc_list) result = self.dict_result( 'share replica export location', f'show {replica["id"]} {rep_exp_loc_obj["ID"]}') export_location = exp_loc_list['export_locations'][0] self.assertEqual(result['id'], export_location['id']) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_share_replicas.py0000664000175000017500000000431000000000000030650 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from manilaclient import config from manilaclient.tests.functional.osc import base from tempest.lib.common.utils import data_utils CONF = config.CONF class ShareReplicasCLITest(base.OSCClientTestBase): def _create_share_and_replica(self, add_cleanup=True): replication_type = CONF.replication_type share_type = self.create_share_type( data_utils.rand_name('test_share_type'), dhss=True, extra_specs={'replication_type': replication_type}) share_network = self.create_share_network(name='test_share_network') share = self.create_share(share_type=share_type['name'], share_network=share_network['id']) replica = self.create_share_replica(share['id'], share_network=share_network['id'], wait=True, add_cleanup=add_cleanup) return replica def test_share_replica_create(self): share_replica = self._create_share_and_replica() share_replica_list = self.listing_result('share replica', 'list') self.assertTableStruct(share_replica_list, [ 'ID', 'Status', 'Share ID', ]) self.assertIn(share_replica['id'], [item['ID'] for item in share_replica_list]) def test_share_replica_delete(self): share_replica = self._create_share_and_replica(add_cleanup=False) self.openstack( f'share replica delete {share_replica["id"]}' ) self.check_object_deleted('share replica', share_replica["id"]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_share_services.py0000664000175000017500000000445500000000000030703 0ustar00zuulzuul00000000000000# 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 ShareServicesTestCase(base.OSCClientTestBase): def test_services_list(self): services = self.list_services() self.assertTableStruct(services, [ 'ID', 'Binary', 'Host', 'Zone', 'Status', 'State', 'Updated At' ]) self.assertTrue(len(services) > 0) # Filter results first_service = services[0] services = self.list_services(host=first_service['Host'], status=first_service['Status'], state=first_service['State']) self.assertEqual(1, len(services)) for attr in ('ID', 'Binary', 'Host', 'State', 'Status', 'Zone'): self.assertEqual(first_service[attr], services[0][attr]) def test_services_set(self): services = self.list_services() service = [service for service in services if service["Binary"] == "manila-data"] first_service = service[0] self.openstack(f'share service set {first_service["Host"]} ' f'{first_service["Binary"]} ' '--disable --disable-reason test') result = self.listing_result('share service', 'list --status disabled') self.assertEqual(first_service['ID'], result[0]['ID']) self.assertEqual('disabled', result[0]['Status']) self.assertEqual('test', result[0]['Disabled Reason']) # enable the share service again self.openstack(f'share service set {first_service["Host"]} ' f'{first_service["Binary"]} ' '--enable') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_share_snapshot_instances.py0000664000175000017500000000640100000000000032757 0ustar00zuulzuul00000000000000# 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 ShareSnapshotInstancesCLITest(base.OSCClientTestBase): def test_openstack_share_snapshot_instance_list(self): share = self.create_share() snapshot = self.create_snapshot(share['id']) share_snapshot_instances_list = self.listing_result( 'share snapshot instance', 'list --detailed') self.assertTableStruct(share_snapshot_instances_list, [ 'ID', 'Snapshot ID', 'Status', 'Created At', 'Updated At', 'Share ID', 'Share Instance ID', 'Progress', 'Provider Location' ]) self.assertIn(snapshot['id'], [item['Snapshot ID'] for item in ( share_snapshot_instances_list)]) def test_openstack_share_snapshot_instance_show(self): share = self.create_share() snapshot = self.create_snapshot(share['id'], wait=True) share_snapshot_instance = self.listing_result( "share snapshot instance", f'list --snapshot {snapshot["id"]}') result = self.dict_result('share snapshot instance', f'show {share_snapshot_instance[0]["ID"]}') self.assertEqual(share_snapshot_instance[0]['ID'], result['id']) self.assertEqual(share_snapshot_instance[0]['Snapshot ID'], result['snapshot_id']) listing_result = self.listing_result( 'share snapshot instance', f'show ' f'{share_snapshot_instance[0]["ID"]}') self.assertTableStruct(listing_result, [ 'Field', 'Value' ]) def test_openstack_share_snapshot_instance_set(self): share = self.create_share() snapshot = self.create_snapshot(share['id'], wait=True) share_snapshot_instance = self.listing_result( "share snapshot instance", f'list --snapshot {snapshot["id"]}') result1 = self.dict_result('share snapshot instance', f'show {share_snapshot_instance[0]["ID"]}') self.assertEqual(share_snapshot_instance[0]['ID'], result1['id']) self.assertEqual(snapshot['id'], result1['snapshot_id']) self.assertEqual('available', result1['status']) self.openstack('share snapshot instance set ' f'{share_snapshot_instance[0]["ID"]} --status error') result2 = self.dict_result('share snapshot instance', f'show {share_snapshot_instance[0]["ID"]}') self.assertEqual(share_snapshot_instance[0]["ID"], result2['id']) self.assertEqual('error', result2['status']) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_share_snapshots.py0000664000175000017500000001713500000000000031101 0ustar00zuulzuul00000000000000# 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 json from manilaclient.tests.functional.osc import base class ShareSnapshotCLITest(base.OSCClientTestBase): """Functional tests for share snpshot.""" def test_share_snapshot_create(self): share = self.create_share() snapshot = self.create_snapshot( share=share['id'], name='Snap', description='Description') self.assertEqual(share["id"], snapshot["share_id"]) self.assertEqual('Snap', snapshot["name"]) self.assertEqual('Description', snapshot["description"]) self.assertEqual('available', snapshot["status"]) snapshots_list = self.listing_result('share snapshot', 'list') self.assertIn(snapshot['id'], [item['ID'] for item in snapshots_list]) def test_share_snapshot_delete(self): share = self.create_share() snapshot_1 = self.create_snapshot( share=share['id'], add_cleanup=False) snapshot_2 = self.create_snapshot( share=share['id'], add_cleanup=False) self.openstack( f'share snapshot delete {snapshot_1["id"]} {snapshot_2["id"]} ' '--wait') self.check_object_deleted('share snapshot', snapshot_1["id"]) self.check_object_deleted('share snapshot', snapshot_2["id"]) snapshot_3 = self.create_snapshot( share=share['id'], add_cleanup=False) self.openstack( f'share snapshot set {snapshot_3["id"]} ' '--status creating') self.openstack( f'share snapshot delete {snapshot_3["id"]} --wait --force') self.check_object_deleted('share snapshot', snapshot_3["id"]) def test_share_snapshot_show(self): share = self.create_share() snapshot = self.create_snapshot( share=share['id'], name='Snap', description='Description') show_result = self.dict_result( 'share snapshot', f'show {snapshot["id"]}') self.assertEqual(snapshot["id"], show_result["id"]) self.assertEqual('Snap', show_result["name"]) self.assertEqual('Description', show_result["description"]) def test_share_snapshot_set(self): share = self.create_share() snapshot = self.create_snapshot(share=share['id']) self.openstack( f'share snapshot set {snapshot["id"]} ' f'--name Snap --description Description') show_result = self.dict_result( 'share snapshot ', f'show {snapshot["id"]}') self.assertEqual(snapshot['id'], show_result["id"]) self.assertEqual('Snap', show_result["name"]) self.assertEqual('Description', show_result["description"]) def test_share_snapshot_unset(self): share = self.create_share() snapshot = self.create_snapshot( share=share['id'], name='Snap', description='Description') self.openstack( f'share snapshot unset {snapshot["id"]} --name --description') show_result = json.loads(self.openstack( f'share snapshot show -f json {snapshot["id"]}')) self.assertEqual(snapshot['id'], show_result["id"]) self.assertIsNone(show_result["name"]) self.assertIsNone(show_result["description"]) def test_share_snapshot_list(self): share = self.create_share() snapshot_1 = self.create_snapshot(share=share['id']) snapshot_2 = self.create_snapshot( share=share['id'], description='Description') snapshots_list = self.listing_result( 'share snapshot', f'list --name {snapshot_2["name"]} ' f'--description {snapshot_2["description"]} --all-projects' ) self.assertTableStruct(snapshots_list, [ 'ID', 'Name', 'Project ID' ]) self.assertIn(snapshot_2["name"], [snap["Name"] for snap in snapshots_list]) self.assertEqual(1, len(snapshots_list)) snapshots_list = self.listing_result( 'share snapshot', f'list --share {share["name"]}') self.assertTableStruct(snapshots_list, [ 'ID', 'Name' ]) id_list = [snap["ID"] for snap in snapshots_list] self.assertIn(snapshot_1["id"], id_list) self.assertIn(snapshot_2["id"], id_list) snapshots_list = self.listing_result( 'share snapshot', f'list --name~ {snapshot_2["name"][-3:]} ' '--description~ Des --detail') self.assertTableStruct(snapshots_list, [ 'ID', 'Name', 'Status', 'Description', 'Created At', 'Size', 'Share ID', 'Share Proto', 'Share Size', 'User ID' ]) self.assertIn(snapshot_2["name"], [snap["Name"] for snap in snapshots_list]) self.assertEqual( snapshot_2["description"], snapshots_list[0]['Description']) self.assertEqual(1, len(snapshots_list)) def test_share_snapshot_export_location_list(self): share = self.create_share() snapshot = self.create_snapshot(share=share['id']) export_location_list = self.listing_result( 'share snapshot export location', f' list {snapshot["id"]}') self.assertTableStruct(export_location_list, [ 'ID', 'Path' ]) def test_share_snapshot_export_location_show(self): share = self.create_share() snapshot = self.create_snapshot(share=share['id']) export_location_list = self.listing_result( 'share snapshot export location', f'list {snapshot["id"]}') export_location = self.dict_result( 'share snapshot export location', f'show {snapshot["id"]} {export_location_list[0]["ID"]}') self.assertIn('id', export_location) self.assertIn('created_at', export_location) self.assertIn('is_admin_only', export_location) self.assertIn('path', export_location) self.assertIn('share_snapshot_instance_id', export_location) self.assertIn('updated_at', export_location) def test_share_snapshot_abandon_adopt(self): share = self.create_share() snapshot = self.create_snapshot( share=share['id'], add_cleanup=False) self.openstack( f'share snapshot abandon {snapshot["id"]} --wait') snapshots_list = self.listing_result('share snapshot', 'list') self.assertNotIn(snapshot['id'], [item['ID'] for item in snapshots_list]) snapshot = self.dict_result( 'share snapshot', f'adopt {share["id"]} 10.0.0.1:/foo/path ' f'--name Snap --description Zorilla --wait') snapshots_list = self.listing_result('share snapshot', 'list') self.assertIn(snapshot['id'], [item['ID'] for item in snapshots_list]) self.addCleanup( self.openstack, f'share snapshot delete {snapshot["id"]} --force --wait') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_share_transfers.py0000664000175000017500000000672500000000000031071 0ustar00zuulzuul00000000000000# Copyright (c) 2022 China Telecom Digital Intelligence. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from manilaclient.tests.functional.osc import base class TransfersCLITest(base.OSCClientTestBase): def setUp(self): super(TransfersCLITest, self).setUp() self.share_type = self.create_share_type() def test_transfer_create_list_show_delete(self): share = self.create_share(share_type=self.share_type['name'], wait_for_status='available', client=self.user_client) # create share transfer self.create_share_transfer(share['id'], name='transfer_test') self._wait_for_object_status('share', share['id'], 'awaiting_transfer') # Get all transfers transfers = self.listing_result( 'share', 'transfer list', client=self.user_client) # We must have at least one transfer self.assertTrue(len(transfers) > 0) self.assertTableStruct(transfers, [ 'ID', 'Name', 'Resource Type', 'Resource Id', ]) # grab the transfer we created transfer = [transfer for transfer in transfers if transfer['Resource Id'] == share['id']] self.assertEqual(1, len(transfer)) show_transfer = self.dict_result('share', f'transfer show {transfer[0]["ID"]}') self.assertEqual(transfer[0]['ID'], show_transfer['id']) expected_keys = ( 'id', 'created_at', 'name', 'resource_type', 'resource_id', 'source_project_id', 'destination_project_id', 'accepted', 'expires_at', ) for key in expected_keys: self.assertIn(key, show_transfer) # filtering by Resource ID filtered_transfers = self.listing_result( 'share', f'transfer list --resource-id {share["id"]}', client=self.user_client) self.assertEqual(1, len(filtered_transfers)) self.assertEqual(show_transfer['resource_id'], share["id"]) # finally delete transfer and share self.openstack(f'share transfer delete {show_transfer["id"]}') self._wait_for_object_status('share', share['id'], 'available') def test_transfer_accept(self): share = self.create_share(share_type=self.share_type['name'], wait_for_status='available', client=self.user_client) # create share transfer transfer = self.create_share_transfer(share['id'], name='transfer_test') self._wait_for_object_status('share', share['id'], 'awaiting_transfer') # accept share transfer self.openstack( f'share transfer accept {transfer["id"]} {transfer["auth_key"]}') self._wait_for_object_status('share', share['id'], 'available') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_share_types.py0000664000175000017500000001244000000000000030215 0ustar00zuulzuul00000000000000# 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 json from manilaclient.tests.functional.osc import base class ShareTypesCLITest(base.OSCClientTestBase): def test_share_type_create(self): name = 'test_share_type' description = 'Description' share_type = self.create_share_type( name=name, description=description) self.assertEqual(name, share_type["name"]) self.assertEqual(description, share_type["description"]) self.assertEqual('public', share_type["visibility"]) share_types_list = self.listing_result('share type', 'list') self.assertIn( share_type["id"], [item['ID'] for item in share_types_list]) def test_share_type_create_specs(self): share_type = self.create_share_type( snapshot_support=True, create_share_from_snapshot_support=True, revert_to_snapshot_support=True, mount_snapshot_support=True, extra_specs={ "foo": "bar", "manila": "zorilla" }, formatter='json') required_specs = share_type["required_extra_specs"] optional_specs = share_type["optional_extra_specs"] self.assertEqual( False, required_specs["driver_handles_share_servers"]) self.assertEqual('True', optional_specs["snapshot_support"]) self.assertEqual( 'True', optional_specs["create_share_from_snapshot_support"]) self.assertEqual( 'True', optional_specs["revert_to_snapshot_support"]) self.assertEqual('True', optional_specs["mount_snapshot_support"]) self.assertEqual("bar", optional_specs["foo"]) self.assertEqual("zorilla", optional_specs["manila"]) def test_share_type_delete(self): share_type_1 = self.create_share_type(add_cleanup=False) share_type_2 = self.create_share_type(add_cleanup=False) self.openstack( f'share type delete {share_type_1["id"]} {share_type_2["id"]}' ) self.check_object_deleted('share type', share_type_1["id"]) self.check_object_deleted('share type', share_type_2["id"]) def test_share_type_set(self): share_type = self.create_share_type() self.openstack( f'share type set {share_type["id"]} --description Description' ' --name Name --public false --extra-specs foo=bar' ) share_type = json.loads(self.openstack( f'share type show {share_type["id"]} -f json' )) self.assertEqual('Description', share_type["description"]) self.assertEqual('Name', share_type["name"]) self.assertEqual('private', share_type["visibility"]) self.assertEqual( 'bar', share_type["optional_extra_specs"]["foo"]) def test_share_type_unset(self): share_type = self.create_share_type( snapshot_support=True, extra_specs={'foo': 'bar'}) self.openstack( f'share type unset {share_type["id"]} ' 'snapshot_support foo') share_type = json.loads(self.openstack( f'share type show {share_type["id"]} -f json' )) self.assertNotIn('foo', share_type["optional_extra_specs"]) self.assertNotIn( 'snapshot_support', share_type["optional_extra_specs"]) def test_share_type_list(self): share_type_1 = self.create_share_type(public=False) share_type_2 = self.create_share_type( extra_specs={'foo': 'bar'}) types_list = self.listing_result( 'share type', 'list --all', client=self.admin_client) self.assertTableStruct(types_list, [ 'ID', 'Name', 'Visibility', 'Is Default', 'Required Extra Specs', 'Optional Extra Specs', 'Description' ]) id_list = [item['ID'] for item in types_list] self.assertIn(share_type_1['id'], id_list) self.assertIn(share_type_2['id'], id_list) types_list = self.listing_result( 'share type', 'list --extra-specs foo=bar') id_list = [item['ID'] for item in types_list] self.assertNotIn(share_type_1['id'], id_list) self.assertIn(share_type_2['id'], id_list) def test_share_type_show(self): share_type = self.create_share_type( extra_specs={'foo': 'bar'}) result = json.loads(self.openstack( f'share type show {share_type["id"]} -f json')) self.assertEqual(share_type["name"], result["name"]) self.assertEqual( share_type["visibility"], result["visibility"]) self.assertEqual( share_type["is_default"], str(result["is_default"])) self.assertEqual( 'bar', result["optional_extra_specs"]["foo"]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_shares.py0000664000175000017500000002066200000000000027161 0ustar00zuulzuul00000000000000# 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 from tempest.lib.common.utils import data_utils 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]) def test_openstack_share_set(self): share = self.create_share() self.openstack(f'share set {share["id"]} --name new_name ' f'--property key=value') result = self.dict_result('share', f'show {share["id"]}') self.assertEqual(share['id'], result['id']) self.assertEqual('new_name', result['name']) self.assertEqual("key='value'", result['properties']) def test_openstack_share_unset(self): share = self.create_share(name='test_name', properties={ 'foo': 'bar', 'test_key': 'test_value'}) result1 = self.dict_result('share', f'show {share["id"]}') self.assertEqual(share['id'], result1['id']) self.assertEqual(share['name'], result1['name']) self.assertEqual("foo='bar', test_key='test_value'", result1['properties']) self.openstack(f'share unset {share["id"]} --name --property test_key') result2 = self.dict_result('share', f'show {share["id"]}') self.assertEqual(share['id'], result2['id']) self.assertEqual('None', result2['name']) self.assertEqual("foo='bar'", result2['properties']) def test_openstack_share_soft_delete(self): share = self.create_share(name='test_share') result1 = self.dict_result('share', f'show {share["id"]}') self.assertEqual(share['id'], result1['id']) self.assertEqual(share['name'], result1['name']) self.openstack(f'share delete {share["id"]} --soft') self.check_object_deleted('share', share['id']) shares_list_after_delete = self.listing_result('share', 'list ' '--soft-deleted') self.assertIn( share['id'], [item['ID'] for item in shares_list_after_delete]) def test_openstack_share_resize(self): share = self.create_share() self.openstack(f'share resize {share["id"]} 10 --wait ') result = self.dict_result('share', f'show {share["id"]}') self.assertEqual('10', result['size']) def test_openstack_share_revert(self): slug = "revert_test" share_type = self.create_share_type( name=data_utils.rand_name(slug), snapshot_support=True, revert_to_snapshot_support=True) share = self.create_share(share_type=share_type['id'], size=10) snapshot = self.create_snapshot(share['id'], wait=True) self.assertEqual(snapshot['size'], share['size']) self.openstack(f'share resize {share["id"]} 15 --wait') result1 = self.dict_result('share', f'show {share["id"]}') self.assertEqual('15', result1["size"]) self.openstack(f'share revert {snapshot["id"]} --wait') result2 = self.dict_result('share', f'show {share["id"]}') self.assertEqual('10', result2['size']) def test_openstack_share_abandon_adopt(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]) export_location_obj = self.get_share_export_locations(share['id'])[0] export_location = export_location_obj['Path'] source = self.dict_result('share', f'show {share["id"]}') host = source['host'] protocol = source['share_proto'] share_type = source['share_type'] self.openstack(f'share abandon {share["id"]} --wait') # verify abandonded 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]) result = self.dict_result( 'share', f'adopt {host} {protocol} {export_location} ' f'--share-type {share_type} --wait') # verify adopted self.assertEqual(host, result['host']) self.assertEqual(protocol, result['share_proto']) self.openstack(f'share delete {result["id"]} --wait') def test_openstack_share_export_location_show(self): share = self.create_share() share_export_locations = self.get_share_export_locations(share["id"]) result_export_locations = self.listing_result( 'share', f'export location list {share["id"]}') for share_export in share_export_locations: export_location = self.dict_result( 'share', f'export location show {share["id"]} ' f'{share_export["ID"]}') self.assertIn(export_location["id"], [item["ID"] for item in result_export_locations]) def test_openstack_share_export_location_list(self): share = self.create_share() share_export_locations = self.get_share_export_locations(share["id"]) result_export_locations = self.listing_result( 'share', f'export location list {share["id"]}') self.assertTableStruct(result_export_locations, [ 'ID', 'Path' ]) export_location_ids = [el['ID'] for el in share_export_locations] for share_export in result_export_locations: self.assertIn(share_export["ID"], export_location_ids) def test_openstack_share_restore(self): share = self.create_share(name='test_share') result1 = self.dict_result('share', f'show {share["id"]}') self.assertEqual(share['id'], result1['id']) self.assertEqual(share['name'], result1['name']) self.openstack(f'share delete {share["id"]} --soft') 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]) self.openstack(f'share restore {share["id"]} ') shares_list_after_restore = self.listing_result('share', 'list') self.assertIn( share['id'], [item['ID'] for item in shares_list_after_restore]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/osc/test_shares_group_type.py0000664000175000017500000001373700000000000031443 0ustar00zuulzuul00000000000000# 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_serialization import jsonutils from tempest.lib.common.utils import data_utils from manilaclient.tests.functional.osc import base class SharesGroupTypesCLITest(base.OSCClientTestBase): def test_share_group_type_create(self): share_group_type_name = 'test_share_group_type_create' share_group_type = self.create_share_group_type( name=share_group_type_name, share_types='dhss_false') self.assertEqual(share_group_type['name'], share_group_type_name) self.assertIsNotNone(share_group_type['share_types']) shares_group_type_list = self.listing_result( 'share', 'group type list') self.assertIn( share_group_type['id'], [item['ID'] for item in shares_group_type_list]) def test_openstack_share_group_type_list(self): share_group_type_name = data_utils.rand_name( 'test_share_group_type_create') share_group_type = self.create_share_group_type( name=share_group_type_name, share_types='dhss_false') shares_group_type_list = self.listing_result( 'share', 'group type list') self.assertTableStruct(shares_group_type_list, [ 'ID', 'Name', 'Share Types', 'Visibility', 'Is Default', 'Group Specs' ]) self.assertIn( share_group_type['id'], [item['ID'] for item in shares_group_type_list]) def test_openstack_share_group_type_show(self): share_group_type_name = data_utils.rand_name( 'test_share_group_type_create') share_type_name = 'dhss_false' share_group_type = self.create_share_group_type( name=share_group_type_name, share_types=share_type_name) shares_group_type_show = self.dict_result( 'share', f'group type show {share_group_type_name}') share_type_id = self.dict_result( 'share', f'type show {share_type_name}')['id'] expected_sgt_values = { 'id': share_group_type['id'], 'name': share_group_type_name, 'share_types': share_type_id, 'visibility': 'public', 'is_default': 'False', 'group_specs': '' } for k, v in shares_group_type_show.items(): self.assertEqual(expected_sgt_values[k], shares_group_type_show[k]) def test_openstack_share_group_type_set(self): share_group_type_name = data_utils.rand_name( 'test_share_group_type_create') share_type_name = 'dhss_false' share_group_type = self.create_share_group_type( name=share_group_type_name, share_types=share_type_name) shares_group_type_show = self.openstack( f'share group type show {share_group_type_name} -f json') shares_group_type_show = jsonutils.loads(shares_group_type_show) expected_sgt_values = { 'id': share_group_type['id'], 'group_specs': {} } for k, v in expected_sgt_values.items(): self.assertEqual(expected_sgt_values[k], shares_group_type_show[k] ) group_snap_key = 'snapshot_support' group_snap_value = 'False' group_specs = f"{group_snap_key}={group_snap_value}" self.dict_result( 'share', f'group type set {share_group_type_name} ' f'--group-specs {group_specs}') shares_group_type_show = self.openstack( f'share group type show {share_group_type_name} -f json') shares_group_type_show = jsonutils.loads(shares_group_type_show) expected_sgt_values = { 'id': share_group_type['id'], 'group_specs': { group_snap_key: group_snap_value } } for k, v in expected_sgt_values.items(): self.assertEqual(expected_sgt_values[k], shares_group_type_show[k]) def test_openstack_share_group_type_unset(self): share_group_type_name = data_utils.rand_name( 'test_share_group_type_create') group_snap_key = 'snapshot_support' group_snap_value = 'False' group_specs = f"{group_snap_key}={group_snap_value}" share_group_type = self.create_share_group_type( name=share_group_type_name, share_types='dhss_false', group_specs=group_specs) shares_group_type_show = self.openstack( f'share group type show {share_group_type_name} -f json') shares_group_type_show = jsonutils.loads(shares_group_type_show) expected_sgt_values = { 'id': share_group_type['id'], 'group_specs': { group_snap_key: group_snap_value } } for k, v in expected_sgt_values.items(): self.assertEqual(expected_sgt_values[k], shares_group_type_show[k] ) self.dict_result( 'share', f'group type unset {share_group_type_name} ' f'{group_snap_key}') shares_group_type_show = self.openstack( f'share group type show {share_group_type_name} -f json') shares_group_type_show = jsonutils.loads(shares_group_type_show) expected_sgt_values = { 'id': share_group_type['id'], 'group_specs': {} } for k, v in expected_sgt_values.items(): self.assertEqual(expected_sgt_values[k], shares_group_type_show[k]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_availability_zones.py0000664000175000017500000000414200000000000030773 0ustar00zuulzuul00000000000000# 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']) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_common.py0000664000175000017500000000461200000000000026375 0ustar00zuulzuul00000000000000# 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:') try: # TODO(gouthamr): Drop when py3.10 becomes min supported version cmds_end = lines.index('Optional arguments:') except ValueError: cmds_end = lines.index('Options:') command_pattern = re.compile(r'^ {4}([a-z0-9\-\_]+)') for line in lines[cmds_start:cmds_end]: match = command_pattern.match(line) if match: commands.append(match.group(1)) commands = set(commands) wanted_commands = set(( 'absolute-limits', 'list', 'help', 'quota-show', 'access-list', 'snapshot-list', 'access-allow', 'access-deny', 'share-network-list', 'security-service-list')) self.assertFalse(wanted_commands - commands) @ddt.data('admin', 'user') def test_credentials(self, role): self.clients[role].manila('credentials') @ddt.data('admin', 'user') def test_list_extensions(self, role): roles = self.parser.listing( self.clients[role].manila('list-extensions')) self.assertTableStruct(roles, ['Name', 'Summary', 'Alias', 'Updated']) @ddt.data('admin', 'user') def test_endpoints(self, role): self.clients[role].manila('endpoints') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_export_locations.py0000664000175000017500000001501400000000000030477 0ustar00zuulzuul00000000000000# 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]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_limits.py0000664000175000017500000000175200000000000026410 0ustar00zuulzuul00000000000000# 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') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_messages.py0000664000175000017500000000530300000000000026712 0ustar00zuulzuul00000000000000# 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']) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_quotas.py0000664000175000017500000003143500000000000026424 0ustar00zuulzuul00000000000000# 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") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_scheduler_stats.py0000664000175000017500000000355000000000000030301 0ustar00zuulzuul00000000000000# 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') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_security_services.py0000664000175000017500000000524700000000000030664 0ustar00zuulzuul00000000000000# 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' self.default_ad_site = 'fake_default_ad_site' @ddt.data( {'name': 'test_name'}, {'description': 'test_description'}, {'user': 'test_username'}, {'password': 'test_password'}, {'server': 'test_server'}, {'default_ad_site': 'test_default_ad_site'}, {'domain': 'test_domain'}, {'dns_ip': 'test_dns_ip'}, {'ou': 'test_ou'}, {'name': '""'}, {'description': '""'}, {'user': '""'}, {'password': '""'}, {'server': '""'}, {'default_ad_site': '""'}, {'domain': '""'}, {'dns_ip': '""'}, {'ou': '""'}, ) def test_create_update_security_service(self, ss_data): expected_data = { 'name': self.name, 'description': self.description, 'user': self.user, 'password': self.password, 'server': self.server, 'domain': self.domain, 'dns_ip': self.dns_ip, 'ou': self.ou, } if 'default_ad_site' in ss_data: expected_data.pop('server') expected_data['default_ad_site'] = self.default_ad_site ss = self.create_security_service(**expected_data) update = self.admin_client.update_security_service(ss['id'], **ss_data) expected_data.update(ss_data) for k, v in expected_data.items(): if v == '""': self.assertEqual('None', update[k]) else: self.assertEqual(v, update[k]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_services.py0000664000175000017500000000330100000000000026722 0ustar00zuulzuul00000000000000# 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') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_share_access.py0000664000175000017500000003206700000000000027535 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_share_network_subnets.py0000664000175000017500000001101600000000000031517 0ustar00zuulzuul00000000000000# 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']) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_share_networks.py0000664000175000017500000005620700000000000030152 0ustar00zuulzuul00000000000000# -*- 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 import time from manilaclient import config from manilaclient import exceptions from manilaclient.tests.functional import base from manilaclient.tests.functional import utils SECURITY_SERVICE_UPDATE_VERSION = '2.63' CONF = config.CONF @ddt.ddt class ShareNetworksReadWriteTest(base.BaseTestCase): def setUp(self): super(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) def test_share_network_reset_status(self): share_network = self.create_share_network( client=self.user_client, name='cool_net_name', description='fakedescription', neutron_net_id='fake_neutron_net_id', neutron_subnet_id='fake_neutron_subnet_id', ) # Admin operation self.admin_client.share_network_reset_state( share_network['id'], 'error', microversion=SECURITY_SERVICE_UPDATE_VERSION) self.user_client.wait_for_resource_status( share_network['id'], 'error', microversion=SECURITY_SERVICE_UPDATE_VERSION, resource_type="share_network") def test_share_network_security_service_add(self): share_network = self.create_share_network( client=self.user_client, name='cool_net_name', description='fakedescription', neutron_net_id='fake_neutron_net_id', neutron_subnet_id='fake_neutron_subnet_id', ) new_security_service = self.create_security_service( client=self.user_client) check_result = ( self.user_client.share_network_security_service_add_check( share_network['id'], security_service_id=new_security_service['id'])) self.assertEqual(check_result['compatible'], 'True') self.user_client.share_network_security_service_add( share_network['id'], new_security_service['id']) network_services = ( self.user_client.share_network_security_service_list( share_network['id'])) self.assertEqual(len(network_services), 1) self.assertEqual( network_services[0]['id'], new_security_service['id']) def test_share_network_security_service_update(self): share_network = self.create_share_network( client=self.user_client, name='cool_net_name', description='fakedescription', neutron_net_id='fake_neutron_net_id', neutron_subnet_id='fake_neutron_subnet_id', ) current_name = 'current' new_name = 'new' current_security_service = self.create_security_service( client=self.user_client, name=current_name) new_security_service = self.create_security_service( client=self.user_client, name=new_name) check_result = ( self.user_client.share_network_security_service_add_check( share_network['id'], current_security_service['id'])) self.assertEqual(check_result['compatible'], 'True') self.user_client.share_network_security_service_add( share_network['id'], current_security_service['id']) network_services = ( self.user_client.share_network_security_service_list( share_network['id'])) self.assertEqual(len(network_services), 1) self.assertEqual(network_services[0]['name'], current_name) check_result = ( self.user_client.share_network_security_service_update_check( share_network['id'], current_security_service['id'], new_security_service['id'])) self.assertEqual(check_result['compatible'], 'True') self.user_client.share_network_security_service_update( share_network['id'], current_security_service['id'], new_security_service['id']) network_services = ( self.user_client.share_network_security_service_list( share_network['id'])) self.assertEqual(len(network_services), 1) self.assertEqual(network_services[0]['name'], new_name) def test_share_network_subnet_create_check(self): share_network = self.create_share_network( client=self.user_client, description='fakedescription', ) check_result = ( self.user_client.share_network_subnet_create_check( share_network['id'], neutron_net_id='fake_neutron_net_id', neutron_subnet_id='fake_neutron_subnet_id')) self.assertEqual(check_result['compatible'], 'True') @ddt.data( {'neutron_net_id': None, 'neutron_subnet_id': 'fake_subnet_id'}, {'neutron_net_id': 'fake_net_id', 'neutron_subnet_id': None}, {'availability_zone': 'invalid_availability_zone'}, ) def test_check_add_share_network_subnet_with_invalid_params(self, params): self.assertRaises( tempest_lib_exc.CommandFailed, self.user_client.share_network_subnet_create_check, self.sn['id'], **params) def test_check_add_share_network_subnet_to_invalid_share_network(self): self.assertRaises( tempest_lib_exc.CommandFailed, self.user_client.share_network_subnet_create_check, 'invalid_share_network', self.neutron_net_id, self.neutron_subnet_id) class ShareNetworkSecurityServiceCheckReadWriteTests(base.BaseTestCase): protocol = None def setUp(self): super(ShareNetworkSecurityServiceCheckReadWriteTests, self).setUp() if self.protocol not in CONF.enable_protocols: message = "%s tests are disabled." % self.protocol raise self.skipException(message) self.client = self.get_user_client() if not self.client.share_network: message = "Can run only with DHSS=True mode" raise self.skipException(message) def _wait_for_update_security_service_compatible_result( self, share_network, current_security_service, new_security_service=None): compatible_expected_result = 'True' check_is_compatible = 'None' tentatives = 0 # There might be a delay from the time the check is requested until # the backend has performed all checks while check_is_compatible != compatible_expected_result: tentatives += 1 if not new_security_service: check_is_compatible = ( self.user_client.share_network_security_service_add_check( share_network['id'], current_security_service['id']))['compatible'] else: check_is_compatible = ( (self.user_client. share_network_security_service_update_check( share_network['id'], current_security_service['id'], new_security_service['id'])))['compatible'] if tentatives > 3: timeout_message = ( "Share network security service add/update check did not " "reach 'compatible=True' within 15 seconds.") raise exceptions.TimeoutException(message=timeout_message) time.sleep(5) def test_check_if_security_service_can_be_added_to_share_network_in_use( self): share_network = self.create_share_network( client=self.user_client, description='fakedescription', neutron_net_id='fake_neutron_net_id', neutron_subnet_id='fake_neutron_subnet_id', ) # Create a share so we can be sure that a share server will exist and # the check will be performed in the backends self.create_share( self.protocol, client=self.user_client, share_network=share_network['id']) current_security_service = self.create_security_service( client=self.user_client) check_result = ( self.user_client.share_network_security_service_add_check( share_network['id'], current_security_service['id'])) self.assertEqual(check_result['compatible'], 'None') self._wait_for_update_security_service_compatible_result( share_network, current_security_service) def test_add_and_update_security_service_when_share_network_is_in_use( self): share_network = self.create_share_network( client=self.user_client, name='cool_net_name', description='fakedescription', neutron_net_id='fake_neutron_net_id', neutron_subnet_id='fake_neutron_subnet_id', ) # Create a share so we can be sure that a share server will exist and # the check will be performed in the backends self.create_share( self.protocol, name='fake_share_name', share_network=share_network['id'], client=self.user_client) current_security_service = self.create_security_service( client=self.user_client, name='current_security_service') new_security_service = self.create_security_service( client=self.user_client, name='new_security_service') check_result = ( self.user_client.share_network_security_service_add_check( share_network['id'], current_security_service['id'])) self.assertEqual(check_result['compatible'], 'None') self._wait_for_update_security_service_compatible_result( share_network, current_security_service) self.user_client.share_network_security_service_add( share_network['id'], current_security_service['id']) network_services = ( self.user_client.share_network_security_service_list( share_network['id'])) self.assertEqual(len(network_services), 1) self.assertEqual( network_services[0]['name'], current_security_service['name']) self.user_client.wait_for_resource_status( share_network['id'], 'active', microversion=SECURITY_SERVICE_UPDATE_VERSION, resource_type="share_network") check_result = ( self.user_client.share_network_security_service_update_check( share_network['id'], current_security_service['id'], new_security_service['id'])) self.assertEqual(check_result['compatible'], 'None') self._wait_for_update_security_service_compatible_result( share_network, current_security_service, new_security_service=new_security_service) self.user_client.share_network_security_service_update( share_network['id'], current_security_service['id'], new_security_service['id']) network_services = ( self.user_client.share_network_security_service_list( share_network['id'])) self.assertEqual(len(network_services), 1) self.assertEqual( network_services[0]['name'], new_security_service['name']) self.user_client.wait_for_resource_status( share_network['id'], 'active', microversion=SECURITY_SERVICE_UPDATE_VERSION, resource_type="share_network") base_security_service_check = ShareNetworkSecurityServiceCheckReadWriteTests class ShareNetworkSecServiceCheckRWNFSTest(base_security_service_check): protocol = 'nfs' class ShareNetworkSecServiceCheckRWTestsCIFSTest(base_security_service_check): protocol = 'cifs' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_share_replica_export_locations.py0000664000175000017500000001042300000000000033357 0ustar00zuulzuul00000000000000# 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]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_share_replicas.py0000664000175000017500000000365600000000000030100 0ustar00zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from manilaclient import config from manilaclient.tests.functional import base from manilaclient.tests.functional import utils CONF = config.CONF @utils.skip_if_microversion_not_supported('2.72') class ShareReplicasTest(base.BaseTestCase): def _create_share_and_replica(self): replication_type = CONF.replication_type share_type = self.create_share_type( driver_handles_share_servers=True, extra_specs={'replication_type': replication_type}) share_network = self.create_share_network() share = self.create_share( share_type=share_type['ID'], share_network=share_network['id'], client=self.get_user_client()) share_replica = self.create_share_replica( share['id'], share_network=share_network['id'], wait_for_creation=True, client=self.get_user_client()) return share, share_replica def test_share_replica_create(self): share, share_replica = self._create_share_and_replica() self.assertEqual(share['id'], share_replica['share_id']) def test_share_replica_delete(self): share, share_replica = self._create_share_and_replica() self.user_client.delete_share_replica(share_replica['id']) self.user_client.wait_for_share_replica_deletion(share_replica['id']) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_share_servers.py0000664000175000017500000003367100000000000027767 0ustar00zuulzuul00000000000000# Copyright 2015 Mirantis Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import ast import ddt import testtools from tempest.lib.common.utils import data_utils from tempest.lib import exceptions from manilaclient.common import constants from manilaclient import config from manilaclient.tests.functional import base from manilaclient.tests.functional import utils CONF = config.CONF @ddt.ddt class ShareServersReadOnlyTest(base.BaseTestCase): def setUp(self): super(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-server 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' @ddt.ddt @utils.skip_if_microversion_not_supported('2.57') class ShareServersMigrationBase(base.BaseTestCase): protocol = None def setUp(self): super(ShareServersMigrationBase, self).setUp() if not CONF.run_share_servers_tests: message = "Share-server tests are disabled." raise self.skipException(message) if self.protocol not in CONF.enable_protocols: message = "%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) if not CONF.run_share_servers_migration_tests: message = "Share server migration tests are disabled." raise self.skipException(message) def _create_share_and_share_network(self): name = data_utils.rand_name('autotest_share_name') description = data_utils.rand_name('autotest_share_description') common_share_network = self.client.get_share_network( self.client.share_network) share_net_info = utils.get_default_subnet(self.client, common_share_network['id']) neutron_net_id = ( share_net_info['neutron_net_id'] if 'none' not in share_net_info['neutron_net_id'].lower() else None) neutron_subnet_id = ( share_net_info['neutron_subnet_id'] if 'none' not in share_net_info['neutron_subnet_id'].lower() else None) share_network = self.client.create_share_network( neutron_net_id=neutron_net_id, neutron_subnet_id=neutron_subnet_id, ) share_type = self.create_share_type( data_utils.rand_name('test_share_type'), driver_handles_share_servers=True) share = self.create_share( share_protocol=self.protocol, size=1, name=name, description=description, share_type=share_type['ID'], share_network=share_network['id'], client=self.client, wait_for_creation=True ) share = self.client.get_share(share['id']) return share, share_network @ddt.data('cancel', 'complete') def test_share_server_migration(self, operation): # Create a share and share network to be used in the tests. share, share_network = self._create_share_and_share_network() share_server_id = share['share_server_id'] src_host = share['host'].split('#')[0] pools = self.admin_client.pool_list(detail=True) host_list = list() # Filter the backends DHSS True and different # than the source host. for hosts in pools: host_name = hosts['Name'].split('#')[0] if (ast.literal_eval(hosts['Capabilities']).get( 'driver_handles_share_servers') and host_name != src_host): host_list.append(host_name) host_list = list(set(host_list)) # If not found any host we need skip the test. if len(host_list) == 0: raise self.skipException("No hosts available for " "share server migration.") dest_backend = None # If found at least one host, we still need to verify the # share server migration compatibility with the destination host. for host in host_list: compatibility = self.admin_client.share_server_migration_check( server_id=share_server_id, dest_host=host, writable=False, nondisruptive=False, preserve_snapshots=False, new_share_network=None) # If found at least one compatible host, we will use it. if compatibility['compatible']: dest_host = host # If not found, we need skip the test. if dest_backend is not None: raise self.skipException("No hosts compatible to perform a " "share server migration.") # Start the share server migration self.admin_client.share_server_migration_start( share_server_id, dest_host) server = self.admin_client.get_share_server(share_server_id) share = self.admin_client.get_share(share['id']) self.assertEqual(constants.STATUS_SERVER_MIGRATING, share['status']) # Wait for the share server migration driver phase 1 done. task_state = constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE server = self.admin_client.wait_for_server_migration_task_state( share_server_id, dest_host, task_state) migration_progress = ( self.admin_client.share_server_migration_get_progress( share_server_id)) dest_share_server_id = migration_progress.get( 'destination_share_server_id') # Call share server migration complete or cancel operations # according the ddt. if operation == 'complete': task_state = constants.TASK_STATE_MIGRATION_SUCCESS self.admin_client.share_server_migration_complete( share_server_id) server = self.admin_client.wait_for_server_migration_task_state( dest_share_server_id, dest_host, task_state) self.admin_client.wait_for_share_server_deletion(share_server_id) else: self.admin_client.share_server_migration_cancel(server['id']) task_state = constants.TASK_STATE_MIGRATION_CANCELLED # Wait for the respectives task state for each operation above. server = self.admin_client.wait_for_server_migration_task_state( server['id'], dest_host, task_state) # Check if the share is available again. share = self.admin_client.get_share(share['id']) self.assertEqual('available', share['status']) class ShareServersMigrationNFSTest(ShareServersMigrationBase): protocol = 'nfs' class ShareServersMigrationCIFSTest(ShareServersMigrationBase): protocol = 'cifs' def load_tests(loader, tests, _): result = [] for test_case in tests: if type(test_case._tests[0]) is ShareServersReadWriteBase: continue if type(test_case._tests[0]) is ShareServersMigrationBase: continue result.append(test_case) return loader.suiteClass(result) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_share_transfers.py0000664000175000017500000000772200000000000030303 0ustar00zuulzuul00000000000000# Copyright (c) 2022 China Telecom Digital Intelligence. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest.lib.common.utils import data_utils from manilaclient.tests.functional import base class ShareTransferTests(base.BaseTestCase): """Check of base share transfers command""" def setUp(self): super(ShareTransferTests, self).setUp() self.share_type = self.create_share_type( name=data_utils.rand_name('test_share_type'), driver_handles_share_servers=False) def test_transfer_create_list_show_delete(self): """Create, list, show and delete a share transfer""" self.skip_if_microversion_not_supported('2.77') share = self.create_share( share_protocol='nfs', size=1, name=data_utils.rand_name('autotest_share_name'), client=self.user_client, share_type=self.share_type['ID'], use_wait_option=True) self.assertEqual("available", share['status']) # create share transfer transfer = self.create_share_transfer(share['id'], name='test_share_transfer') self.assertIn('auth_key', transfer) # list share transfers transfers = self.list_share_transfer() # We must have at least one transfer self.assertTrue(len(transfers) > 0) # show the share transfer transfer_show = self.get_share_transfer(transfer['id']) self.assertEqual(transfer_show['name'], transfer['name']) self.assertNotIn('auth_key', transfer_show) # delete share transfer self.delete_share_transfer(transfer['id']) self.user_client.wait_for_transfer_deletion(transfer['id']) share = self.user_client.get_share(share['id']) self.assertEqual("available", share['status']) # finally delete the share self.user_client.delete_share(share['id']) self.user_client.wait_for_share_deletion(share['id']) def test_transfer_accept(self): """Show share transfer accept""" self.skip_if_microversion_not_supported('2.77') share = self.create_share( share_protocol='nfs', size=1, name=data_utils.rand_name('autotest_share_name'), client=self.user_client, share_type=self.share_type['ID'], use_wait_option=True) self.assertEqual("available", share['status']) # create share transfer transfer = self.create_share_transfer(share['id'], name='test_share_transfer') share = self.user_client.get_share(share['id']) transfer_id = transfer['id'] auth_key = transfer['auth_key'] self.assertEqual("share", transfer['resource_type']) self.assertEqual('test_share_transfer', transfer['name']) self.assertEqual("awaiting_transfer", share['status']) # accept the share transfer self.accept_share_transfer(transfer_id, auth_key) # after accept complete, the transfer will be deleted. self.user_client.wait_for_transfer_deletion(transfer_id) share = self.user_client.get_share(share['id']) # check share status roll back to available self.assertEqual("available", share['status']) # finally delete the share self.user_client.delete_share(share['id']) self.user_client.wait_for_share_deletion(share['id']) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_share_types.py0000664000175000017500000005002000000000000027425 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_shares.py0000664000175000017500000002733700000000000026403 0ustar00zuulzuul00000000000000# Copyright 2015 Mirantis Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import ddt import testtools from tempest.lib.common.utils import data_utils from tempest.lib import exceptions from manilaclient.common import constants from manilaclient import config from manilaclient.tests.functional import base from manilaclient.tests.functional import utils CONF = config.CONF @ddt.ddt class SharesReadWriteBase(base.BaseTestCase): protocol = None def setUp(self): super(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("creating", create['status']) self.assertEqual(name, create['name']) self.assertEqual('1', create['size']) self.assertEqual(self.protocol.upper(), create['share_proto']) self.user_client.delete_share(create['id']) self.user_client.wait_for_share_deletion(create['id']) def test_create_update_share(self): name = data_utils.rand_name('autotest_share_name') new_name = 'new_' + name description = data_utils.rand_name('autotest_share_description') new_description = 'new_' + description create = self.create_share( self.protocol, name=name, description=description, client=self.user_client) self.assertEqual(name, create['name']) self.assertEqual(description, create['description']) self.assertEqual('False', create['is_public']) self.user_client.update_share( create['id'], name=new_name, description=new_description) get = self.user_client.get_share(create['id']) self.assertEqual(new_name, get['name']) self.assertEqual(new_description, get['description']) self.assertEqual('False', get['is_public']) # only admins operating at system scope can set a share to be public self.admin_client.update_share(create['id'], is_public=True) get = self.user_client.get_share(create['id']) self.assertEqual(new_name, get['name']) self.assertEqual(new_description, get['description']) self.assertEqual('True', get['is_public']) def test_get_share(self): get = self.user_client.get_share(self.share['id']) self.assertEqual(self.name, get['name']) self.assertEqual(self.description, get['description']) self.assertEqual('1', get['size']) self.assertEqual(self.protocol.upper(), get['share_proto']) def test_create_delete_with_wait(self): name = data_utils.rand_name('share-with-wait-%s') description = data_utils.rand_name('we-wait-until-share-is-ready') share_1, share_2 = ( self.create_share(self.protocol, name=(name % num), description=description, use_wait_option=True, client=self.user_client) for num in range(0, 2) ) self.assertEqual("available", share_1['status']) self.assertEqual("available", share_2['status']) self.delete_share([share_1['id'], share_2['id']], wait=True, client=self.user_client) for share in (share_1, share_2): self.assertRaises(exceptions.NotFound, self.user_client.get_share, share['id']) def test_create_soft_delete_and_restore_share(self): self.skip_if_microversion_not_supported('2.69') microversion = '2.69' description = data_utils.rand_name('we-wait-until-share-is-ready') share = self.create_share(self.protocol, name='share_name', description=description, use_wait_option=True, client=self.user_client) self.assertEqual("available", share['status']) # soft delete the share to recycle bin self.soft_delete_share([share['id']], client=self.user_client, microversion=microversion) self.user_client.wait_for_share_soft_deletion(share['id']) # get shares list in recycle bin result = self.user_client.list_shares(is_soft_deleted=True, microversion=microversion) share_ids = [sh['ID'] for sh in result] # check share is in recycle bin self.assertIn(share['id'], share_ids) # restore the share from recycle bin self.restore_share([share['id']], client=self.user_client, microversion=microversion) self.user_client.wait_for_share_restore(share['id']) result1 = self.user_client.list_shares(is_soft_deleted=True, microversion=microversion) share_ids1 = [sh['ID'] for sh in result1] # check share not in recycle bin self.assertNotIn(share['id'], share_ids1) @ddt.ddt class SharesTestMigration(base.BaseTestCase): def setUp(self): super(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' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_shares_listing.py0000664000175000017500000003510400000000000030123 0ustar00zuulzuul00000000000000# -*- 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.soft_name = data_utils.rand_name('soft_delete_share_name') self.admin_private_share = self.create_share( name=self.admin_private_name, description=self.admin_private_description, public=False, client=None, wait_for_creation=False) self.private_share = self.create_share( name=self.private_name, description=self.private_description, public=False, client=self.get_user_client(), wait_for_creation=False) self.public_share = self.create_share( name=self.public_name, description=self.public_description, public=True, client=self.admin_client) self.wait_soft_delete_share = self.create_share( name=self.soft_name, public=False, client=self.get_user_client(), wait_for_creation=False) self.shares_created = (self.private_share['id'], self.public_share['id'], self.admin_private_share['id'], self.wait_soft_delete_share['id']) for share_id in self.shares_created: self.admin_client.wait_for_resource_status( share_id, constants.STATUS_AVAILABLE) self.soft_delete_share([self.wait_soft_delete_share['id']], client=self.get_user_client(), microversion='2.69') def _list_shares(self, filters=None): filters = filters or dict() shares = self.user_client.list_shares(filters=filters) self.assertGreater(len(shares), 0) if filters: for share in shares: try: share_get = self.user_client.get_share(share['ID']) except exceptions.NotFound: # NOTE(vponomaryov): Case when some share was deleted # between our 'list' and 'get' requests. Skip such case. # It occurs with concurrently running tests. continue if 'migrating' in share_get['status']: # all bets are off, a fair chance share migration # started between the 'list' and 'get' requests. No need # to verify these shares. continue for filter_key, expected_value in filters.items(): if filter_key in ('share_network', 'share-network'): filter_key = 'share_network_id' if share_get[filter_key] != expected_value: # Possibly a mismatch because the share was # migrated to a new network in the time that # elapsed between the 'list' and 'get' requests. # If this isn't one of the shares created in # this class, don't worry about such mismatches self.assertNotIn(share_get['id'], self.shares_created) continue if (expected_value != 'deleting' and share_get[filter_key] == 'deleting'): continue self.assertEqual(expected_value, share_get[filter_key]) def test_list_shares(self): self._list_shares() @ddt.data(1, 0) def test_list_shares_for_all_tenants(self, all_tenants): shares = self.admin_client.list_shares(all_tenants=all_tenants) self.assertLessEqual(1, len(shares)) if all_tenants: self.assertTrue(all('Project ID' in s for s in shares)) for s_id in (self.private_share['id'], self.public_share['id'], self.admin_private_share['id']): self.assertTrue(any(s_id == s['ID'] for s in shares)) else: self.assertTrue(all('Project ID' not in s for s in shares)) self.assertTrue(any(self.admin_private_share['id'] == s['ID'] for s in shares)) if self.private_share['project_id'] != ( self.admin_private_share['project_id']): for s_id in ( self.private_share['id'], self.public_share['id']): self.assertFalse(any(s_id == s['ID'] for s in shares)) @ddt.data(True, False) def test_list_shares_with_public(self, public): shares = self.user_client.list_shares(is_public=public) self.assertGreater(len(shares), 1) if public: self.assertTrue(all('Project ID' in s for s in shares)) else: self.assertTrue(all('Project ID' not in s for s in shares)) def test_list_shares_by_name(self): shares = self.user_client.list_shares( filters={'name': self.private_name}) self.assertEqual(1, len(shares)) self.assertTrue( any(self.private_share['id'] == s['ID'] for s in shares)) for share in shares: get = self.user_client.get_share(share['ID']) self.assertEqual(self.private_name, get['name']) def test_list_shares_by_share_type(self): share_type_id = self.user_client.get_share_type( self.private_share['share_type'])['ID'] # NOTE(vponomaryov): this is API 2.6+ specific self._list_shares({'share_type': share_type_id}) def test_list_shares_by_status(self): self._list_shares({'status': 'available'}) def test_list_shares_by_project_id(self): project_id = self.user_client.get_project_id( self.user_client.tenant_name) self._list_shares({'project_id': project_id}) @testtools.skipUnless( CONF.share_network, "Usage of Share networks is disabled") def test_list_shares_by_share_network(self): share_network_id = self.user_client.get_share_network( CONF.share_network)['id'] self._list_shares({'share_network': share_network_id}) @ddt.data( {'limit': 1}, {'limit': 2}, {'limit': 1, 'offset': 1}, {'limit': 2, 'offset': 0}, ) def test_list_shares_with_limit(self, filters): shares = self.user_client.list_shares(filters=filters) self.assertEqual(filters['limit'], len(shares)) def test_list_share_select_column(self): shares = self.user_client.list_shares(columns="Name,Size") self.assertTrue(any(s['Name'] is not None for s in shares)) self.assertTrue(any(s['Size'] is not None for s in shares)) self.assertTrue(all('Description' not in s for s in shares)) @ddt.data('ID', 'Path') def test_list_shares_by_export_location(self, option): export_locations = self.admin_client.list_share_export_locations( self.public_share['id']) shares = self.admin_client.list_shares( filters={'export_location': export_locations[0][option]}) self.assertEqual(1, len(shares)) self.assertTrue( any(self.public_share['id'] == s['ID'] for s in shares)) for share in shares: get = self.admin_client.get_share(share['ID']) self.assertEqual(self.public_name, get['name']) @ddt.data('ID', 'Path') def test_list_share_instances_by_export_location(self, option): export_locations = self.admin_client.list_share_export_locations( self.public_share['id']) share_instances = self.admin_client.list_share_instances( filters={'export_location': export_locations[0][option]}) self.assertEqual(1, len(share_instances)) share_instance_id = share_instances[0]['ID'] except_export_locations = ( self.admin_client.list_share_instance_export_locations( share_instance_id)) self.assertGreater(len(except_export_locations), 0) self.assertTrue( any(export_locations[0][option] == e[option] for e in except_export_locations)) def test_list_share_by_export_location_with_invalid_version(self): self.assertRaises( exceptions.CommandFailed, self.admin_client.list_shares, filters={'export_location': 'fake'}, microversion='2.34') def test_list_share_instance_by_export_location_invalid_version(self): self.assertRaises( exceptions.CommandFailed, self.admin_client.list_share_instances, filters={'export_location': 'fake'}, microversion='2.34') @ddt.data('name', 'description') def test_list_shares_by_inexact_option(self, option): shares = self.user_client.list_shares( filters={option + '~': option}) # We know we have to have atleast three shares. # Due to test concurrency, there can be # more than three shares (some created by other tests). self.assertGreaterEqual(len(shares), 3) self.assertTrue( any(self.private_share['id'] == s['ID'] for s in shares)) def test_list_shares_by_inexact_unicode_option(self): self.create_share( name=u'共享名称', description=u'共享描述', client=self.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)) def test_list_shares_in_recycle_bin(self): shares = self.user_client.list_shares(is_soft_deleted=True) self.assertTrue( any(self.wait_soft_delete_share['id'] == s['ID'] for s in shares)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_shares_metadata.py0000664000175000017500000001241700000000000030234 0ustar00zuulzuul00000000000000# 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]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_snapshot_access.py0000664000175000017500000001667700000000000030303 0ustar00zuulzuul00000000000000# 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, 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_snapshot_instances.py0000664000175000017500000001155000000000000031012 0ustar00zuulzuul00000000000000# 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') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_snapshot_instances_export_locations.py0000664000175000017500000001103200000000000034461 0ustar00zuulzuul00000000000000# 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'])) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/test_snapshots_export_locations.py0000664000175000017500000000700000000000000032575 0ustar00zuulzuul00000000000000# 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'])) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/functional/utils.py0000664000175000017500000001153300000000000025206 0ustar00zuulzuul00000000000000# 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 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 str(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 str(pair[1]).lower() == 'true' else False if str(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') ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.577273 python-manilaclient-4.8.0/manilaclient/tests/unit/0000775000175000017500000000000000000000000022306 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/__init__.py0000664000175000017500000000000000000000000024405 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.577273 python-manilaclient-4.8.0/manilaclient/tests/unit/common/0000775000175000017500000000000000000000000023576 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/common/__init__.py0000664000175000017500000000000000000000000025675 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.577273 python-manilaclient-4.8.0/manilaclient/tests/unit/common/apiclient/0000775000175000017500000000000000000000000025546 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/common/apiclient/__init__.py0000664000175000017500000000000000000000000027645 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/common/test_httpclient.py0000664000175000017500000001772100000000000027375 0ustar00zuulzuul00000000000000# 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 from unittest import mock import ddt 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() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/fakes.py0000664000175000017500000000556700000000000023766 0ustar00zuulzuul00000000000000# 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. """ 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[0], expected[1], 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 ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.577273 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/0000775000175000017500000000000000000000000023072 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/__init__.py0000664000175000017500000000000000000000000025171 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/osc_fakes.py0000664000175000017500000001472200000000000025407 0ustar00zuulzuul00000000000000# 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 sys from unittest import mock from keystoneauth1 import fixture 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) 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 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 FakeLimitsResource(FakeResource): class AbsoluteLimit: def __init__(self, name, value): self.name = name self.value = value class RateLimit: def __init__(self, verb, uri, regex, value, remaining, unit, next_available): self.verb = verb self.uri = uri self.regex = regex self.value = value self.remaining = remaining self.unit = unit self.next_available = next_available @property def absolute(self): return [ self.AbsoluteLimit(key, value) for key, value in self._info['absolute_limit'].items() ] @property def rate(self): rate = self._info['rate_limit'] return [ self.RateLimit( rate['verb'], rate['uri'], rate['regex'], rate['value'], rate['remaining'], rate['unit'], rate['next-available']) ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/osc_utils.py0000664000175000017500000000477300000000000025463 0ustar00zuulzuul00000000000000# 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5852752 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/0000775000175000017500000000000000000000000023421 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/__init__.py0000664000175000017500000000000000000000000025520 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/fakes.py0000664000175000017500000014467100000000000025101 0ustar00zuulzuul00000000000000# 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 random from unittest import mock 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.transfers = mock.Mock() self.share_access_rules = mock.Mock() self.share_groups = mock.Mock() self.share_types = mock.Mock() self.share_type_access = mock.Mock() self.quotas = mock.Mock() self.quota_classes = mock.Mock() self.share_backups = mock.Mock() self.share_snapshots = mock.Mock() self.share_group_snapshots = mock.Mock() self.share_snapshot_export_locations = mock.Mock() self.share_snapshot_instances = mock.Mock() self.share_replicas = mock.Mock() self.share_replica_export_locations = mock.Mock() self.share_networks = mock.Mock() self.share_network_subnets = mock.Mock() self.security_services = mock.Mock() self.shares.resource_class = osc_fakes.FakeResource(None, {}) self.share_instance_export_locations = mock.Mock() self.share_export_locations = mock.Mock() self.share_snapshot_instance_export_locations = mock.Mock() self.share_export_locations.resource_class = ( osc_fakes.FakeResource(None, {})) self.messages = mock.Mock() self.availability_zones = mock.Mock() self.services = mock.Mock() self.share_instances = mock.Mock() self.pools = mock.Mock() self.limits = mock.Mock() self.share_group_types = mock.Mock() self.share_group_type_access = mock.Mock() self.share_servers = mock.Mock() self.resource_locks = mock.Mock() 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, "scheduler_hints": {}, } # 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 = { "created_at": 'time-' + uuid.uuid4().hex, "fake_path": "/foo/el/path", "fake_share_instance_id": 'share-instance-id' + uuid.uuid4().hex, "fake_uuid": "foo_el_uuid", "id": "id-" + uuid.uuid4().hex, "is_admin_only": False, "preferred": False, "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 @staticmethod def create_share_export_locations(attrs=None, count=2): """Create multiple fake export locations. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share export locations to be faked :return: A list of FakeResource objects """ share_export_locations = [] for n in range(0, count): share_export_locations.append( FakeShareExportLocation. create_one_export_location(attrs)) return share_export_locations 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': 'active', '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 class FakeQuotaSet(object): """Fake quota set""" @staticmethod def create_fake_quotas(attrs=None): """Create a fake quota set :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with project_id, resource and so on """ attrs = attrs or {} quotas_info = { 'gigabytes': 1000, 'id': 'tenant-id-c96a43119a40ec7d01794cb8', 'share_group_snapshots': 50, 'share_groups': 50, 'share_networks': 10, 'shares': 50, 'shapshot_gigabytes': 1000, 'snapshots': 50, 'per_share_gigabytes': -1, } quotas_info.update(attrs) quotas = osc_fakes.FakeResource(info=copy.deepcopy( quotas_info), loaded=True) return quotas class FakeShareSnapshotIntances(object): """Fake a share snapshot instance""" @staticmethod def create_one_snapshot_instance(attrs=None, methods=None): """Create a fake share snapshot instance :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_snapshot_instance = { 'id': 'snapshot-instance-id-' + uuid.uuid4().hex, 'snapshot_id': 'snapshot-id-' + uuid.uuid4().hex, 'status': None, 'created_at': datetime.datetime.now().isoformat(), 'updated_at': datetime.datetime.now().isoformat(), 'share_id': 'share-id-' + uuid.uuid4().hex, 'share_instance_id': 'share-instance-id-' + uuid.uuid4().hex, 'progress': None, 'provider_location': None } share_snapshot_instance.update(attrs) share_snapshot_instance = osc_fakes.FakeResource(info=copy.deepcopy( share_snapshot_instance), methods=methods, loaded=True) return share_snapshot_instance @staticmethod def create_share_snapshot_instances(attrs=None, count=2): """Create multiple fake snapshot instances. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share snapshot instances to be faked :return: A list of FakeResource objects """ share_snapshot_instances = [] for n in range(0, count): share_snapshot_instances.append( FakeShareSnapshot.create_one_snapshot(attrs)) return share_snapshot_instances class FakeShareSnapshotInstancesExportLocations(object): """Fake a share snapshot instance Export Locations""" @staticmethod def create_one_snapshot_instance(attrs=None, methods=None): """Create a fake share snapshot instance export locations :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_snapshot_instance_export_location = { 'id': 'snapshot-instance-export-location-id-' + uuid.uuid4().hex, 'is_admin_only': False, 'path': '0.0.0.0/:fake-share-instance-export-location-id', } share_snapshot_instance_export_location.update(attrs) share_snapshot_instance_export_location = osc_fakes.FakeResource( info=copy.deepcopy( share_snapshot_instance_export_location), methods=methods, loaded=True) return share_snapshot_instance_export_location @staticmethod def create_share_snapshot_instances(attrs=None, count=2): """Create multiple fake snapshot instances. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share snapshot instance locations to be faked :return: A list of FakeResource objects """ share_snapshot_instances = [] for n in range(0, count): share_snapshot_instances.append( FakeShareSnapshot.create_one_snapshot(attrs)) return share_snapshot_instances class FakeShareSnapshot(object): """Fake a share snapshot""" @staticmethod def create_one_snapshot(attrs=None, methods=None): """Create a fake share snapshot :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_snapshot = { 'created_at': datetime.datetime.now().isoformat(), 'description': 'description-' + uuid.uuid4().hex, 'id': 'snapshot-id-' + uuid.uuid4().hex, 'name': 'name-' + uuid.uuid4().hex, 'project_id': 'project-id-' + uuid.uuid4().hex, 'provider_location': None, 'share_id': 'share-id-' + uuid.uuid4().hex, 'share_proto': 'NFS', 'share_size': 1, 'size': 1, 'status': None, 'user_id': 'user-id-' + uuid.uuid4().hex } share_snapshot.update(attrs) share_snapshot = osc_fakes.FakeResource(info=copy.deepcopy( share_snapshot), methods=methods, loaded=True) return share_snapshot @staticmethod def create_share_snapshots(attrs=None, count=2): """Create multiple fake snapshots. :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_snapshots = [] for n in range(0, count): share_snapshots.append( FakeShareSnapshot.create_one_snapshot(attrs)) return share_snapshots class FakeShareTransfer(object): """Fake a share transfer""" @staticmethod def create_one_transfer(attrs=None, methods=None): """Create a fake share transfer :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with id, resource and so on """ attrs = attrs or {} methods = methods or {} now_time = datetime.datetime.now() delta_time = now_time + datetime.timedelta(minutes=5) share_transfer = { 'accepted': 'False', 'auth_key': 'auth-key-' + uuid.uuid4().hex, 'created_at': now_time.isoformat(), 'destination_project_id': None, 'expires_at': delta_time.isoformat(), 'id': 'transfer-id-' + uuid.uuid4().hex, 'name': 'name-' + uuid.uuid4().hex, 'resource_id': 'resource-id-' + uuid.uuid4().hex, 'resource_type': 'share', 'source_project_id': 'source-project-id-' + uuid.uuid4().hex } share_transfer.update(attrs) share_transfer = osc_fakes.FakeResource(info=copy.deepcopy( share_transfer), methods=methods, loaded=True) return share_transfer @staticmethod def create_share_transfers(attrs=None, count=2): """Create multiple fake transfers. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share transfers to be faked :return: A list of FakeResource objects """ share_transfers = [] for n in range(0, count): share_transfers.append( FakeShareSnapshot.create_one_snapshot(attrs)) return share_transfers class FakeSnapshotAccessRule(object): """Fake one or more snapshot access rules""" @staticmethod def create_one_access_rule(attrs={}): """Create a fake snapshot access rule :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with project_id, resource and so on """ snapshot_access_rule = { 'access_to': 'demo', 'access_type': 'user', 'id': 'access_rule-id-' + uuid.uuid4().hex, 'state': 'queued_to_apply' } snapshot_access_rule.update(attrs) snapshot_access_rule = osc_fakes.FakeResource(info=copy.deepcopy( snapshot_access_rule), loaded=True) return snapshot_access_rule @staticmethod def create_access_rules(attrs={}, count=2): """Create multiple fake snapshots. :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 """ access_rules = [] for n in range(0, count): access_rules.append( FakeSnapshotAccessRule.create_one_access_rule(attrs)) return access_rules class FakeSnapshotExportLocation(object): """Fake one or more export locations""" @staticmethod def create_one_export_location(attrs=None): """Create a fake snapshot export location :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with project_id, resource and so on """ attrs = attrs or {} snapshot_export_location_info = { "created_at": 'time-' + uuid.uuid4().hex, "id": "id-" + uuid.uuid4().hex, "is_admin_only": False, "links": [], "path": "/path/to/fake/snapshot/snapshot", "share_snapshot_instance_id": 'instance-id' + uuid.uuid4().hex, "updated_at": 'time-' + uuid.uuid4().hex, } snapshot_export_location_info.update(attrs) snapshot_export_location = osc_fakes.FakeResource(info=copy.deepcopy( snapshot_export_location_info), loaded=True) return snapshot_export_location @staticmethod def create_export_locations(attrs={}, count=2): """Create multiple fake export locations. :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 """ export_locations = [] for n in range(0, count): export_locations.append( FakeSnapshotExportLocation.create_one_export_location( attrs)) return export_locations class FakeMessage(object): """Fake message""" @staticmethod def create_one_message(attrs=None): """Create a fake message :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with project_id, resource and so on """ attrs = attrs or {} message = { 'id': 'message-id-' + uuid.uuid4().hex, 'action_id': '001', 'detail_id': '002', 'user_message': 'user message', 'message_level': 'ERROR', 'resource_type': 'SHARE', 'resource_id': 'resource-id-' + uuid.uuid4().hex, 'created_at': datetime.datetime.now().isoformat(), 'expires_at': ( datetime.datetime.now() + datetime.timedelta(days=30) ).isoformat(), 'request_id': 'req-' + uuid.uuid4().hex, } message.update(attrs) message = osc_fakes.FakeResource(info=copy.deepcopy( message), loaded=True) return message @staticmethod def create_messages(attrs={}, count=2): """Create multiple fake messages. :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 """ messages = [] for n in range(0, count): messages.append( FakeMessage.create_one_message(attrs)) return messages class FakeShareReplica(object): """Fake a share replica""" @staticmethod def create_one_replica(attrs=None, methods=None): """Create a fake share replica :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_replica = { 'availability_zone': None, 'cast_rules_to_readonly': True, 'created_at': datetime.datetime.now().isoformat(), 'host': None, 'id': 'replica-id-' + uuid.uuid4().hex, 'replica_state': None, 'share_id': 'share-id-' + uuid.uuid4().hex, 'share_network_id': None, 'share_server_id': None, 'status': None, 'updated_at': None } share_replica.update(attrs) share_replica = osc_fakes.FakeResource(info=copy.deepcopy( share_replica), methods=methods, loaded=True) return share_replica @staticmethod def create_share_replicas(attrs=None, count=2): """Create multiple fake replicas. :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_replicas = [] for n in range(0, count): share_replicas.append( FakeShareReplica.create_one_replica(attrs)) return share_replicas class FakeShareAvailabilityZones(object): """Fake one or more availability zones""" @staticmethod def create_one_availability_zone(attrs=None): """Create a fake share availability zone :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with project_id, resource and so on """ attrs = attrs or {} availability_zone = { "id": 'id-' + uuid.uuid4().hex, "name": 'name-' + uuid.uuid4().hex, "created_at": 'time-' + uuid.uuid4().hex, "updated_at": 'time-' + uuid.uuid4().hex, } availability_zone.update(attrs) availability_zone = osc_fakes.FakeResource(info=copy.deepcopy( availability_zone), loaded=True) return availability_zone @staticmethod def create_share_availability_zones(attrs=None, count=2): """Create multiple availability zones. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of availability zones to be faked :return: A list of FakeResource objects """ availability_zones = [] for n in range(0, count): availability_zones.append( FakeShareAvailabilityZones.create_one_availability_zone(attrs)) return availability_zones class FakeShareService(object): """Fake one or more share service""" @staticmethod def create_fake_service(attrs=None): """Create a fake share service :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with project_id, resource and so on """ attrs = attrs or {} share_service_info = { "binary": "manila-share", "host": "fake_host@fake_backend", "id": uuid.uuid4().hex, "status": "enabled", "state": "up", "updated_at": 'time-' + uuid.uuid4().hex, "zone": "fake_zone" } share_service_info.update(attrs) share_service = osc_fakes.FakeResource(info=copy.deepcopy( share_service_info), loaded=True) return share_service @staticmethod def create_fake_services(attrs=None, count=2): """Create multiple fake services. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share services to be faked :return: A list of FakeResource objects """ services = [] for n in range(count): services.append( FakeShareService.create_fake_service(attrs)) return services class FakeShareSecurityService(object): """Fake one or more share security service""" @staticmethod def create_fake_security_service(attrs=None, methods=None): """Create a fake share security service :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_security_service_info = { "created_at": datetime.datetime.now().isoformat(), "description": 'description', "dns_ip": '0.0.0.0', "domain": 'fake.domain', "id": uuid.uuid4().hex, "name": 'name-' + uuid.uuid4().hex, "ou": 'fake_OU', "password": 'password', "project_id": uuid.uuid4().hex, "server": 'fake_hostname', "default_ad_site": 'fake_default_ad_site', "status": 'new', "type": 'ldap', "updated_at": datetime.datetime.now().isoformat(), "user": 'fake_user', } share_security_service_info.update(attrs) share_security_service = osc_fakes.FakeResource(info=copy.deepcopy( share_security_service_info), methods=methods, loaded=True) return share_security_service @staticmethod def create_fake_security_services(attrs=None, count=2): """Create multiple fake security services. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share security services to be faked :return: A list of FakeResource objects """ security_services = [] for n in range(count): security_services.append( FakeShareSecurityService.create_fake_security_service(attrs)) return security_services class FakeSharePools(object): """Fake one or more share pool""" @staticmethod def create_one_share_pool(attrs=None): """Create a fake share pool :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object """ attrs = attrs or {} share_pool = { "name": 'fake_pool@gamma#fake_pool', "host": 'fake_host_' + uuid.uuid4().hex, "backend": 'fake_backend_' + uuid.uuid4().hex, "pool": 'fake_pool_' + uuid.uuid4().hex, "capabilities": {'fake_capability': uuid.uuid4().hex} } share_pool.update(attrs) share_pool = osc_fakes.FakeResource(info=copy.deepcopy( share_pool), loaded=True) return share_pool @staticmethod def create_share_pools(attrs=None, count=2): """Create multiple fake share pools. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share pools to be faked :return: A list of FakeResource objects """ share_pools = [] for n in range(count): share_pools.append( FakeSharePools.create_one_share_pool(attrs)) return share_pools class FakeShareInstance(object): """Fake a share instance""" @staticmethod def create_one_share_instance(attrs=None, methods=None): """Create a fake share instance :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_instance = { 'status': None, 'progress': None, 'share_id': 'share-id-' + uuid.uuid4().hex, 'availability_zone': None, 'replica_state': None, 'created_at': datetime.datetime.now().isoformat(), 'cast_rules_to_readonly': False, 'share_network_id': 'sn-id-' + uuid.uuid4().hex, 'share_server_id': 'ss-id-' + uuid.uuid4().hex, 'host': None, 'access_rules_status': None, 'id': 'instance-id-' + uuid.uuid4().hex } share_instance.update(attrs) share_instance = osc_fakes.FakeResource(info=copy.deepcopy( share_instance), methods=methods, loaded=True) return share_instance @staticmethod def create_share_instances(attrs=None, count=2): """Create multiple fake instances. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share instances to be faked :return: A list of FakeResource objects """ share_instances = [] for n in range(count): share_instances.append( FakeShareInstance.create_one_share_instance(attrs)) return share_instances class FakeShareLimits(object): """Fake one or more share limits""" @staticmethod def create_one_share_limit(attrs=None): """Create a fake share limit dict :param Dictionary attrs: A dictionary with all attributes :return: A FakeLimitsResource object, with share limits. """ attrs = attrs or {} share_limits = { 'absolute_limit': { "totalShareNetworksUsed": 4, }, 'rate_limit': { "regex": "^/shares", "uri": "/shares", "verb": "GET", "next-available": "2021-09-01T00:00:00Z", "unit": "MINUTE", "value": "3", "remaining": "1", } } share_limits.update(attrs) share_limits = osc_fakes.FakeLimitsResource( info=copy.deepcopy(share_limits), loaded=True) return share_limits class FakeShareNetwork(object): """Fake a share network""" @staticmethod def create_one_share_network(attrs=None, methods=None): """Create a fake share network :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_network = { 'id': str(uuid.uuid4()), 'project_id': uuid.uuid4().hex, 'created_at': datetime.datetime.now().isoformat(), 'description': 'description-' + uuid.uuid4().hex, 'name': 'name-' + uuid.uuid4().hex, "status": "active", "security_service_update_support": True, 'share_network_subnets': [ { 'id': str(uuid.uuid4()), "availability_zone": None, "created_at": datetime.datetime.now().isoformat(), "updated_at": datetime.datetime.now().isoformat(), "segmentation_id": 1010, "neutron_net_id": str(uuid.uuid4()), "neutron_subnet_id": str(uuid.uuid4()), "ip_version": 4, "cidr": "10.0.0.0/24", "network_type": "vlan", "mtu": "1500", "gateway": "10.0.0.1" }, ], } share_network.update(attrs) share_network = osc_fakes.FakeResource(info=copy.deepcopy( share_network), methods=methods, loaded=True) return share_network @staticmethod def create_share_networks(attrs=None, count=2): """Create multiple fake share networks. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share networks to be faked :return: A list of FakeResource objects """ share_networks = [] for n in range(count): share_networks.append( FakeShareNetwork.create_one_share_network(attrs)) return share_networks class FakeShareNetworkSubnet(object): """Fake a share network subnet""" @staticmethod def create_one_share_subnet(attrs=None): """Create a fake share network subnet :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with project_id, resource and so on """ attrs = attrs or {} share_network_subnet = { "availability_zone": None, "cidr": "10.0.0.0/24", "created_at": datetime.datetime.now().isoformat(), "gateway": "10.0.0.1", 'id': str(uuid.uuid4()), "ip_version": 4, "mtu": "1500", "network_type": "vlan", "neutron_net_id": str(uuid.uuid4()), "neutron_subnet_id": str(uuid.uuid4()), "segmentation_id": 1010, "share_network_id": str(uuid.uuid4()), "share_network_name": str(uuid.uuid4()), "updated_at": datetime.datetime.now().isoformat(), "properties": {}, } share_network_subnet.update(attrs) share_network_subnet = osc_fakes.FakeResource(info=copy.deepcopy( share_network_subnet), loaded=True) return share_network_subnet @staticmethod def create_share_network_subnets(attrs=None, count=2): """Create multiple fake share network subnets. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share network subnets to be faked :return: A list of FakeResource objects """ share_network_subnets = [] for n in range(count): share_network_subnets.append( FakeShareNetworkSubnet.create_one_share_subnet(attrs)) return share_network_subnets class FakeShareGroup(object): """Fake a share group""" @staticmethod def create_one_share_group(attrs=None, methods=None): """Create a fake share group :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_group = { "id": 'share-group-id-' + uuid.uuid4().hex, 'name': None, 'created_at': datetime.datetime.now().isoformat(), 'status': 'available', 'description': None, 'availability_zone': None, "project_id": 'project-id-' + uuid.uuid4().hex, 'host': None, 'share_group_type_id': 'share-group-type-id-' + uuid.uuid4().hex, 'source_share_group_snapshot_id': None, 'share_network_id': None, 'share_server_id': None, 'share_types': ['share-types-id-' + uuid.uuid4().hex], 'consistent_snapshot_support': None } share_group.update(attrs) share_group = osc_fakes.FakeResource(info=copy.deepcopy( share_group), methods=methods, loaded=True) return share_group @staticmethod def create_share_groups(attrs=None, count=2): """Create multiple fake groups. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share groups to be faked :return: A list of FakeResource objects """ share_groups = [] for n in range(0, count): share_groups.append( FakeShareGroup.create_one_share_group(attrs)) return share_groups class FakeShareGroupType(object): """Fake one or more share group types""" @staticmethod def create_one_share_group_type(attrs=None, methods=None): """Create a fake share group 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_group_type_info = { "is_public": True, "group_specs": { "snapshot_support": True }, "share_types": ['share-types-id-' + uuid.uuid4().hex], "id": 'share-group-type-id-' + uuid.uuid4().hex, "name": 'share-group-type-name-' + uuid.uuid4().hex, "is_default": False } share_group_type_info.update(attrs) share_group_type = osc_fakes.FakeResource(info=copy.deepcopy( share_group_type_info), methods=methods, loaded=True) return share_group_type @staticmethod def create_share_group_types(attrs=None, count=2): """Create multiple fake share group types. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share group types to be faked :return: A list of FakeResource objects """ share_group_types = [] for n in range(0, count): share_group_types.append( FakeShareGroupType.create_one_share_group_type(attrs)) return share_group_types @staticmethod def get_share_group_types(share_group_types=None, count=2): """Get an iterable MagicMock object with a list of faked group 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 group types to be faked :return An iterable Mock object with side_effect set to a list of faked group types """ if share_group_types is None: share_group_types = FakeShareGroupType.share_group_types(count) return mock.Mock(side_effect=share_group_types) class FakeShareGroupSnapshot(object): """Fake a share group snapshot""" @staticmethod def create_one_share_group_snapshot(attrs=None, methods=None): """Create a fake share group snapshot :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_group_snapshot = { 'status': 'available', 'share_group_id': 'share-group-id-' + uuid.uuid4().hex, 'name': None, 'created_at': datetime.datetime.now().isoformat(), "project_id": 'project-id-' + uuid.uuid4().hex, 'id': 'share-group-snapshot-id-' + uuid.uuid4().hex, 'description': None } share_group_snapshot.update(attrs) share_group_snapshot = osc_fakes.FakeResource(info=copy.deepcopy( share_group_snapshot), methods=methods, loaded=True) return share_group_snapshot @staticmethod def create_share_group_snapshots(attrs=None, count=2): """Create multiple fake share group snapshot. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share group snapshot to be faked :return: A list of FakeResource objects """ share_group_snapshots = [] for n in range(0, count): share_group_snapshots.append( FakeShareGroupSnapshot.create_one_share_group_snapshot(attrs)) return share_group_snapshots class FakeShareServer(object): """Fake a share server""" @staticmethod def create_one_server(attrs=None, methods=None): """Create a fake share server :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_server = { 'id': str(uuid.uuid4()), 'project_id': uuid.uuid4().hex, "updated_at": datetime.datetime.now().isoformat(), 'status': None, 'host': None, 'check_only': False, 'share_network_name': None, 'share_network_id': str(uuid.uuid4()), 'share_network_subnet_id': str(uuid.uuid4()), 'created_at': datetime.datetime.now().isoformat(), 'is_auto_deletable': False, 'identifier': str(uuid.uuid4()) } share_server.update(attrs) share_server = osc_fakes.FakeResource(info=copy.deepcopy( share_server), methods=methods, loaded=True) return share_server @staticmethod def create_share_servers(attrs=None, count=2): """Create multiple fake servers. :param dict attrs: A dictionary with all attributes :param int count: The number of share server to be faked :return: A list of FakeResource objects """ attrs = attrs or {} share_servers = [] for n in range(count): share_servers.append( FakeShareServer.create_one_server(attrs)) return share_servers class FakeResourceLock(object): """Fake a resource lock""" @staticmethod def create_one_lock(attrs=None, methods=None): """Create a fake resource lock :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with id, resource and so on """ attrs = attrs or {} methods = methods or {} now_time = datetime.datetime.now() delta_time = now_time + datetime.timedelta(minutes=5) lock = { 'id': str(uuid.uuid4()), 'resource_id': str(uuid.uuid4()), 'resource_type': 'share', 'resource_action': 'delete', 'created_at': now_time.isoformat(), 'updated_at': delta_time.isoformat(), 'project_id': uuid.uuid4().hex, 'user_id': uuid.uuid4().hex, 'lock_context': 'user', 'lock_reason': 'created by func tests', } lock.update(attrs) lock = osc_fakes.FakeResource(info=copy.deepcopy( lock), methods=methods, loaded=True) return lock @staticmethod def create_locks(attrs=None, count=2): """Create multiple fake locks. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share transfers to be faked :return: A list of FakeResource objects """ resource_locks = [] for n in range(0, count): resource_locks.append( FakeResourceLock.create_one_lock(attrs)) return resource_locks class FakeShareBackup(object): """Fake a share Backup""" @staticmethod def create_one_backup(attrs=None, methods=None): """Create a fake share backup :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_backup = { 'id': 'backup-id-' + uuid.uuid4().hex, 'share_id': 'share-id-' + uuid.uuid4().hex, 'status': None, 'name': None, 'description': None, 'size': '0', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': datetime.datetime.now().isoformat(), 'availability_zone': None, 'progress': None, 'restore_progress': None, 'host': None, 'topic': None, } share_backup.update(attrs) share_backup = osc_fakes.FakeResource(info=copy.deepcopy( share_backup), methods=methods, loaded=True) return share_backup @staticmethod def create_share_backups(attrs=None, count=2): """Create multiple fake backups. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of share backups to be faked :return: A list of FakeResource objects """ share_backups = [] for n in range(0, count): share_backups.append( FakeShareBackup.create_one_backup(attrs)) return share_backups ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_availability_zones.py0000664000175000017500000000365300000000000030731 0ustar00zuulzuul00000000000000# 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.osc.v2 import availability_zones as osc_availability_zones from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestAvailabilityZones(manila_fakes.TestShare): def setUp(self): super(TestAvailabilityZones, self).setUp() self.zones_mock = self.app.client_manager.share.availability_zones self.zones_mock.reset_mock() class TestShareAvailabilityZoneList(TestAvailabilityZones): availability_zones = manila_fakes.FakeShareAvailabilityZones.\ create_share_availability_zones() COLUMNS = ("Id", "Name", "Created At", "Updated At") def setUp(self): super(TestShareAvailabilityZoneList, self).setUp() self.zones_mock.list.return_value = self.availability_zones # Get the command object to test self.cmd = osc_availability_zones.ShareAvailabilityZoneList( self.app, None) self.values = (oscutils.get_dict_properties( s._info, self.COLUMNS) for s in self.availability_zones) def test_share_list_availability_zone(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.assertEqual(self.COLUMNS, columns) self.assertCountEqual(list(self.values), list(data)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_messages.py0000664000175000017500000001406200000000000026644 0ustar00zuulzuul00000000000000# 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 osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.osc.v2 import messages as osc_messages from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes COLUMNS = [ 'ID', 'Resource Type', 'Resource ID', 'Action ID', 'User Message', 'Detail ID', 'Created At', ] class TestMessage(manila_fakes.TestShare): def setUp(self): super(TestMessage, self).setUp() self.messages_mock = self.app.client_manager.share.messages self.messages_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION) class TestMessageDelete(TestMessage): def setUp(self): super(TestMessageDelete, self).setUp() self.message = ( manila_fakes.FakeMessage.create_one_message()) self.messages_mock.get.return_value = self.message self.cmd = osc_messages.DeleteMessage(self.app, None) def test_message_delete_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_message_delete(self): arglist = [ self.message.id ] verifylist = [ ('message', [self.message.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.messages_mock.delete.assert_called_with(self.message) self.assertIsNone(result) def test_message_delete_multiple(self): messages = ( manila_fakes.FakeMessage.create_messages( count=2)) arglist = [ messages[0].id, messages[1].id ] verifylist = [ ('message', [messages[0].id, messages[1].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertEqual(self.messages_mock.delete.call_count, len(messages)) self.assertIsNone(result) def test_message_delete_exception(self): arglist = [ self.message.id ] verifylist = [ ('message', [self.message.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.messages_mock.delete.side_effect = exceptions.CommandError() self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) class TestMessageShow(TestMessage): def setUp(self): super(TestMessageShow, self).setUp() self.message = ( manila_fakes.FakeMessage.create_one_message()) self.messages_mock.get.return_value = self.message self.cmd = osc_messages.ShowMessage(self.app, None) self.data = self.message._info.values() self.columns = self.message._info.keys() def test_message_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_message_show(self): arglist = [ self.message.id ] verifylist = [ ('message', self.message.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.messages_mock.get.assert_called_with(self.message.id) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestMessageList(TestMessage): def setUp(self): super(TestMessageList, self).setUp() self.messages = ( manila_fakes.FakeMessage.create_messages( count=2)) self.messages_mock.list.return_value = self.messages self.values = (oscutils.get_dict_properties( m._info, COLUMNS) for m in self.messages) self.cmd = osc_messages.ListMessage(self.app, None) def test_list_messages(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.messages_mock.list.assert_called_with( search_opts={ 'limit': None, 'request_id': None, 'resource_type': None, 'resource_id': None, 'action_id': None, 'detail_id': None, 'message_level': None, 'created_since': None, 'created_before': None }) self.assertEqual(COLUMNS, columns) self.assertEqual(list(self.values), list(data)) def test_list_messages_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.50") arglist = [ '--before', '2021-02-06T09:49:58-05:00', '--since', '2021-02-05T09:49:58-05:00' ] verifylist = [ ('before', '2021-02-06T09:49:58-05:00'), ('since', '2021-02-05T09:49:58-05:00') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_quotas.py0000664000175000017500000003762600000000000026364 0ustar00zuulzuul00000000000000# 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 unittest import mock from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from osc_lib import exceptions from manilaclient import api_versions from manilaclient.common.apiclient.exceptions import BadRequest from manilaclient.osc.v2 import quotas as osc_quotas from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestQuotas(manila_fakes.TestShare): def setUp(self): super(TestQuotas, self).setUp() self.quotas_mock = self.app.client_manager.share.quotas self.quotas_mock.reset_mock() self.quota_classes_mock = self.app.client_manager.share.quota_classes self.quota_classes_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION ) class TestQuotaSet(TestQuotas): project = identity_fakes.FakeProject.create_one_project() user = identity_fakes.FakeUser.create_one_user() def setUp(self): super(TestQuotaSet, self).setUp() self.quotas = manila_fakes.FakeQuotaSet.create_fake_quotas() self.quotas_mock.update = mock.Mock() self.quotas_mock.update.return_value = None self.quota_classes_mock.update = mock.Mock() self.quota_classes_mock.update.return_value = None self.cmd = osc_quotas.QuotaSet(self.app, None) def test_quota_set_default_class_shares(self): arglist = [ 'default', '--class', '--shares', '40' ] verifylist = [ ('project', 'default'), ('quota_class', True), ('shares', 40) ] with mock.patch('osc_lib.utils.find_resource') as mock_find_resource: mock_find_resource.return_value = self.project parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.quota_classes_mock.update.assert_called_with( class_name='default', gigabytes=None, share_networks=None, shares=40, snapshot_gigabytes=None, snapshots=None) self.assertIsNone(result) mock_find_resource.assert_not_called() self.quotas_mock.assert_not_called() def test_quota_set_shares(self): arglist = [ self.project.id, '--shares', '40' ] verifylist = [ ('project', self.project.id), ('shares', 40) ] with mock.patch('osc_lib.utils.find_resource') as mock_find_resource: mock_find_resource.return_value = self.project parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.quotas_mock.update.assert_called_with( force=None, gigabytes=None, share_networks=None, shares=40, snapshot_gigabytes=None, snapshots=None, tenant_id=self.project.id, user_id=None) self.assertIsNone(result) def test_quota_set_gigabytes(self): arglist = [ self.project.id, '--gigabytes', '1100' ] verifylist = [ ('project', self.project.id), ('gigabytes', 1100) ] with mock.patch('osc_lib.utils.find_resource') as mock_find_resource: mock_find_resource.return_value = self.project parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.quotas_mock.update.assert_called_with( force=None, gigabytes=1100, share_networks=None, shares=None, snapshot_gigabytes=None, snapshots=None, tenant_id=self.project.id, user_id=None) self.assertIsNone(result) def test_quota_set_share_type(self): arglist = [ self.project.id, '--share-type', 'default' ] verifylist = [ ('project', self.project.id), ('share_type', 'default') ] with mock.patch('osc_lib.utils.find_resource') as mock_find_resource: mock_find_resource.return_value = self.project parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.quotas_mock.update.assert_called_with( force=None, gigabytes=None, share_networks=None, share_type='default', shares=None, snapshot_gigabytes=None, snapshots=None, tenant_id=self.project.id, user_id=None) self.assertIsNone(result) def test_quota_set_force(self): arglist = [ self.project.id, '--force', '--shares', '40' ] verifylist = [ ('project', self.project.id), ('force', True), ('shares', 40) ] with mock.patch('osc_lib.utils.find_resource') as mock_find_resource: mock_find_resource.return_value = self.project parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.quotas_mock.update.assert_called_with( force=True, gigabytes=None, share_networks=None, shares=40, snapshot_gigabytes=None, snapshots=None, tenant_id=self.project.id, user_id=None) self.assertIsNone(result) def test_quota_set_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.39' ) arglist = [ self.project.id, '--share-groups', '40' ] verifylist = [ ('project', self.project.id), ('share_groups', 40) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_quota_set_update_project_exception(self): arglist = [ self.project.id, '--share-groups', '40', '--share-group-snapshots', '40' ] verifylist = [ ('project', self.project.id), ('share_groups', 40), ('share_group_snapshots', 40) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.quotas_mock.update.side_effect = BadRequest() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_quota_set_update_class_exception(self): arglist = [ 'default', '--class', '--gigabytes', '40' ] verifylist = [ ('project', 'default'), ('gigabytes', 40) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.quota_classes_mock.update.side_effect = BadRequest() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_quota_set_nothing_to_set_exception(self): arglist = [ self.project.id, ] verifylist = [ ('project', self.project.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_quota_set_share_replicas(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.53' ) arglist = [ self.project.id, '--share-replicas', '2', ] verifylist = [ ('project', self.project.id), ('share_replicas', 2) ] with mock.patch('osc_lib.utils.find_resource') as mock_find_resource: mock_find_resource.return_value = self.project parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.quotas_mock.update.assert_called_with( force=None, gigabytes=None, share_networks=None, share_replicas=2, shares=None, snapshot_gigabytes=None, snapshots=None, tenant_id=self.project.id, user_id=None) self.assertIsNone(result) def test_quota_set_replica_gigabytes_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.51') arglist = [ self.project.id, '--replica-gigabytes', '10', ] verifylist = [ ('project', self.project.id), ('replica_gigabytes', 10) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_quota_set_per_share_gigabytes_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.61') arglist = [ self.project.id, '--per-share-gigabytes', '10', ] verifylist = [ ('project', self.project.id), ('per_share_gigabytes', 10) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_quota_set_per_share_gigabytes(self): arglist = [ self.project.id, '--per-share-gigabytes', '10', ] verifylist = [ ('project', self.project.id), ('per_share_gigabytes', 10) ] with mock.patch('osc_lib.utils.find_resource') as mock_find_resource: mock_find_resource.return_value = self.project parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.quotas_mock.update.assert_called_with( force=None, gigabytes=None, share_networks=None, shares=None, snapshot_gigabytes=None, snapshots=None, per_share_gigabytes=10, tenant_id=self.project.id, user_id=None) self.assertIsNone(result) class TestQuotaShow(TestQuotas): project = identity_fakes.FakeProject.create_one_project() user = identity_fakes.FakeUser.create_one_user() def setUp(self): super(TestQuotaShow, self).setUp() self.quotas = manila_fakes.FakeQuotaSet.create_fake_quotas() self.quotas_mock.get.return_value = self.quotas self.cmd = osc_quotas.QuotaShow(self.app, None) def test_quota_show(self): arglist = [ self.project.id ] verifylist = [ ('project', self.project.id) ] with mock.patch('osc_lib.utils.find_resource') as mock_find_resource: mock_find_resource.return_value = self.project parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.quotas_mock.get.assert_called_with( detail=False, tenant_id=self.project.id, user_id=None ) self.assertCountEqual(columns, self.quotas.keys()) self.assertCountEqual(data, self.quotas._info.values()) def test_quota_show_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.38' ) arglist = [ self.project.id, '--share-type', 'default' ] verifylist = [ ('project', self.project.id), ('share_type', 'default') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_quota_show_defaults(self): arglist = [ self.project.id, '--defaults' ] verifylist = [ ('project', self.project.id), ('defaults', True) ] self.quotas_mock.defaults = mock.Mock() self.quotas_mock.defaults.return_value = self.quotas with mock.patch('osc_lib.utils.find_resource') as mock_find_resource: mock_find_resource.return_value = self.project parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.quotas_mock.defaults.assert_called_with(self.project.id) self.assertCountEqual(columns, self.quotas.keys()) self.assertCountEqual(data, self.quotas._info.values()) class TestQuotaDelete(TestQuotas): project = identity_fakes.FakeProject.create_one_project() user = identity_fakes.FakeUser.create_one_user() def setUp(self): super(TestQuotaDelete, self).setUp() self.quotas = manila_fakes.FakeQuotaSet.create_fake_quotas() self.quotas_mock.delete.return_value = None self.cmd = osc_quotas.QuotaDelete(self.app, None) def test_quota_delete(self): arglist = [ self.project.id ] verifylist = [ ('project', self.project.id) ] with mock.patch('osc_lib.utils.find_resource') as mock_find_resource: mock_find_resource.return_value = self.project parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.quotas_mock.delete.assert_called_with( tenant_id=self.project.id, user_id=None) self.assertIsNone(result) def test_quota_delete_share_type(self): arglist = [ self.project.id, '--share-type', 'default' ] verifylist = [ ('project', self.project.id), ('share_type', 'default') ] with mock.patch('osc_lib.utils.find_resource') as mock_find_resource: mock_find_resource.return_value = self.project parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.quotas_mock.delete.assert_called_with( share_type='default', tenant_id=self.project.id, user_id=None) self.assertIsNone(result) def test_quota_delete_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.38' ) arglist = [ self.project.id, '--share-type', 'default' ] verifylist = [ ('project', self.project.id), ('share_type', 'default') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_resource_locks.py0000664000175000017500000002377400000000000030071 0ustar00zuulzuul00000000000000# 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 unittest import mock from osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.osc.v2 import resource_locks as osc_resource_locks from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes DETAIL_COLUMNS = [ 'ID', 'Resource Id', 'Resource Type', 'Resource Action', 'Created At', 'Updated At', 'User Id', 'Project Id', 'Lock Reason', 'Lock Context', ] SUMMARY_COLUMNS = [ 'ID', 'Resource Id', 'Resource Type', 'Resource Action', ] class TestResourceLock(manila_fakes.TestShare): def setUp(self): super(TestResourceLock, self).setUp() self.shares_mock = self.app.client_manager.share.shares self.shares_mock.reset_mock() self.locks_mock = self.app.client_manager.share.resource_locks self.locks_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION) class TestResourceLockCreate(TestResourceLock): def setUp(self): super(TestResourceLockCreate, self).setUp() self.share = manila_fakes.FakeShare.create_one_share() self.shares_mock.create.return_value = self.share self.shares_mock.get.return_value = self.share self.lock = manila_fakes.FakeResourceLock.create_one_lock( attrs={'resource_id': self.share.id}) self.locks_mock.get.return_value = self.lock self.locks_mock.create.return_value = self.lock self.cmd = osc_resource_locks.CreateResourceLock(self.app, None) self.data = tuple(self.lock._info.values()) self.columns = tuple(self.lock._info.keys()) def test_share_lock_create_missing_required_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_lock_create(self): arglist = [ '--resource-action', 'revert_to_snapshot', '--lock-reason', "you cannot go back in time", self.share.id, 'share', ] verifylist = [ ('resource', self.share.id), ('resource_type', 'share'), ('resource_action', 'revert_to_snapshot'), ('lock_reason', 'you cannot go back in time') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.locks_mock.create.assert_called_with( self.share.id, 'share', 'revert_to_snapshot', 'you cannot go back in time', ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestResourceLockDelete(TestResourceLock): def setUp(self): super(TestResourceLockDelete, self).setUp() self.lock = manila_fakes.FakeResourceLock.create_one_lock() self.locks_mock.get.return_value = self.lock self.lock.delete = mock.Mock() self.cmd = osc_resource_locks.DeleteResourceLock(self.app, None) def test_share_lock_delete_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_lock_delete(self): arglist = [ self.lock.id ] verifylist = [ ('lock', [self.lock.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.lock.delete.assert_called_once_with() self.assertIsNone(result) def test_share_lock_delete_multiple(self): locks = manila_fakes.FakeResourceLock.create_locks(count=2) arglist = [ locks[0].id, locks[1].id ] verifylist = [ ('lock', [locks[0].id, locks[1].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertEqual(self.lock.delete.call_count, len(locks)) self.assertIsNone(result) def test_share_lock_delete_exception(self): arglist = [ self.lock.id ] verifylist = [ ('lock', [self.lock.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.lock.delete.side_effect = exceptions.CommandError() self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) class TestResourceLockShow(TestResourceLock): def setUp(self): super(TestResourceLockShow, self).setUp() self.lock = manila_fakes.FakeResourceLock.create_one_lock() self.locks_mock.get.return_value = self.lock self.cmd = osc_resource_locks.ShowResourceLock(self.app, None) self.data = self.lock._info.values() self.columns = list(self.lock._info.keys()) def test_share_lock_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_lock_show(self): arglist = [ self.lock.id, ] verifylist = [ ('lock', self.lock.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.locks_mock.get.assert_called_with(self.lock.id) self.assertEqual(len(self.columns), len(columns)) self.assertCountEqual(sorted(self.data), sorted(data)) class TestResourceLockList(TestResourceLock): def setUp(self): super(TestResourceLockList, self).setUp() self.locks = manila_fakes.FakeResourceLock.create_locks(count=2) self.locks_mock.list.return_value = self.locks self.values = (oscutils.get_dict_properties( m._info, DETAIL_COLUMNS) for m in self.locks) self.cmd = osc_resource_locks.ListResourceLock(self.app, None) def test_share_lock_list(self): arglist = [ '--detailed' ] verifylist = [ ('detailed', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.locks_mock.list.assert_called_with( search_opts={ 'all_projects': False, 'project_id': None, 'user_id': None, 'id': None, 'resource_id': None, 'resource_type': None, 'resource_action': None, 'lock_context': None, 'created_before': None, 'created_since': None, 'limit': None, 'offset': None, }, sort_key=None, sort_dir=None ) self.assertEqual(sorted(DETAIL_COLUMNS), sorted(columns)) actual_data = [sorted(d) for d in data] expected_data = [sorted(v) for v in self.values] self.assertEqual(actual_data, expected_data) class TestResourceLockSet(TestResourceLock): def setUp(self): super(TestResourceLockSet, self).setUp() self.lock = manila_fakes.FakeResourceLock.create_one_lock() self.lock.update = mock.Mock() self.locks_mock.get.return_value = self.lock self.cmd = osc_resource_locks.SetResourceLock(self.app, None) def test_share_lock_set_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_lock_set(self): arglist = [ self.lock.id, '--resource-action', 'unmanage', ] verifylist = [ ('lock', self.lock.id), ('resource_action', 'unmanage') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertIsNone(result) self.locks_mock.update.assert_called_with(self.lock.id, resource_action='unmanage') class TestResourceLockUnSet(TestResourceLock): def setUp(self): super(TestResourceLockUnSet, self).setUp() self.lock = manila_fakes.FakeResourceLock.create_one_lock() self.lock.update = mock.Mock() self.locks_mock.get.return_value = self.lock self.cmd = osc_resource_locks.UnsetResourceLock(self.app, None) def test_share_lock_unset_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_lock_unset(self): arglist = [ self.lock.id, '--lock-reason' ] verifylist = [ ('lock', self.lock.id), ('lock_reason', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertIsNone(result) self.locks_mock.update.assert_called_with(self.lock.id, lock_reason=None) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_security_services.py0000664000175000017500000005354700000000000030622 0ustar00zuulzuul00000000000000# 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 osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.osc.v2 import security_services as osc_security_services from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShareSecurityService(manila_fakes.TestShare): def setUp(self): super(TestShareSecurityService, self).setUp() self.security_services_mock = ( self.app.client_manager.share.security_services) self.security_services_mock.reset_mock() self.share_networks_mock = self.app.client_manager.share.share_networks self.share_networks_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION ) @ddt.ddt class TestShareSecurityServiceCreate(TestShareSecurityService): def setUp(self): super(TestShareSecurityServiceCreate, self).setUp() self.security_service = manila_fakes.FakeShareSecurityService \ .create_fake_security_service() self.security_services_mock.create.return_value = self.security_service self.cmd = osc_security_services.CreateShareSecurityService( self.app, None) self.data = self.security_service._info.values() self.columns = self.security_service._info.keys() def test_share_security_service_create_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_security_service_create(self): arglist = [ self.security_service.type, '--dns-ip', self.security_service.dns_ip, '--ou', self.security_service.ou, '--server', self.security_service.server, '--domain', self.security_service.domain, '--user', self.security_service.user, '--password', self.security_service.password, '--name', self.security_service.name, '--description', self.security_service.description, '--default-ad-site', self.security_service.default_ad_site ] verifylist = [ ('type', self.security_service.type), ('dns_ip', self.security_service.dns_ip), ('ou', self.security_service.ou), ('server', self.security_service.server), ('domain', self.security_service.domain), ('user', self.security_service.user), ('password', self.security_service.password), ('name', self.security_service.name), ('description', self.security_service.description), ('default_ad_site', self.security_service.default_ad_site) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.security_services_mock.create.assert_called_with( self.security_service.type, dns_ip=self.security_service.dns_ip, server=self.security_service.server, domain=self.security_service.domain, user=self.security_service.user, password=self.security_service.password, name=self.security_service.name, description=self.security_service.description, ou=self.security_service.ou, default_ad_site=self.security_service.default_ad_site ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) @ddt.data('2.43', '2.75') def test_share_security_service_create_api_version_exception(self, version): self.app.client_manager.share.api_version = api_versions.APIVersion( version ) arglist = [ self.security_service.type, ] verifylist = [ ('type', self.security_service.type), ] if api_versions.APIVersion(version) <= api_versions.APIVersion("2.43"): arglist.extend(['--ou', self.security_service.ou]) verifylist.append(('ou', self.security_service.ou)) if api_versions.APIVersion(version) <= api_versions.APIVersion("2.75"): arglist.extend(['--default-ad-site', self.security_service.default_ad_site]) verifylist.append(('default_ad_site', self.security_service.default_ad_site)) parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareSecurityServiceDelete(TestShareSecurityService): def setUp(self): super(TestShareSecurityServiceDelete, self).setUp() self.security_service = manila_fakes.FakeShareSecurityService \ .create_fake_security_service() self.security_services_mock.get.return_value = self.security_service self.security_services = manila_fakes.FakeShareSecurityService \ .create_fake_security_services() self.cmd = osc_security_services.DeleteShareSecurityService( self.app, None) def test_share_security_service_delete_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_security_service_delete(self): arglist = [ self.security_services[0].id, self.security_services[1].id, ] verifylist = [ ('security_service', [self.security_services[0].id, self.security_services[1].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertEqual(self.security_services_mock.delete.call_count, len(self.security_services)) self.assertIsNone(result) def test_share_security_service_delete_exception(self): arglist = [ self.security_services[0].id, ] verifylist = [ ('security_service', [self.security_services[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.security_services_mock.delete.side_effect = \ exceptions.CommandError() self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareSecurityServiceShow(TestShareSecurityService): def setUp(self): super(TestShareSecurityServiceShow, self).setUp() self.security_service = manila_fakes.FakeShareSecurityService \ .create_fake_security_service() self.security_services_mock.get.return_value = self.security_service self.cmd = osc_security_services.ShowShareSecurityService( self.app, None) self.data = self.security_service._info.values() self.columns = self.security_service._info.keys() def test_share_security_service_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_security_service_show(self): arglist = [ self.security_service.id ] verifylist = [ ('security_service', self.security_service.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.security_services_mock.get.assert_called_with( self.security_service.id ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) @ddt.ddt class TestShareSecurityServiceSet(TestShareSecurityService): def setUp(self): super(TestShareSecurityServiceSet, self).setUp() self.security_service = manila_fakes.FakeShareSecurityService \ .create_fake_security_service(methods={'update': None}) self.security_services_mock.get.return_value = self.security_service self.cmd = osc_security_services.SetShareSecurityService( self.app, None) def test_share_security_service_set_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_security_service_set(self): arglist = [ self.security_service.id, '--dns-ip', self.security_service.dns_ip, '--ou', self.security_service.ou, '--server', self.security_service.server, '--domain', self.security_service.domain, '--user', self.security_service.user, '--password', self.security_service.password, '--name', self.security_service.name, '--description', self.security_service.description, '--default-ad-site', self.security_service.default_ad_site ] verifylist = [ ('security_service', self.security_service.id), ('dns_ip', self.security_service.dns_ip), ('ou', self.security_service.ou), ('server', self.security_service.server), ('domain', self.security_service.domain), ('user', self.security_service.user), ('password', self.security_service.password), ('name', self.security_service.name), ('description', self.security_service.description), ('default_ad_site', self.security_service.default_ad_site) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.security_service.update.assert_called_with( dns_ip=self.security_service.dns_ip, server=self.security_service.server, domain=self.security_service.domain, user=self.security_service.user, password=self.security_service.password, name=self.security_service.name, description=self.security_service.description, ou=self.security_service.ou, default_ad_site=self.security_service.default_ad_site, ) self.assertIsNone(result) def test_share_security_service_set_exception(self): arglist = [ self.security_service.id, '--name', self.security_service.name, ] verifylist = [ ('security_service', self.security_service.id), ('name', self.security_service.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.security_service.update.side_effect = \ exceptions.CommandError() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) @ddt.data('2.43', '2.75') def test_share_security_service_set_api_version_exception(self, version): self.app.client_manager.share.api_version = api_versions.APIVersion( version ) arglist = [ self.security_service.id, ] verifylist = [ ('security_service', self.security_service.id), ] if api_versions.APIVersion(version) <= api_versions.APIVersion("2.43"): arglist.extend(['--ou', self.security_service.ou]) verifylist.append(('ou', self.security_service.ou)) if api_versions.APIVersion(version) <= api_versions.APIVersion("2.75"): arglist.extend(['--default-ad-site', self.security_service.default_ad_site]) verifylist.append(('default_ad_site', self.security_service.default_ad_site)) parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) @ddt.ddt class TestShareSecurityServiceUnset(TestShareSecurityService): def setUp(self): super(TestShareSecurityServiceUnset, self).setUp() self.security_service = manila_fakes.FakeShareSecurityService \ .create_fake_security_service(methods={'update': None}) self.security_services_mock.get.return_value = self.security_service self.cmd = osc_security_services.UnsetShareSecurityService( self.app, None) def test_share_security_service_unset_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_security_service_unset(self): arglist = [ self.security_service.id, '--dns-ip', '--ou', '--server', '--domain', '--user', '--password', '--name', '--description', '--default-ad-site', ] verifylist = [ ('security_service', self.security_service.id), ('dns_ip', True), ('ou', True), ('server', True), ('domain', True), ('user', True), ('password', True), ('name', True), ('description', True), ('default_ad_site', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.security_service.update.assert_called_with( dns_ip='', server='', domain='', user='', password='', name='', description='', ou='', default_ad_site='' ) self.assertIsNone(result) def test_share_security_service_unset_exception(self): arglist = [ self.security_service.id, '--name', ] verifylist = [ ('security_service', self.security_service.id), ('name', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.security_service.update.side_effect = \ exceptions.CommandError() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) @ddt.data('2.43', '2.75') def test_share_security_service_unset_api_version_exception(self, version): self.app.client_manager.share.api_version = api_versions.APIVersion( version ) arglist = [ self.security_service.id, ] verifylist = [ ('security_service', self.security_service.id), ] if api_versions.APIVersion(version) <= api_versions.APIVersion("2.43"): arglist.extend(['--ou']) verifylist.append(('ou', True)) if api_versions.APIVersion(version) <= api_versions.APIVersion("2.75"): arglist.extend(['--default-ad-site']), verifylist.append(('default_ad_site', True)) parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareSecurityServiceList(TestShareSecurityService): columns = [ 'ID', 'Name', 'Status', 'Type', ] def setUp(self): super(TestShareSecurityServiceList, self).setUp() self.share_network = ( manila_fakes.FakeShareNetwork.create_one_share_network()) self.share_networks_mock.get.return_value = self.share_network self.services_list = manila_fakes.FakeShareSecurityService \ .create_fake_security_services() self.security_services_mock.list.return_value = self.services_list self.values = (oscutils.get_dict_properties( i._info, self.columns) for i in self.services_list) self.cmd = osc_security_services.ListShareSecurityService( self.app, None) def test_share_security_service_list_no_args(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.security_services_mock.list.assert_called_with( search_opts={ 'all_tenants': False, 'status': None, 'name': None, 'type': None, 'user': None, 'dns_ip': None, 'server': None, 'domain': None, 'offset': None, 'limit': None, }, detailed=False) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) def test_share_security_service_list(self): arglist = [ '--share-network', self.share_network.id, '--status', self.services_list[0].status, '--name', self.services_list[0].name, '--type', self.services_list[0].type, '--user', self.services_list[0].user, '--dns-ip', self.services_list[0].dns_ip, '--ou', self.services_list[0].ou, '--server', self.services_list[0].server, '--domain', self.services_list[0].domain, '--default-ad-site', self.services_list[0].default_ad_site, '--limit', '1', ] verifylist = [ ('share_network', self.share_network.id), ('status', self.services_list[0].status), ('name', self.services_list[0].name), ('type', self.services_list[0].type), ('user', self.services_list[0].user), ('dns_ip', self.services_list[0].dns_ip), ('ou', self.services_list[0].ou), ('server', self.services_list[0].server), ('domain', self.services_list[0].domain), ('default_ad_site', self.services_list[0].default_ad_site), ('limit', 1), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.security_services_mock.list.assert_called_with( search_opts={ 'all_tenants': False, 'status': self.services_list[0].status, 'name': self.services_list[0].name, 'type': self.services_list[0].type, 'user': self.services_list[0].user, 'dns_ip': self.services_list[0].dns_ip, 'server': self.services_list[0].server, 'domain': self.services_list[0].domain, 'default_ad_site': self.services_list[0].default_ad_site, 'offset': None, 'limit': 1, 'ou': self.services_list[0].ou, 'share_network_id': self.share_network.id, }, detailed=False) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) def test_share_security_service_list_ou_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.43' ) arglist = [ '--ou', self.services_list[0].ou, ] verifylist = [ ('ou', self.services_list[0].ou), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_security_service_list_ad_site_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.75' ) arglist = [ '--default-ad-site', self.services_list[0].default_ad_site, ] verifylist = [ ('default_ad_site', self.services_list[0].default_ad_site), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_security_service_list_detail_all_projects(self): arglist = [ '--all-projects', '--detail' ] verifylist = [ ('all_projects', True), ('detail', True), ] columns_detail = self.columns.copy() columns_detail.append('Project ID') columns_detail.append('Share Networks') values_detail = (oscutils.get_dict_properties( i._info, columns_detail) for i in self.services_list) parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.security_services_mock.list.assert_called_with( search_opts={ 'all_tenants': True, 'status': None, 'name': None, 'type': None, 'user': None, 'dns_ip': None, 'server': None, 'domain': None, 'offset': None, 'limit': None, }, detailed=True) self.assertEqual(columns_detail, columns) self.assertEqual(list(values_detail), list(data)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_services.py0000664000175000017500000002207400000000000026662 0ustar00zuulzuul00000000000000# 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 osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.osc import utils from manilaclient.osc.v2 import services as osc_services from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShareService(manila_fakes.TestShare): def setUp(self): super(TestShareService, self).setUp() self.services_mock = self.app.client_manager.share.services self.services_mock.reset_mock() class TestShareServiceSet(TestShareService): def setUp(self): super(TestShareServiceSet, self).setUp() self.share_service = ( manila_fakes.FakeShareService.create_fake_service() ) self.cmd = osc_services.SetShareService(self.app, None) def test_share_service_set_enable(self): arglist = [ self.share_service.host, self.share_service.binary, '--enable' ] verifylist = [ ('host', self.share_service.host), ('binary', self.share_service.binary), ('enable', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.services_mock.enable.assert_called_with( self.share_service.host, self.share_service.binary) self.assertIsNone(result) def test_share_service_set_enable_exception(self): arglist = [ self.share_service.host, self.share_service.binary, '--enable' ] verifylist = [ ('host', self.share_service.host), ('binary', self.share_service.binary), ('enable', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.services_mock.enable.side_effect = Exception() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_service_set_disable(self): arglist = [ self.share_service.host, self.share_service.binary, '--disable' ] verifylist = [ ('host', self.share_service.host), ('binary', self.share_service.binary), ('disable', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.services_mock.disable.assert_called_with( self.share_service.host, self.share_service.binary) self.assertIsNone(result) def test_service_set_disable_with_reason(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.83") reason = 'earthquake' arglist = [ '--disable', '--disable-reason', reason, self.share_service.host, self.share_service.binary, ] verifylist = [ ('host', self.share_service.host), ('binary', self.share_service.binary), ('disable', True), ('disable_reason', reason), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.services_mock.disable.assert_called_with( self.share_service.host, self.share_service.binary, disable_reason=reason ) self.assertIsNone(result) def test_share_service_set_disable_exception(self): arglist = [ self.share_service.host, self.share_service.binary, '--disable' ] verifylist = [ ('host', self.share_service.host), ('binary', self.share_service.binary), ('disable', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.services_mock.disable.side_effect = Exception() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) @ddt.ddt class TestShareServiceList(TestShareService): columns = [ 'id', 'binary', 'host', 'zone', 'status', 'state', 'updated_at' ] columns_with_reason = columns + ['disabled_reason'] column_headers = utils.format_column_headers(columns) column_headers_with_reason = utils.format_column_headers( columns_with_reason) def setUp(self): super(TestShareServiceList, self).setUp() self.services_list = ( manila_fakes.FakeShareService.create_fake_services( {'disabled_reason': ''}) ) self.services_mock.list.return_value = self.services_list self.values = (oscutils.get_dict_properties( i._info, self.columns) for i in self.services_list) self.values_with_reason = (oscutils.get_dict_properties( i._info, self.columns_with_reason) for i in self.services_list) self.cmd = osc_services.ListShareService(self.app, None) @ddt.data('2.82', '2.83') def test_share_service_list(self, version): self.app.client_manager.share.api_version = api_versions.APIVersion( version) arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.services_mock.list.assert_called_with( search_opts={ 'host': None, 'binary': None, 'status': None, 'state': None, 'zone': None }) if api_versions.APIVersion(version) >= api_versions.APIVersion("2.83"): self.assertEqual(self.column_headers_with_reason, columns) self.assertEqual(list(self.values_with_reason), list(data)) else: self.assertEqual(self.column_headers, columns) self.assertEqual(list(self.values), list(data)) @ddt.data('2.82', '2.83') def test_share_service_list_host_status(self, version): self.app.client_manager.share.api_version = api_versions.APIVersion( version) arglist = [ '--host', self.services_list[0].host, '--status', self.services_list[1].status ] verifylist = [ ('host', self.services_list[0].host), ('status', self.services_list[1].status) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.services_mock.list.assert_called_with( search_opts={ 'host': self.services_list[0].host, 'binary': None, 'status': self.services_list[1].status, 'state': None, 'zone': None }) if api_versions.APIVersion(version) >= api_versions.APIVersion("2.83"): self.assertEqual(self.column_headers_with_reason, columns) self.assertEqual(list(self.values_with_reason), list(data)) else: self.assertEqual(self.column_headers, columns) self.assertEqual(list(self.values), list(data)) @ddt.data('2.82', '2.83') def test_share_service_list_binary_state_zone(self, version): self.app.client_manager.share.api_version = api_versions.APIVersion( version) arglist = [ '--binary', self.services_list[0].binary, '--state', self.services_list[1].state, '--zone', self.services_list[1].zone ] verifylist = [ ('binary', self.services_list[0].binary), ('state', self.services_list[1].state), ('zone', self.services_list[1].zone) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.services_mock.list.assert_called_with( search_opts={ 'host': None, 'binary': self.services_list[0].binary, 'status': None, 'state': self.services_list[1].state, 'zone': self.services_list[1].zone }) if api_versions.APIVersion(version) >= api_versions.APIVersion("2.83"): self.assertEqual(self.column_headers_with_reason, columns) self.assertEqual(list(self.values_with_reason), list(data)) else: self.assertEqual(self.column_headers, columns) self.assertEqual(list(self.values), list(data)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share.py0000664000175000017500000020500500000000000026136 0ustar00zuulzuul00000000000000# 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 ddt from unittest import mock import uuid from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from osc_lib import exceptions as osc_exceptions from osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.api_versions import MAX_VERSION from manilaclient.common.apiclient import exceptions from manilaclient.common import cliutils from manilaclient.osc.v2 import share as osc_shares from manilaclient.tests.unit.osc import osc_fakes 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.export_locations_mock = ( self.app.client_manager.share.share_export_locations ) self.export_locations_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() self.snapshots_mock = self.app.client_manager.share.share_snapshots self.snapshots_mock.reset_mock() self.share_types_mock = self.app.client_manager.share.share_types self.share_types_mock.reset_mock() self.share_networks_mock = self.app.client_manager.share.share_networks self.share_networks_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( MAX_VERSION) 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 def setup_share_groups_mock(self): self.share_group_mock = self.app.client_manager.share.share_groups self.share_group_mock.reset_mock() share_group = manila_fakes.FakeShareGroup.create_one_share_group() self.share_group_mock.get = mock.Mock(return_value=share_group) return share_group @ddt.ddt class TestShareCreate(TestShare): def setUp(self): super(TestShareCreate, self).setUp() self.new_share = manila_fakes.FakeShare.create_one_share( attrs={'status': 'available'} ) self.shares_mock.create.return_value = self.new_share self.shares_mock.get.return_value = self.new_share self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot()) self.snapshots_mock.get.return_value = self.share_snapshot self.share_type = manila_fakes.FakeShareType.create_one_sharetype() self.share_types_mock.get.return_value = self.share_type # 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), '--share-type', self.share_type.id, ] verifylist = [ ('share_proto', self.new_share.share_proto), ('size', self.new_share.size), ('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.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=self.share_type.id, size=self.new_share.size, snapshot_id=None, scheduler_hints={} ) 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), '--share-type', self.share_type.id, '--property', 'Manila=zorilla', '--property', 'Zorilla=manila' ] verifylist = [ ('share_proto', self.new_share.share_proto), ('size', self.new_share.size), ('share_type', self.share_type.id), ('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=self.share_type.id, size=self.new_share.size, snapshot_id=None, scheduler_hints={} ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) def test_share_create_scheduler_hints(self): """Verifies scheduler hints are parsed correctly.""" self.app.client_manager.share.api_version = api_versions.APIVersion( "2.65") shares = self.setup_shares_mock(count=2) share1_name = shares[0].name share2_name = shares[1].name arglist = [ self.new_share.share_proto, str(self.new_share.size), '--share-type', self.share_type.id, '--scheduler-hint', ('same_host=%s' % share1_name), '--scheduler-hint', ('different_host=%s' % share2_name), ] verifylist = [ ('share_proto', self.new_share.share_proto), ('size', self.new_share.size), ('share_type', self.share_type.id), ('scheduler_hint', {'same_host': share1_name, 'different_host': share2_name}), ] 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=self.share_type.id, size=self.new_share.size, snapshot_id=None, scheduler_hints={'same_host': shares[0].id, 'different_host': shares[1].id}, ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) def test_share_create_with_snapshot(self): """Verifies create share from snapshot.""" arglist = [ self.new_share.share_proto, str(self.new_share.size), '--snapshot-id', self.share_snapshot.id ] verifylist = [ ('share_proto', self.new_share.share_proto), ('size', self.new_share.size), ('snapshot_id', self.share_snapshot.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch( 'manilaclient.common.apiclient.utils.find_resource', mock.Mock(return_value=self.share_snapshot)): columns, data = self.cmd.take_action(parsed_args) osc_shares.apiutils.find_resource.assert_called_once_with( mock.ANY, self.share_snapshot.id) 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=self.share_snapshot.id, scheduler_hints={} ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) def test_share_create_wait(self): arglist = [ self.new_share.share_proto, str(self.new_share.size), '--share-type', self.share_type.id, '--wait' ] verifylist = [ ('share_proto', self.new_share.share_proto), ('size', self.new_share.size), ('share_type', self.share_type.id), ('wait', 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( 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=self.share_type.id, size=self.new_share.size, snapshot_id=None, scheduler_hints={} ) self.shares_mock.get.assert_called_with(self.new_share.id) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) @mock.patch('manilaclient.osc.v2.share.LOG') def test_share_create_wait_error(self, mock_logger): arglist = [ self.new_share.share_proto, str(self.new_share.size), '--share-type', self.share_type.id, '--wait' ] verifylist = [ ('share_proto', self.new_share.share_proto), ('size', self.new_share.size), ('share_type', self.share_type.id), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_status', return_value=False): 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=self.share_type.id, size=self.new_share.size, snapshot_id=None, scheduler_hints={} ) mock_logger.error.assert_called_with( "ERROR: Share is in error state.") self.shares_mock.get.assert_called_with(self.new_share.id) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) def test_create_share_with_no_existing_share_type(self): 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) self.share_types_mock.get.side_effect = osc_exceptions.CommandError() self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) self.share_types_mock.get.assert_called_once_with( share_type='default') @ddt.data('None', 'NONE', 'none') def test_create_share_with_the_name_none(self, name): arglist = [ '--name', name, self.new_share.share_proto, str(self.new_share.size), '--share-type', self.share_type.id, ] verifylist = [ ('name', name), ('share_proto', self.new_share.share_proto), ('size', self.new_share.size), ('share_type', self.share_type.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) 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.shares_mock.soft_delete.assert_not_called() self.shares_mock.force_delete.assert_not_called() 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 = [mock.call(s, None) for s in shares] self.shares_mock.delete.assert_has_calls(calls) self.assertIsNone(result) def test_share_delete_with_share_group(self): shares = self.setup_shares_mock(count=1) share_group = self.setup_share_groups_mock() arglist = [ shares[0].name, '--share-group', share_group['id'] ] verifylist = [ ("share_group", share_group['id']), ('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], share_group['id']) 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.shares_mock.delete.assert_not_called() self.shares_mock.soft_delete.assert_not_called() self.assertIsNone(result) def test_share_delete_with_soft(self): shares = self.setup_shares_mock(count=1) arglist = [ '--soft', shares[0].name, ] verifylist = [ ('soft', 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.soft_delete.assert_called_once_with(shares[0]) self.shares_mock.delete.assert_not_called() self.shares_mock.force_delete.assert_not_called() 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) self.shares_mock.delete.side_effect = exceptions.CommandError() self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) 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) def test_share_delete_wait(self): shares = self.setup_shares_mock(count=1) arglist = [ shares[0].name, '--wait' ] verifylist = [ ("force", False), ("share_group", None), ('shares', [shares[0].name]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=True): result = self.cmd.take_action(parsed_args) self.shares_mock.delete.assert_called_with(shares[0], None) self.shares_mock.get.assert_called_with(shares[0].name) self.assertIsNone(result) def test_share_delete_wait_error(self): shares = self.setup_shares_mock(count=1) arglist = [ shares[0].name, '--wait' ] verifylist = [ ("force", False), ("share_group", None), ('shares', [shares[0].name]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=False): self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args ) 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, 'is_soft_deleted': False, 'export_location': None, 'name~': None, 'description~': 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 = self._get_search_opts() 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 = self._get_search_opts() 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 = self._get_search_opts() 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 = self._get_search_opts() 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 = self._get_search_opts() 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 = self._get_search_opts() 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 = self._get_search_opts() 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 = self._get_search_opts() 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 = self._get_search_opts() 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 = self._get_search_opts() search_opts['limit'] = 2 search_opts['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) def test_share_list_name_description_filter(self): arglist = [ '--name~', self.new_share.name, '--description~', self.new_share.description, ] verifylist = [ ('name~', self.new_share.name), ('description~', self.new_share.description), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) cmd_columns, cmd_data = self.cmd.take_action(parsed_args) search_opts = self._get_search_opts() search_opts['name~'] = self.new_share.name search_opts['description~'] = self.new_share.description 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_list_share_soft_deleted_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.60") arglist = [ '--soft-deleted', ] verifylist = [ ('soft_deleted', True), ] search_opts = self._get_search_opts() search_opts['is_soft_deleted'] = True parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) def test_list_share_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.35") arglist = [ '--name~', 'Name', '--description~', 'Description', ] verifylist = [ ('name~', 'Name'), ('description~', 'Description'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) 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.convert_dict_list_to_string = mock.Mock() cliutils.convert_dict_list_to_string.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( methods={"reset_state": None, "reset_task_state": None, "set_metadata": None} ) 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._share.set_metadata.assert_called_with( {'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') def test_share_set_visibility_exception(self): arglist = [ '--public', 'not_a_boolean_value', self._share.id, ] verifylist = [ ('public', 'not_a_boolean_value'), ('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='not_a_boolean_value') # '--public' takes only boolean value, would raise a BadRequest self.shares_mock.update.side_effect = exceptions.BadRequest() self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_set_property_exception(self): arglist = [ '--property', 'key=', self._share.id, ] verifylist = [ ('property', {'key': ''}), ('share', self._share.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self._share.set_metadata.assert_called_with( {'key': ''}) # '--property' takes key=value arguments # missing a value would raise a BadRequest self._share.set_metadata.side_effect = exceptions.BadRequest self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_set_status(self): new_status = 'available' arglist = [ self._share.id, '--status', new_status ] verifylist = [ ('share', self._share.id), ('status', new_status) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self._share.reset_state.assert_called_with(new_status) self.assertIsNone(result) def test_share_set_status_exception(self): new_status = 'available' arglist = [ self._share.id, '--status', new_status ] verifylist = [ ('share', self._share.id), ('status', new_status) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self._share.reset_state.side_effect = Exception() self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_set_task_state(self): new_task_state = 'migration_starting' arglist = [ self._share.id, '--task-state', new_task_state ] verifylist = [ ('share', self._share.id), ('task_state', new_task_state) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self._share.reset_task_state.assert_called_with(new_task_state) self.assertIsNone(result) class TestShareUnset(TestShare): def setUp(self): super(TestShareUnset, self).setUp() self._share = manila_fakes.FakeShare.create_one_share( methods={"delete_metadata": None} ) 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._share.delete_metadata.assert_called_with( 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) def test_share_unset_name_exception(self): arglist = [ '--name', self._share.id, ] verifylist = [ ('name', True), ('share', self._share.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.shares_mock.update.side_effect = exceptions.BadRequest() self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_unset_property_exception(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._share.delete_metadata.assert_called_with( parsed_args.property) # 404 Not Found would be raised, if property 'Manila' doesn't exist self._share.delete_metadata.side_effect = exceptions.NotFound self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) class TestResizeShare(TestShare): def setUp(self): super(TestResizeShare, self).setUp() self._share = manila_fakes.FakeShare.create_one_share( attrs={'size': 2, 'status': 'available'}) self.shares_mock.get.return_value = self._share # Get the command objects to test self.cmd = osc_shares.ResizeShare(self.app, None) def test_share_shrink(self): arglist = [ self._share.id, '1' ] verifylist = [ ('share', self._share.id), ('new_size', 1) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.shares_mock.shrink.assert_called_with( self._share, 1 ) def test_share_shrink_exception(self): arglist = [ self._share.id, '1' ] verifylist = [ ('share', self._share.id), ('new_size', 1) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.shares_mock.shrink.side_effect = Exception() self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_extend(self): arglist = [ self._share.id, '3' ] verifylist = [ ('share', self._share.id), ('new_size', 3) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.shares_mock.extend.assert_called_with( self._share, 3 ) def test_share_extend_exception(self): arglist = [ self._share.id, '3' ] verifylist = [ ('share', self._share.id), ('new_size', 3) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.shares_mock.extend.side_effect = Exception() self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_resize_already_required_size(self): arglist = [ self._share.id, '2' ] verifylist = [ ('share', self._share.id), ('new_size', 2) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args ) def test_share_missing_args(self): arglist = [ self._share.id ] verifylist = [ ('share', self._share.id) ] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_resize_wait(self): arglist = [ self._share.id, '3', '--wait' ] verifylist = [ ('share', self._share.id), ('new_size', 3), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.shares_mock.extend.assert_called_with( self._share, 3 ) self.shares_mock.get.assert_called_with(self._share.id) def test_share_resize_wait_error(self): arglist = [ self._share.id, '3', '--wait' ] verifylist = [ ('share', self._share.id), ('new_size', 3), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_status', return_value=False): self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestAdoptShare(TestShare): def setUp(self): super(TestAdoptShare, self).setUp() self._share_type = manila_fakes.FakeShareType.create_one_sharetype() self.share_types_mock.get.return_value = self._share_type self._share = manila_fakes.FakeShare.create_one_share( attrs={ 'status': 'available', 'share_type': self._share_type.id, 'share_server_id': 'server-id' + uuid.uuid4().hex}) self.shares_mock.get.return_value = self._share self.shares_mock.manage.return_value = self._share # Get the command objects to test self.cmd = osc_shares.AdoptShare(self.app, None) self.datalist = tuple(self._share._info.values()) self.columns = tuple(self._share._info.keys()) def test_share_adopt_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_adopt_required_args(self): arglist = [ 'some.host@driver#pool', 'NFS', '10.0.0.1:/example_path' ] verifylist = [ ('service_host', 'some.host@driver#pool'), ('protocol', 'NFS'), ('export_path', '10.0.0.1:/example_path') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.manage.assert_called_with( description=None, export_path='10.0.0.1:/example_path', name=None, protocol='NFS', service_host='some.host@driver#pool' ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) def test_share_adopt(self): arglist = [ 'some.host@driver#pool', 'NFS', '10.0.0.1:/example_path', '--name', self._share.id, '--description', self._share.description, '--share-type', self._share.share_type, '--driver-options', 'key1=value1', 'key2=value2', '--wait', '--public', '--share-server-id', self._share.share_server_id ] verifylist = [ ('service_host', 'some.host@driver#pool'), ('protocol', 'NFS'), ('export_path', '10.0.0.1:/example_path'), ('name', self._share.id), ('description', self._share.description), ('share_type', self._share_type.id), ('driver_options', ['key1=value1', 'key2=value2']), ('wait', True), ('public', True), ('share_server_id', self._share.share_server_id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.manage.assert_called_with( description=self._share.description, driver_options={'key1': 'value1', 'key2': 'value2'}, export_path='10.0.0.1:/example_path', name=self._share.id, protocol='NFS', service_host='some.host@driver#pool', share_server_id=self._share.share_server_id, share_type=self._share_type.id, public=True ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) @mock.patch('manilaclient.osc.v2.share.LOG') def test_share_adopt_wait_error(self, mock_logger): arglist = [ 'some.host@driver#pool', 'NFS', '10.0.0.1:/example_path', '--wait' ] verifylist = [ ('service_host', 'some.host@driver#pool'), ('protocol', 'NFS'), ('export_path', '10.0.0.1:/example_path'), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_status', return_value=False): columns, data = self.cmd.take_action(parsed_args) self.shares_mock.manage.assert_called_with( description=None, export_path='10.0.0.1:/example_path', name=None, protocol='NFS', service_host='some.host@driver#pool' ) mock_logger.error.assert_called_with( "ERROR: Share is in error state.") self.shares_mock.get.assert_called_with(self._share.id) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) def test_share_adopt_visibility_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.7") arglist = [ 'some.host@driver#pool', 'NFS', '10.0.0.1:/example_path', '--public' ] verifylist = [ ('service_host', 'some.host@driver#pool'), ('protocol', 'NFS'), ('export_path', '10.0.0.1:/example_path'), ('public', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_adopt_share_server_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.48") arglist = [ 'some.host@driver#pool', 'NFS', '10.0.0.1:/example_path', '--share-server-id', self._share.share_server_id ] verifylist = [ ('service_host', 'some.host@driver#pool'), ('protocol', 'NFS'), ('export_path', '10.0.0.1:/example_path'), ('share_server_id', self._share.share_server_id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) class TestAbandonShare(TestShare): def setUp(self): super(TestAbandonShare, self).setUp() self._share = manila_fakes.FakeShare.create_one_share( attrs={'status': 'available'}) self.shares_mock.get.return_value = self._share # Get the command objects to test self.cmd = osc_shares.AbandonShare(self.app, None) def test_share_abandon(self): arglist = [ self._share.id, ] verifylist = [ ('share', [self._share.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.shares_mock.unmanage.assert_called_with(self._share) self.assertIsNone(result) def test_share_abandon_wait(self): arglist = [ self._share.id, '--wait' ] verifylist = [ ('share', [self._share.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=True): result = self.cmd.take_action(parsed_args) self.shares_mock.unmanage.assert_called_with(self._share) self.assertIsNone(result) def test_share_abandon_wait_error(self): arglist = [ self._share.id, '--wait' ] verifylist = [ ('share', [self._share.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=False): self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestShareExportLocationShow(TestShare): def setUp(self): super(TestShareExportLocationShow, 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()) self.export_locations_mock.get.return_value = ( self._export_location ) # Get the command object to test self.cmd = osc_shares.ShareExportLocationShow(self.app, None) def test_share_show_export_location(self): arglist = [ self._share.id, self._export_location.fake_uuid ] verifylist = [ ('share', self._share.id), ('export_location', self._export_location.fake_uuid) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.export_locations_mock.get.assert_called_with( share=self._share, export_location=self._export_location.fake_uuid ) self.assertEqual(tuple(self._export_location._info.keys()), columns) self.assertCountEqual(self._export_location._info.values(), data) class TestShareExportLocationList(TestShare): columns = ['ID', 'Path', 'Preferred'] def setUp(self): super(TestShareExportLocationList, self).setUp() self._share = manila_fakes.FakeShare.create_one_share() self.shares_mock.get.return_value = self._share self._export_locations = [ manila_fakes.FakeShareExportLocation.create_one_export_location()] self.export_locations_mock.list.return_value = ( self._export_locations ) self.values = (oscutils.get_dict_properties( e._info, self.columns) for e in self._export_locations) # Get the command object to test self.cmd = osc_shares.ShareExportLocationList(self.app, None) def test_share_list_export_location(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.export_locations_mock.list.assert_called_with( share=self._share ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.values, data) class TestShowShareProperties(TestShare): properties = { 'key1': 'value1', 'key2': 'value2' } def setUp(self): super(TestShowShareProperties, self).setUp() self._share = manila_fakes.FakeShare.create_one_share( attrs={ 'metadata': osc_fakes.FakeResource( info=self.properties) }, methods={'get_metadata': None} ) self.shares_mock.get.return_value = self._share self.shares_mock.get_metadata.return_value = self._share.metadata # Get the command object to test self.cmd = osc_shares.ShowShareProperties(self.app, None) self.datalist = tuple(self._share.metadata._info.values()) self.columns = tuple(self._share.metadata._info.keys()) def test_share_show_properties(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.shares_mock.get_metadata.assert_called_with(self._share) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) class TestShareRevert(TestShare): def setUp(self): super(TestShareRevert, self).setUp() self.share = manila_fakes.FakeShare.create_one_share( attrs={'revert_to_snapshot_support': True}, methods={'revert_to_snapshot': None} ) self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot( attrs={'share_id': self.share.id})) self.shares_mock.get.return_value = self.share self.snapshots_mock.get.return_value = self.share_snapshot self.cmd = osc_shares.RevertShare(self.app, None) def test_share_revert(self): arglist = [ self.share_snapshot.id ] verifylist = [ ('snapshot', self.share_snapshot.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_snapshot.share_id) self.share.revert_to_snapshot.assert_called_with(self.share_snapshot) self.assertIsNone(result) def test_share_revert_exception(self): arglist = [ self.share_snapshot.id ] verifylist = [ ('snapshot', self.share_snapshot.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.share.revert_to_snapshot.side_effect = Exception() self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareMigrationStart(TestShare): def setUp(self): super(TestShareMigrationStart, self).setUp() self.new_share_type = manila_fakes.FakeShareType.create_one_sharetype() self.share_types_mock.get.return_value = self.new_share_type self.new_share_network = manila_fakes.FakeShareNetwork \ .create_one_share_network() self.share_networks_mock.get.return_value = self.new_share_network self._share = manila_fakes.FakeShare.create_one_share( attrs={ 'status': 'available'}, methods={'migration_start': None}) self.shares_mock.get.return_value = self._share self.shares_mock.manage.return_value = self._share # Get the command objects to test self.cmd = osc_shares.ShareMigrationStart(self.app, None) def test_migration_start_with_new_share_type(self): """Test with new_share_type""" arglist = [ self._share.id, 'host@driver#pool', '--preserve-metadata', 'False', '--preserve-snapshots', 'False', '--writable', 'False', '--nondisruptive', 'False', '--new-share-type', self.new_share_type.id, '--force-host-assisted-migration', 'False' ] verifylist = [ ('share', self._share.id), ('host', 'host@driver#pool'), ('preserve_metadata', 'False'), ('preserve_snapshots', 'False'), ('writable', 'False'), ('nondisruptive', 'False'), ('new_share_type', self.new_share_type.id), ('force_host_assisted_migration', 'False') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self._share.migration_start.assert_called_with( "host@driver#pool", 'False', 'False', 'False', 'False', 'False', None, self.new_share_type.id ) self.assertIsNone(result) def test_migration_start_with_new_share_network(self): """Test with new_share_network""" arglist = [ self._share.id, 'host@driver#pool', '--preserve-metadata', 'False', '--preserve-snapshots', 'False', '--writable', 'False', '--nondisruptive', 'False', '--new-share-network', self.new_share_network.id, '--force-host-assisted-migration', 'False' ] verifylist = [ ('share', self._share.id), ('host', 'host@driver#pool'), ('preserve_metadata', 'False'), ('preserve_snapshots', 'False'), ('writable', 'False'), ('nondisruptive', 'False'), ('new_share_network', self.new_share_network.id), ('force_host_assisted_migration', 'False') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self._share.migration_start.assert_called_with( "host@driver#pool", 'False', 'False', 'False', 'False', 'False', self.new_share_network.id, None ) self.assertIsNone(result) class TestShareMigrationCancel(TestShare): def setUp(self): super(TestShareMigrationCancel, self).setUp() self._share = manila_fakes.FakeShare.create_one_share( attrs={ 'status': 'migrating'}, methods={'migration_cancel': None}) self.shares_mock.get.return_value = self._share self.shares_mock.manage.return_value = self._share # Get the command objects to test self.cmd = osc_shares.ShareMigrationCancel(self.app, None) def test_migration_cancel(self): arglist = [ self._share.id ] verifylist = [ ('share', self._share.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self._share.migration_cancel.assert_called self.assertIsNone(result) class TestShareMigrationComplete(TestShare): def setUp(self): super(TestShareMigrationComplete, self).setUp() self._share = manila_fakes.FakeShare.create_one_share( attrs={ 'status': 'migrating'}, methods={'migration_complete': None}) self.shares_mock.get.return_value = self._share self.shares_mock.manage.return_value = self._share # Get the command objects to test self.cmd = osc_shares.ShareMigrationComplete(self.app, None) def test_migration_complete(self): arglist = [ self._share.id ] verifylist = [ ('share', self._share.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self._share.migration_complete.assert_called self.assertIsNone(result) class TestShareMigrationShow(TestShare): def setUp(self): super(TestShareMigrationShow, self).setUp() self._share = manila_fakes.FakeShare.create_one_share( attrs={ 'status': 'available', 'task_state': 'migration_in_progress'}, methods={'migration_get_progress': ("", {'total_progress': 0, 'task_state': 'migration_in_progress', 'details': {}}) }) self.shares_mock.get.return_value = self._share self.shares_mock.manage.return_value = self._share # Get the command objects to test self.cmd = osc_shares.ShareMigrationShow(self.app, None) def test_migration_show(self): arglist = [ self._share.id ] verifylist = [ ('share', self._share.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self._share.migration_get_progress.assert_called class TestShareRestore(TestShare): def setUp(self): super(TestShareRestore, self).setUp() self.share = manila_fakes.FakeShare.create_one_share( methods={'restore': None} ) self.shares_mock.get.return_value = self.share self.cmd = osc_shares.RestoreShare(self.app, None) def test_share_restore(self): arglist = [ self.share.name ] verifylist = [ ('share', [self.share.name]) ] 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) self.share.restore.assert_called_with(self.share) self.assertIsNone(result) def test_share_restore_exception(self): arglist = [ self.share.name ] verifylist = [ ('share', [self.share.name]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.share.restore.side_effect = Exception() self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_access_rules.py0000664000175000017500000005005100000000000030670 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt from osc_lib import exceptions 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( api_versions.MAX_VERSION) self.shares_mock.reset_mock() self.access_rules_mock = ( self.app.client_manager.share.share_access_rules) self.access_rules_mock.reset_mock() @ddt.ddt 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 self.access_rules_mock.get.return_value = self.access_rule # 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) @ddt.data( {'lock_visibility': True, 'lock_deletion': True, 'lock_reason': 'testing resource locks'}, {'lock_visibility': False, 'lock_deletion': True, 'lock_reason': None}, {'lock_visibility': True, 'lock_deletion': False, 'lock_reason': None}, ) @ddt.unpack def test_share_access_create_restrict(self, lock_visibility, lock_deletion, lock_reason): arglist = [ self.share.id, 'user', 'demo', '--properties', 'key=value' ] verifylist = [ ("share", self.share.id), ("access_type", "user"), ("access_to", "demo"), ('properties', ['key=value']), ] allow_call_kwargs = {} if lock_visibility: arglist.append('--lock-visibility') verifylist.append(('lock_visibility', lock_visibility)) allow_call_kwargs['lock_visibility'] = lock_visibility if lock_deletion: arglist.append('--lock-deletion') verifylist.append(('lock_deletion', lock_deletion)) allow_call_kwargs['lock_deletion'] = lock_deletion if lock_reason: arglist.append('--lock-reason') arglist.append(lock_reason) verifylist.append(('lock_reason', lock_reason)) allow_call_kwargs['lock_reason'] = lock_reason 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'}, **allow_call_kwargs ) self.assertEqual(ACCESS_RULE_ATTRIBUTES, columns) self.assertCountEqual(self.access_rule._info.values(), data) @ddt.data( {'lock_visibility': True, 'lock_deletion': False}, {'lock_visibility': False, 'lock_deletion': True}, ) @ddt.unpack def test_share_access_create_restrict_not_available( self, lock_visibility, lock_deletion): arglist = [ self.share.id, 'user', 'demo', ] self.app.client_manager.share.api_version = api_versions.APIVersion( "2.79") verifylist = [ ("share", self.share.id), ("access_type", "user"), ("access_to", "demo"), ("lock_visibility", lock_visibility), ("lock_deletion", lock_deletion), ("lock_reason", None), ] if lock_visibility: arglist.append('--lock-visibility') if lock_deletion: arglist.append('--lock-deletion') parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) 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) def test_share_access_create_wait(self): arglist = [ self.share.id, 'user', 'demo', '--wait' ] verifylist = [ ("share", self.share.id), ("access_type", "user"), ("access_to", "demo"), ("wait", True), ] 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) @mock.patch('manilaclient.osc.v2.share_access_rules.LOG') def test_share_access_create_wait_error(self, mock_logger): arglist = [ self.share.id, 'user', 'demo', '--wait' ] verifylist = [ ("share", self.share.id), ("access_type", "user"), ("access_to", "demo"), ("wait", True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_status', return_value=False): 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={}, ) mock_logger.error.assert_called_with( "ERROR: Share access rule is in error state.") self.assertEqual(ACCESS_RULE_ATTRIBUTES, columns) self.assertCountEqual(self.access_rule._info.values(), data) @ddt.ddt 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) @ddt.data(True, False) def test_share_access_delete(self, unrestrict): arglist = [ self.share.id, self.access_rule.id ] verifylist = [ ("share", self.share.id), ("id", self.access_rule.id), ] deny_kwargs = {} if unrestrict: arglist.append('--unrestrict') verifylist.append(("unrestrict", unrestrict)) deny_kwargs['unrestrict'] = unrestrict 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, **deny_kwargs) self.assertIsNone(result) def test_share_access_delete_unrestrict_not_available(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.79") arglist = [ self.share.id, self.access_rule.id, "--unrestrict" ] verifylist = [ ("share", self.share.id), ("id", self.access_rule.id), ("unrestrict", True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_access_delete_wait(self): arglist = [ self.share.id, self.access_rule.id, '--wait' ] verifylist = [ ("share", self.share.id), ("id", self.access_rule.id), ('wait', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=True): 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) def test_share_access_delete_wait_error(self): arglist = [ self.share.id, self.access_rule.id, '--wait' ] verifylist = [ ("share", self.share.id), ("id", self.access_rule.id), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=False): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) @ddt.ddt 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)) @ddt.data( {'access_to': '10.0.0.0/0', 'access_type': 'ip'}, {'access_key': '10.0.0.0/0', 'access_level': 'rw'}, ) def test_access_rules_list_access_filters(self, filters): arglist = [ self.share.id, ] verifylist = [ ("share", self.share.id), ] for filter_key, filter_value in filters.items(): filter_arg = filter_key.replace("_", "-") arglist.append(f'--{filter_arg}') arglist.append(filter_value) verifylist.append((filter_key, filter_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, filters) self.assertEqual(self.access_rules_columns, columns) self.assertEqual(tuple(self.values_list), tuple(data)) @ddt.data( {'access_to': '10.0.0.0/0', 'access_type': 'ip'}, {'access_key': '10.0.0.0/0', 'access_level': 'rw'}, ) def test_access_rules_list_access_filters_command_error(self, filters): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.81") arglist = [ self.share.id, ] verifylist = [ ("share", self.share.id), ] for filter_key, filter_value in filters.items(): filter_arg = filter_key.replace("_", "-") arglist.append(f'--{filter_arg}') arglist.append(filter_value) verifylist.append((filter_key, filter_value)) parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_backups.py0000664000175000017500000003251400000000000027651 0ustar00zuulzuul00000000000000# 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 osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.api_versions import MAX_VERSION from manilaclient.osc.v2 import share_backups as osc_share_backups from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShareBackup(manila_fakes.TestShare): def setUp(self): super(TestShareBackup, self).setUp() self.shares_mock = self.app.client_manager.share.shares self.shares_mock.reset_mock() self.backups_mock = self.app.client_manager.share.share_backups self.backups_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( MAX_VERSION) class TestShareBackupCreate(TestShareBackup): def setUp(self): super(TestShareBackupCreate, self).setUp() self.share = manila_fakes.FakeShare.create_one_share() self.shares_mock.get.return_value = self.share self.share_backup = ( manila_fakes.FakeShareBackup.create_one_backup( attrs={'status': 'available'} )) self.backups_mock.create.return_value = self.share_backup self.backups_mock.get.return_value = self.share_backup self.cmd = osc_share_backups.CreateShareBackup(self.app, None) self.data = tuple(self.share_backup._info.values()) self.columns = tuple(self.share_backup._info.keys()) def test_share_backup_create_missing_args(self): arglist = [] verifylist = [] self.assertRaises( osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_backup_create(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.backups_mock.create.assert_called_with( self.share, ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_backup_create_name(self): arglist = [ self.share.id, '--name', "FAKE_SHARE_BACKUP_NAME" ] verifylist = [ ('share', self.share.id), ('name', "FAKE_SHARE_BACKUP_NAME") ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.backups_mock.create.assert_called_with( self.share, name="FAKE_SHARE_BACKUP_NAME", ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestShareBackupDelete(TestShareBackup): def setUp(self): super(TestShareBackupDelete, self).setUp() self.share_backup = ( manila_fakes.FakeShareBackup.create_one_backup()) self.backups_mock.get.return_value = self.share_backup self.cmd = osc_share_backups.DeleteShareBackup(self.app, None) def test_share_backup_delete_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_backup_delete(self): arglist = [ self.share_backup.id ] verifylist = [ ('backup', [self.share_backup.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.backups_mock.delete.assert_called_with(self.share_backup) self.assertIsNone(result) def test_share_backup_delete_multiple(self): share_backups = ( manila_fakes.FakeShareBackup.create_share_backups( count=2)) arglist = [ share_backups[0].id, share_backups[1].id ] verifylist = [ ('backup', [share_backups[0].id, share_backups[1].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertEqual(self.backups_mock.delete.call_count, len(share_backups)) self.assertIsNone(result) def test_share_backup_delete_exception(self): arglist = [ self.share_backup.id ] verifylist = [ ('backup', [self.share_backup.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.backups_mock.delete.side_effect = exceptions.CommandError() self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareBackupList(TestShareBackup): columns = [ 'ID', 'Name', 'Share ID', 'Status', ] detailed_columns = [ 'ID', 'Name', 'Share ID', 'Status', 'Description', 'Size', 'Created At', 'Updated At', 'Availability Zone', 'Progress', 'Restore Progress', 'Host', 'Topic', ] def setUp(self): super(TestShareBackupList, self).setUp() self.share = manila_fakes.FakeShare.create_one_share() self.shares_mock.get.return_value = self.share self.backups_list = ( manila_fakes.FakeShareBackup.create_share_backups( count=2)) self.backups_mock.list.return_value = self.backups_list self.values = (oscutils.get_dict_properties( i._info, self.columns) for i in self.backups_list) self.detailed_values = (oscutils.get_dict_properties( i._info, self.detailed_columns) for i in self.backups_list) self.cmd = osc_share_backups.ListShareBackup(self.app, None) def test_share_backup_list(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.backups_mock.list.assert_called_with( detailed=0, search_opts={ 'offset': None, 'limit': None, 'name': None, 'description': None, 'name~': None, 'description~': None, 'status': None, 'share_id': None }, sort_key=None, sort_dir=None ) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) def test_share_backup_list_detail(self): arglist = [ '--detail' ] verifylist = [ ('detail', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.backups_mock.list.assert_called_with( detailed=1, search_opts={ 'offset': None, 'limit': None, 'name': None, 'description': None, 'name~': None, 'description~': None, 'status': None, 'share_id': None }, sort_key=None, sort_dir=None ) self.assertEqual(self.detailed_columns, columns) self.assertEqual(list(self.detailed_values), list(data)) def test_share_backup_list_for_share(self): arglist = [ '--share', 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.backups_mock.list.assert_called_with( detailed=0, search_opts={ 'offset': None, 'limit': None, 'name': None, 'description': None, 'name~': None, 'description~': None, 'status': None, 'share_id': self.share.id }, sort_key=None, sort_dir=None ) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) class TestShareBackupShow(TestShareBackup): def setUp(self): super(TestShareBackupShow, self).setUp() self.share_backup = ( manila_fakes.FakeShareBackup.create_one_backup() ) self.backups_mock.get.return_value = self.share_backup self.cmd = osc_share_backups.ShowShareBackup(self.app, None) self.data = tuple(self.share_backup._info.values()) self.columns = tuple(self.share_backup._info.keys()) def test_share_backup_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises( osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_backup_show(self): arglist = [ self.share_backup.id ] verifylist = [ ('backup', self.share_backup.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.backups_mock.get.assert_called_with( self.share_backup.id ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestShareBackupRestore(TestShareBackup): def setUp(self): super(TestShareBackupRestore, self).setUp() self.share_backup = ( manila_fakes.FakeShareBackup.create_one_backup() ) self.backups_mock.get.return_value = self.share_backup self.cmd = osc_share_backups.RestoreShareBackup( self.app, None) def test_share_backup_restore(self): arglist = [ self.share_backup.id, ] verifylist = [ ('backup', self.share_backup.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.backups_mock.restore.assert_called_with(self.share_backup.id) self.assertIsNone(result) class TestShareBackupSet(TestShareBackup): def setUp(self): super(TestShareBackupSet, self).setUp() self.share_backup = ( manila_fakes.FakeShareBackup.create_one_backup() ) self.backups_mock.get.return_value = self.share_backup self.cmd = osc_share_backups.SetShareBackup(self.app, None) def test_set_share_backup_name(self): arglist = [ self.share_backup.id, '--name', "FAKE_SHARE_BACKUP_NAME" ] verifylist = [ ('backup', self.share_backup.id), ('name', "FAKE_SHARE_BACKUP_NAME") ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.backups_mock.update.assert_called_with(self.share_backup, name=parsed_args.name) self.assertIsNone(result) def test_set_backup_status(self): arglist = [ self.share_backup.id, '--status', 'available' ] verifylist = [ ('backup', self.share_backup.id), ('status', 'available') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.backups_mock.reset_status.assert_called_with( self.share_backup, parsed_args.status) self.assertIsNone(result) class TestShareBackupUnset(TestShareBackup): def setUp(self): super(TestShareBackupUnset, self).setUp() self.share_backup = ( manila_fakes.FakeShareBackup.create_one_backup() ) self.backups_mock.get.return_value = self.share_backup self.cmd = osc_share_backups.UnsetShareBackup(self.app, None) def test_unset_backup_name(self): arglist = [ self.share_backup.id, '--name' ] verifylist = [ ('backup', self.share_backup.id), ('name', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.backups_mock.update.assert_called_with( self.share_backup, name=None) self.assertIsNone(result) def test_unset_backup_description(self): arglist = [ self.share_backup.id, '--description' ] verifylist = [ ('backup', self.share_backup.id), ('description', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.backups_mock.update.assert_called_with( self.share_backup, description=None) self.assertIsNone(result) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_group_snapshots.py0000664000175000017500000005207000000000000031456 0ustar00zuulzuul00000000000000# 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 uuid from osc_lib import exceptions from osc_lib import utils as oscutils from unittest import mock from manilaclient import api_versions from manilaclient.osc.v2 import ( share_group_snapshots as osc_share_group_snapshots) from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes LOG = logging.getLogger(__name__) class TestShareGroupSnapshot(manila_fakes.TestShare): def setUp(self): super(TestShareGroupSnapshot, self).setUp() self.groups_mock = self.app.client_manager.share.share_groups self.groups_mock.reset_mock() self.group_snapshot_mocks = ( self.app.client_manager.share.share_group_snapshots) self.group_snapshot_mocks.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION) class TestCreateShareGroupSnapshot(TestShareGroupSnapshot): def setUp(self): super(TestCreateShareGroupSnapshot, self).setUp() self.share_group = ( manila_fakes.FakeShareGroup.create_one_share_group() ) self.groups_mock.get.return_value = self.share_group self.share_group_snapshot = ( manila_fakes.FakeShareGroupSnapshot .create_one_share_group_snapshot() ) self.group_snapshot_mocks.create.return_value = ( self.share_group_snapshot) self.group_snapshot_mocks.get.return_value = ( self.share_group_snapshot) self.cmd = osc_share_group_snapshots.CreateShareGroupSnapshot( self.app, None) self.data = tuple(self.share_group_snapshot._info.values()) self.columns = tuple(self.share_group_snapshot._info.keys()) def test_share_group_snapshot_create_missing_args(self): arglist = [] verifylist = [] self.assertRaises( osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_group_snapshot_create(self): arglist = [ self.share_group.id ] verifylist = [ ('share_group', self.share_group.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.group_snapshot_mocks.create.assert_called_with( self.share_group, name=None, description=None ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_group_snapshot_create_options(self): arglist = [ self.share_group.id, '--name', self.share_group_snapshot.name, '--description', self.share_group_snapshot.description ] verifylist = [ ('share_group', self.share_group.id), ('name', self.share_group_snapshot.name), ('description', self.share_group_snapshot.description) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.group_snapshot_mocks.create.assert_called_with( self.share_group, name=self.share_group_snapshot.name, description=self.share_group_snapshot.description, ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_group_snapshot_create_wait(self): arglist = [ self.share_group.id, '--wait' ] verifylist = [ ('share_group', self.share_group.id), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) with mock.patch('osc_lib.utils.wait_for_status', return_value=True): self.group_snapshot_mocks.create.assert_called_with( self.share_group, name=None, description=None, ) self.group_snapshot_mocks.get.assert_called_with( self.share_group_snapshot.id) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) @mock.patch('manilaclient.osc.v2.share_group_snapshots.LOG') def test_share_group_snapshot_create_wait_exception(self, mock_logger): arglist = [ self.share_group.id, '--wait' ] verifylist = [ ('share_group', self.share_group.id), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_status', return_value=False): columns, data = self.cmd.take_action(parsed_args) self.group_snapshot_mocks.create.assert_called_with( self.share_group, name=None, description=None, ) mock_logger.error.assert_called_with( "ERROR: Share group snapshot is in error state.") self.group_snapshot_mocks.get.assert_called_with( self.share_group_snapshot.id) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestDeleteShareGroupSnapshot(TestShareGroupSnapshot): def setUp(self): super(TestDeleteShareGroupSnapshot, self).setUp() self.share_group_snapshot = ( manila_fakes.FakeShareGroupSnapshot .create_one_share_group_snapshot()) self.group_snapshot_mocks.get.return_value = ( self.share_group_snapshot) self.cmd = osc_share_group_snapshots.DeleteShareGroupSnapshot( self.app, None) def test_share_group_snapshot_delete_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_group_snapshot_delete(self): arglist = [ self.share_group_snapshot.id ] verifylist = [ ('share_group_snapshot', [self.share_group_snapshot.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.group_snapshot_mocks.delete.assert_called_with( self.share_group_snapshot, force=False) self.assertIsNone(result) def test_share_group_snapshot_delete_force(self): arglist = [ self.share_group_snapshot.id, '--force' ] verifylist = [ ('share_group_snapshot', [self.share_group_snapshot.id]), ('force', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.group_snapshot_mocks.delete.assert_called_with( self.share_group_snapshot, force=True) self.assertIsNone(result) def test_share_group_snapshot_delete_multiple(self): share_group_snapshots = ( manila_fakes.FakeShareGroupSnapshot. create_share_group_snapshots(count=2)) arglist = [ share_group_snapshots[0].id, share_group_snapshots[1].id ] verifylist = [ ('share_group_snapshot', [share_group_snapshots[0].id, ( share_group_snapshots[1].id)]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertEqual(self.group_snapshot_mocks.delete.call_count, len(share_group_snapshots)) self.assertIsNone(result) def test_share_group_snapshot_delete_exception(self): arglist = [ self.share_group_snapshot.id ] verifylist = [ ('share_group_snapshot', [self.share_group_snapshot.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.group_snapshot_mocks.delete.side_effect = ( exceptions.CommandError()) self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_group_snapshot_delete_wait(self): arglist = [ self.share_group_snapshot.id, '--wait' ] verifylist = [ ('share_group_snapshot', [self.share_group_snapshot.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=True): result = self.cmd.take_action(parsed_args) self.group_snapshot_mocks.delete.assert_called_with( self.share_group_snapshot, force=False) self.group_snapshot_mocks.get.assert_called_with( self.share_group_snapshot.id) self.assertIsNone(result) def test_share_group_snapshot_delete_wait_exception(self): arglist = [ self.share_group_snapshot.id, '--wait' ] verifylist = [ ('share_group_snapshot', [self.share_group_snapshot.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=False): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestShowShareGroupSnapshot(TestShareGroupSnapshot): def setUp(self): super(TestShowShareGroupSnapshot, self).setUp() self.share_group_snapshot = ( manila_fakes.FakeShareGroupSnapshot .create_one_share_group_snapshot()) self.group_snapshot_mocks.get.return_value = ( self.share_group_snapshot) self.cmd = osc_share_group_snapshots.ShowShareGroupSnapshot( self.app, None) self.data = tuple(self.share_group_snapshot._info.values()) self.columns = tuple(self.share_group_snapshot._info.keys()) def test_share_group_snapshot_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises( osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_group_show(self): arglist = [ self.share_group_snapshot.id ] verifylist = [ ('share_group_snapshot', self.share_group_snapshot.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.group_snapshot_mocks.get.assert_called_with( self.share_group_snapshot.id ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestSetShareGroupSnapshot(TestShareGroupSnapshot): def setUp(self): super(TestSetShareGroupSnapshot, self).setUp() self.share_group_snapshot = ( manila_fakes.FakeShareGroupSnapshot .create_one_share_group_snapshot()) self.group_snapshot_mocks.get.return_value = ( self.share_group_snapshot) self.cmd = osc_share_group_snapshots.SetShareGroupSnapshot( self.app, None) self.data = tuple(self.share_group_snapshot._info.values()) self.columns = tuple(self.share_group_snapshot._info.keys()) def test_set_share_group_snapshot_name_description(self): group_snapshot_name = 'group-snapshot-name-' + uuid.uuid4().hex group_snapshot_description = ( 'group-snapshot-description-' + uuid.uuid4().hex) arglist = [ self.share_group_snapshot.id, '--name', group_snapshot_name, '--description', group_snapshot_description ] verifylist = [ ('share_group_snapshot', self.share_group_snapshot.id), ('name', group_snapshot_name), ('description', group_snapshot_description) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.group_snapshot_mocks.update.assert_called_with( self.share_group_snapshot, name=parsed_args.name, description=parsed_args.description) self.assertIsNone(result) def test_set_share_group_snapshot_status(self): arglist = [ self.share_group_snapshot.id, '--status', 'available' ] verifylist = [ ('share_group_snapshot', self.share_group_snapshot.id), ('status', 'available') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.group_snapshot_mocks.reset_state.assert_called_with( self.share_group_snapshot, 'available') self.assertIsNone(result) def test_set_share_group_snapshot_exception(self): arglist = [ self.share_group_snapshot.id, '--status', 'available' ] verifylist = [ ('share_group_snapshot', self.share_group_snapshot.id), ('status', 'available') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.group_snapshot_mocks.reset_state.side_effect = Exception() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestUnsetShareGroupSnapshot(TestShareGroupSnapshot): def setUp(self): super(TestUnsetShareGroupSnapshot, self).setUp() self.share_group_snapshot = ( manila_fakes.FakeShareGroupSnapshot .create_one_share_group_snapshot()) self.group_snapshot_mocks.get.return_value = ( self.share_group_snapshot) self.cmd = osc_share_group_snapshots.UnsetShareGroupSnapshot( self.app, None) def test_unset_share_group_snapshot_name_description(self): arglist = [ self.share_group_snapshot.id, '--name', '--description' ] verifylist = [ ('share_group_snapshot', self.share_group_snapshot.id), ('name', True), ('description', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.group_snapshot_mocks.update.assert_called_with( self.share_group_snapshot, name='', description='') self.assertIsNone(result) def test_unset_share_group_snapshot_name_exception(self): arglist = [ self.share_group_snapshot.id, '--name', ] verifylist = [ ('share_group_snapshot', self.share_group_snapshot.id), ('name', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.group_snapshot_mocks.update.side_effect = Exception() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestListShareGroupSnapshot(TestShareGroupSnapshot): columns = [ 'ID', 'Name', 'Status', 'Description', ] def setUp(self): super(TestListShareGroupSnapshot, self).setUp() self.share_group = ( manila_fakes.FakeShareGroup.create_one_share_group() ) self.groups_mock.get.return_value = self.share_group self.share_group_snapshot = ( manila_fakes.FakeShareGroupSnapshot .create_one_share_group_snapshot({ 'share_group_id': self.share_group.id })) self.share_group_snapshots_list = [self.share_group_snapshot] self.group_snapshot_mocks.list.return_value = ( self.share_group_snapshots_list) self.values = ( oscutils.get_dict_properties(s._info, self.columns) for s in self.share_group_snapshots_list ) self.cmd = osc_share_group_snapshots.ListShareGroupSnapshot( self.app, None) def test_share_group_snapshot_list_no_options(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.group_snapshot_mocks.list.assert_called_once_with( search_opts={ 'all_tenants': False, 'name': None, 'status': None, 'share_group_id': None, 'limit': None, 'offset': None, }) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) def test_share_group_snapshot_list_detail_all_projects(self): columns_detail = [ 'ID', 'Name', 'Status', 'Description', 'Created At', 'Share Group ID', 'Project ID' ] values = ( oscutils.get_dict_properties(s._info, columns_detail) for s in self.share_group_snapshots_list) arglist = [ '--detailed', '--all-projects', ] verifylist = [ ('detailed', True), ('all_projects', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.group_snapshot_mocks.list.assert_called_once_with( search_opts={ 'all_tenants': True, 'name': None, 'status': None, 'share_group_id': None, 'limit': None, 'offset': None, }) self.assertEqual(columns_detail, columns) self.assertEqual(list(values), list(data)) def test_share_group_snapshot_list_search_options(self): arglist = [ '--name', self.share_group_snapshot.name, '--status', self.share_group_snapshot.status, '--share-group', self.share_group.id, ] verifylist = [ ('name', self.share_group_snapshot.name), ('status', self.share_group_snapshot.status), ('share_group', self.share_group.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.groups_mock.get.assert_called_with(self.share_group.id) self.group_snapshot_mocks.list.assert_called_once_with( search_opts={ 'all_tenants': False, 'name': self.share_group_snapshot.name, 'status': self.share_group_snapshot.status, 'share_group_id': self.share_group.id, 'limit': None, 'offset': None, }) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) class TestListShareGroupSnapshotMembers(TestShareGroupSnapshot): columns = [ 'Share ID', 'Size', ] def setUp(self): super(TestListShareGroupSnapshotMembers, self).setUp() self.share = manila_fakes.FakeShare.create_one_share() self.share_group_snapshot = ( manila_fakes.FakeShareGroupSnapshot .create_one_share_group_snapshot({ 'members': [{ 'share_id': self.share.id, 'size': self.share.size }] })) self.group_snapshot_mocks.get.return_value = ( self.share_group_snapshot) self.values = ( oscutils.get_dict_properties(s, self.columns) for s in self.share_group_snapshot.members ) self.cmd = osc_share_group_snapshots.ListShareGroupSnapshotMembers( self.app, None) def test_share_group_snapshot_list_members(self): arglist = [ self.share_group_snapshot.id ] verifylist = [ ('share_group_snapshot', self.share_group_snapshot.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.group_snapshot_mocks.get.assert_called_with( self.share_group_snapshot.id) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_group_type.py0000664000175000017500000004244100000000000030416 0ustar00zuulzuul00000000000000# 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 unittest import mock 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 import utils from manilaclient.osc.v2 import share_group_types as osc_share_group_types from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes COLUMNS = [ 'ID', 'Name', 'Share Types', 'Visibility', 'Is Default', 'Group Specs', ] class TestShareGroupType(manila_fakes.TestShare): def setUp(self): super(TestShareGroupType, self).setUp() self.sgt_mock = self.app.client_manager.share.share_group_types self.sgt_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION) class TestShareGroupTypeCreate(TestShareGroupType): def setUp(self): super(TestShareGroupTypeCreate, self).setUp() self.share_types = ( manila_fakes.FakeShareType.create_share_types(count=2)) formatted_share_types = [] for st in self.share_types: formatted_share_types.append(st.name) self.share_group_type = ( manila_fakes.FakeShareGroupType.create_one_share_group_type( attrs={ 'share_types': formatted_share_types } )) self.share_group_type_formatted = ( manila_fakes.FakeShareGroupType.create_one_share_group_type( attrs={ 'id': self.share_group_type['id'], 'name': self.share_group_type['name'], 'share_types': formatted_share_types } )) formatted_sgt = utils.format_share_group_type( self.share_group_type_formatted) self.sgt_mock.create.return_value = self.share_group_type self.sgt_mock.get.return_value = self.share_group_type # Get the command object to test self.cmd = osc_share_group_types.CreateShareGroupType(self.app, None) self.data = tuple(formatted_sgt.values()) self.columns = tuple(formatted_sgt.keys()) def test_share_group_type_create_required_args(self): """Verifies required arguments.""" arglist = [ self.share_group_type.name, self.share_types[0].name, self.share_types[1].name, ] verifylist = [ ('name', self.share_group_type.name), ('share_types', [self.share_types[0].name, self.share_types[1].name]) ] with mock.patch( 'manilaclient.common.apiclient.utils.find_resource', side_effect=[self.share_types[0], self.share_types[1]]): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.sgt_mock.create.assert_called_with( group_specs={}, is_public=True, name=self.share_group_type.name, share_types=[ self.share_types[0].name, self.share_types[1].name] ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_group_type_create_missing_required_arg(self): """Verifies missing required arguments.""" arglist = [ self.share_group_type.name, ] verifylist = [ ('name', self.share_group_type.name) ] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_group_type_create_private(self): arglist = [ self.share_group_type.name, self.share_types[0].name, self.share_types[1].name, '--public', 'False' ] verifylist = [ ('name', self.share_group_type.name), ('share_types', [self.share_types[0].name, self.share_types[1].name]), ('public', 'False') ] with mock.patch( 'manilaclient.common.apiclient.utils.find_resource', side_effect=[self.share_types[0], self.share_types[1]]): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.sgt_mock.create.assert_called_with( group_specs={}, is_public=False, name=self.share_group_type.name, share_types=[self.share_types[0].name, self.share_types[1].name] ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_group_type_create_group_specs(self): arglist = [ self.share_group_type.name, self.share_types[0].name, self.share_types[1].name, '--group-specs', 'consistent_snapshot_support=true' ] verifylist = [ ('name', self.share_group_type.name), ('share_types', [self.share_types[0].name, self.share_types[1].name]), ('group_specs', ['consistent_snapshot_support=true']) ] with mock.patch( 'manilaclient.common.apiclient.utils.find_resource', side_effect=[self.share_types[0], self.share_types[1]]): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.sgt_mock.create.assert_called_with( group_specs={'consistent_snapshot_support': 'True'}, is_public=True, name=self.share_group_type.name, share_types=[ self.share_types[0].name, self.share_types[1].name] ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_create_share_group_type(self): arglist = [ self.share_group_type.name, self.share_types[0].name, self.share_types[1].name ] verifylist = [ ('name', self.share_group_type.name), ('share_types', [self.share_types[0].name, self.share_types[1].name]) ] with mock.patch( 'manilaclient.common.apiclient.utils.find_resource', side_effect=[self.share_types[0], self.share_types[1], self.share_group_type]): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.sgt_mock.create.assert_called_with( group_specs={}, is_public=True, name=self.share_group_type.name, share_types=[self.share_types[0].name, self.share_types[1].name] ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestShareGroupTypeDelete(TestShareGroupType): def setUp(self): super(TestShareGroupTypeDelete, self).setUp() self.share_group_types = ( manila_fakes.FakeShareGroupType.create_share_group_types(count=2)) self.sgt_mock.delete.return_value = None self.sgt_mock.get = ( manila_fakes.FakeShareGroupType.get_share_group_types( self.share_group_types)) # Get the command object to test self.cmd = osc_share_group_types.DeleteShareGroupType(self.app, None) def test_share_group_type_delete_one(self): arglist = [ self.share_group_types[0].name ] verifylist = [ ('share_group_types', [self.share_group_types[0].name]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.sgt_mock.delete.assert_called_with( self.share_group_types[0]) self.assertIsNone(result) def test_share_group_type_delete_multiple(self): arglist = [] for t in self.share_group_types: arglist.append(t.name) verifylist = [ ('share_group_types', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) calls = [] for t in self.share_group_types: calls.append(mock.call(t)) self.sgt_mock.delete.assert_has_calls(calls) self.assertIsNone(result) def test_delete_share_group_type_with_exception(self): arglist = [ 'non_existing_type', ] verifylist = [ ('share_group_types', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.sgt_mock.delete.side_effect = exceptions.CommandError() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_delete_share_group_type(self): arglist = [ self.share_group_types[0].name ] verifylist = [ ('share_group_types', [self.share_group_types[0].name]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.sgt_mock.delete.assert_called_with(self.share_group_types[0]) self.assertIsNone(result) class TestShareGroupTypeSet(TestShareGroupType): def setUp(self): super(TestShareGroupTypeSet, self).setUp() self.share_group_type = ( manila_fakes.FakeShareGroupType.create_one_share_group_type( methods={'set_keys': None, 'update': None})) self.sgt_mock.get.return_value = self.share_group_type # Get the command object to test self.cmd = osc_share_group_types.SetShareGroupType(self.app, None) def test_share_group_type_set_group_specs(self): arglist = [ self.share_group_type.id, '--group-specs', 'consistent_snapshot_support=true' ] verifylist = [ ('share_group_type', self.share_group_type.id), ('group_specs', ['consistent_snapshot_support=true']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.share_group_type.set_keys.assert_called_with( {'consistent_snapshot_support': 'True'}) self.assertIsNone(result) def test_share_group_type_set_extra_specs_exception(self): arglist = [ self.share_group_type.id, '--group-specs', 'snapshot_support=true' ] verifylist = [ ('share_group_type', self.share_group_type.id), ('group_specs', ['snapshot_support=true']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.share_group_type.set_keys.side_effect = BadRequest() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareGroupTypeUnset(TestShareGroupType): def setUp(self): super(TestShareGroupTypeUnset, self).setUp() self.share_group_type = ( manila_fakes.FakeShareGroupType.create_one_share_group_type( methods={'unset_keys': None})) self.sgt_mock.get.return_value = self.share_group_type # Get the command object to test self.cmd = osc_share_group_types.UnsetShareGroupType(self.app, None) def test_share_group_type_unset_extra_specs(self): arglist = [ self.share_group_type.id, 'consistent_snapshot_support' ] verifylist = [ ('share_group_type', self.share_group_type.id), ('group_specs', ['consistent_snapshot_support']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.share_group_type.unset_keys.assert_called_with( ['consistent_snapshot_support']) self.assertIsNone(result) def test_share_group_type_unset_exception(self): arglist = [ self.share_group_type.id, 'snapshot_support' ] verifylist = [ ('share_group_type', self.share_group_type.id), ('group_specs', ['snapshot_support']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.share_group_type.unset_keys.side_effect = NotFound() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareGroupTypeList(TestShareGroupType): def setUp(self): super(TestShareGroupTypeList, self).setUp() self.share_group_types = ( manila_fakes.FakeShareGroupType.create_share_group_types()) self.sgt_mock.list.return_value = self.share_group_types # Get the command object to test self.cmd = osc_share_group_types.ListShareGroupType(self.app, None) self.values = (oscutils.get_dict_properties( s._info, COLUMNS) for s in self.share_group_types) def test_share_group_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.sgt_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_group_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.sgt_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_group_type_list_group_specs(self): arglist = [ '--group-specs', 'consistent_snapshot_support=true' ] verifylist = [ ('group_specs', ['consistent_snapshot_support=true']) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.sgt_mock.list.assert_called_once_with( search_opts={'group_specs': { 'consistent_snapshot_support': 'True'}}, show_all=False) self.assertEqual(COLUMNS, columns) self.assertEqual(list(self.values), list(data)) class TestShareGroupTypeShow(TestShareGroupType): def setUp(self): super(TestShareGroupTypeShow, self).setUp() self.share_types = ( manila_fakes.FakeShareType.create_share_types(count=2)) formatted_share_types = [] for st in self.share_types: formatted_share_types.append(st.name) self.share_group_type = ( manila_fakes.FakeShareGroupType.create_one_share_group_type( attrs={ 'share_types': formatted_share_types } )) self.share_group_type_formatted = ( manila_fakes.FakeShareGroupType.create_one_share_group_type( attrs={ 'id': self.share_group_type['id'], 'name': self.share_group_type['name'], 'share_types': formatted_share_types } )) formatted_sgt = utils.format_share_group_type( self.share_group_type_formatted) self.sgt_mock.get.return_value = self.share_group_type # Get the command object to test self.cmd = osc_share_group_types.ShowShareGroupType(self.app, None) self.data = tuple(formatted_sgt.values()) self.columns = tuple(formatted_sgt.keys()) def test_share_group_type_show(self): arglist = [ self.share_group_type.name ] verifylist = [ ("share_group_type", self.share_group_type.name) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.sgt_mock.get.assert_called_with(self.share_group_type) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_group_type_access.py0000664000175000017500000001533200000000000031736 0ustar00zuulzuul00000000000000# 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_group_type_access as osc_sgta from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShareGroupTypeAccess(manila_fakes.TestShare): def setUp(self): super(TestShareGroupTypeAccess, self).setUp() self.type_access_mock = ( self.app.client_manager.share.share_group_type_access) self.type_access_mock.reset_mock() self.share_group_types_mock = ( self.app.client_manager.share.share_group_types) self.share_group_types_mock.reset_mock() self.projects_mock = self.app.client_manager.identity.projects self.projects_mock.reset_mock() class TestShareGroupTypeAccessAllow(TestShareGroupTypeAccess): def setUp(self): super(TestShareGroupTypeAccessAllow, self).setUp() self.project = identity_fakes.FakeProject.create_one_project() self.share_group_type = ( manila_fakes.FakeShareGroupType.create_one_share_group_type( attrs={'is_public': False} ) ) self.share_group_types_mock.get.return_value = self.share_group_type self.projects_mock.get.return_value = self.project self.type_access_mock.add_project_access.return_value = None # Get the command object to test self.cmd = osc_sgta.ShareGroupTypeAccessAllow(self.app, None) def test_share_group_type_access_create(self): arglist = [ self.share_group_type.id, self.project.id ] verifylist = [ ('share_group_type', self.share_group_type.id), ('projects', [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_group_type, self.project.id) self.assertIsNone(result) def test_share_group_type_access_create_invalid_project_exception(self): arglist = [ self.share_group_type.id, 'invalid_project_format' ] verifylist = [ ('share_group_type', self.share_group_type.id), ('projects', ['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 TestShareGroupTypeAccessList(TestShareGroupTypeAccess): columns = ['Project ID'] data = (('',), ('',)) def setUp(self): super(TestShareGroupTypeAccessList, self).setUp() self.type_access_mock.list.return_value = ( self.columns, self.data) # Get the command object to test self.cmd = osc_sgta.ListShareGroupTypeAccess(self.app, None) def test_share_group_type_access_list(self): share_group_type = ( manila_fakes.FakeShareGroupType.create_one_share_group_type( attrs={'is_public': False} ) ) self.share_group_types_mock.get.return_value = share_group_type arglist = [ share_group_type.id, ] verifylist = [ ('share_group_type', share_group_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_group_type) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) def test_share_group_type_access_list_public_type(self): share_group_type = ( manila_fakes.FakeShareGroupType.create_one_share_group_type( attrs={'is_public': True} ) ) self.share_group_types_mock.get.return_value = share_group_type arglist = [ share_group_type.id, ] verifylist = [ ('share_group_type', share_group_type.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareGroupTypeAccessDeny(TestShareGroupTypeAccess): def setUp(self): super(TestShareGroupTypeAccessDeny, self).setUp() self.project = identity_fakes.FakeProject.create_one_project() self.share_group_type = ( manila_fakes.FakeShareGroupType.create_one_share_group_type( attrs={'is_public': False})) self.share_group_types_mock.get.return_value = self.share_group_type self.projects_mock.get.return_value = self.project self.type_access_mock.remove_project_access.return_value = None # Get the command object to test self.cmd = osc_sgta.ShareGroupTypeAccessDeny(self.app, None) def test_share_group_type_access_delete(self): arglist = [ self.share_group_type.id, self.project.id ] verifylist = [ ('share_group_type', self.share_group_type.id), ('projects', [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_group_type, self.project.id) self.assertIsNone(result) def test_share_group_type_access_delete_exception(self): arglist = [ self.share_group_type.id, 'invalid_project_format' ] verifylist = [ ('share_group_type', self.share_group_type.id), ('projects', ['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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_groups.py0000664000175000017500000006002200000000000027533 0ustar00zuulzuul00000000000000# 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 from unittest import mock import uuid from osc_lib import exceptions from osc_lib import exceptions as osc_exceptions from osc_lib import utils as oscutils from manilaclient.osc import utils from manilaclient import api_versions from manilaclient.osc.v2 import share_groups as osc_share_groups from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShareGroup(manila_fakes.TestShare): def setUp(self): super(TestShareGroup, self).setUp() self.groups_mock = self.app.client_manager.share.share_groups self.groups_mock.reset_mock() self.share_types_mock = self.app.client_manager.share.share_types self.share_types_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION) class TestShareGroupCreate(TestShareGroup): def setUp(self): super(TestShareGroupCreate, self).setUp() self.share_group = ( manila_fakes.FakeShareGroup.create_one_share_group() ) self.formatted_result = ( manila_fakes.FakeShareGroup.create_one_share_group( attrs={ "id": self.share_group.id, 'created_at': self.share_group.created_at, "project_id": self.share_group.project_id, 'share_group_type_id': ( self.share_group.share_group_type_id), 'share_types': '\n'.join(self.share_group.share_types) })) self.groups_mock.create.return_value = self.share_group self.groups_mock.get.return_value = self.share_group self.cmd = osc_share_groups.CreateShareGroup(self.app, None) self.data = tuple(self.formatted_result._info.values()) self.columns = tuple(self.share_group._info.keys()) def test_share_group_create_no_args(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.groups_mock.create.assert_called_with( name=None, description=None, share_types=[], share_group_type=None, share_network=None, source_share_group_snapshot=None, availability_zone=None ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_group_create_with_options(self): arglist = [ '--name', self.share_group.name, '--description', self.share_group.description ] verifylist = [ ('name', self.share_group.name), ('description', self.share_group.description) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.groups_mock.create.assert_called_with( name=self.share_group.name, description=self.share_group.description, share_types=[], share_group_type=None, share_network=None, source_share_group_snapshot=None, availability_zone=None ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_group_create_az(self): arglist = [ '--availability-zone', self.share_group.availability_zone ] verifylist = [ ('availability_zone', self.share_group.availability_zone) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.groups_mock.create.assert_called_with( name=None, description=None, share_types=[], share_group_type=None, share_network=None, source_share_group_snapshot=None, availability_zone=self.share_group.availability_zone ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_group_create_share_types(self): share_types = manila_fakes.FakeShareType.create_share_types(count=2) self.share_types_mock.get = manila_fakes.FakeShareType.get_share_types( share_types) arglist = [ '--share-types', share_types[0].id, share_types[1].id ] verifylist = [ ('share_types', [share_types[0].id, share_types[1].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.groups_mock.create.assert_called_with( name=None, description=None, share_types=share_types, share_group_type=None, share_network=None, source_share_group_snapshot=None, availability_zone=None ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_group_create_wait(self): arglist = [ '--wait' ] verifylist = [ ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.groups_mock.create.assert_called_with( name=None, description=None, share_types=[], share_group_type=None, share_network=None, source_share_group_snapshot=None, availability_zone=None ) self.groups_mock.get.assert_called_with(self.share_group.id) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) # TODO(archanaserver): Add test cases for share-group-type, # share-network and source-share-group-snapshot when the # options have OSC support. class TestShareGroupDelete(TestShareGroup): def setUp(self): super(TestShareGroupDelete, self).setUp() self.share_group = ( manila_fakes.FakeShareGroup.create_one_share_group()) self.groups_mock.get.return_value = self.share_group self.cmd = osc_share_groups.DeleteShareGroup(self.app, None) def test_share_group_delete(self): arglist = [ self.share_group.id ] verifylist = [ ('share_group', [self.share_group.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.groups_mock.delete.assert_called_with( self.share_group, force=False) self.assertIsNone(result) def test_share_group_delete_force(self): arglist = [ self.share_group.id, '--force' ] verifylist = [ ('share_group', [self.share_group.id]), ('force', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.groups_mock.delete.assert_called_with( self.share_group, force=True) self.assertIsNone(result) def test_share_group_delete_multiple(self): share_groups = ( manila_fakes.FakeShareGroup.create_share_groups( count=2)) arglist = [ share_groups[0].id, share_groups[1].id ] verifylist = [ ('share_group', [share_groups[0].id, share_groups[1].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertEqual(self.groups_mock.delete.call_count, len(share_groups)) self.assertIsNone(result) def test_share_group_delete_exception(self): arglist = [ self.share_group.id ] verifylist = [ ('share_group', [self.share_group.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.groups_mock.delete.side_effect = exceptions.CommandError() self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_group_delete_wait(self): arglist = [ self.share_group.id, '--wait' ] verifylist = [ ('share_group', [self.share_group.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=True): result = self.cmd.take_action(parsed_args) self.groups_mock.delete.assert_called_with( self.share_group, force=False) self.groups_mock.get.assert_called_with(self.share_group.id) self.assertIsNone(result) def test_share_group_delete_wait_exception(self): arglist = [ self.share_group.id, '--wait' ] verifylist = [ ('share_group', [self.share_group.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=False): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestShareGroupShow(TestShareGroup): def setUp(self): super(TestShareGroupShow, self).setUp() self.share_group = ( manila_fakes.FakeShareGroup.create_one_share_group() ) self.formatted_result = ( manila_fakes.FakeShareGroup.create_one_share_group( attrs={ "id": self.share_group.id, 'created_at': self.share_group.created_at, "project_id": self.share_group.project_id, 'share_group_type_id': ( self.share_group.share_group_type_id), 'share_types': '\n'.join(self.share_group.share_types) })) self.groups_mock.get.return_value = self.share_group self.data = tuple(self.formatted_result._info.values()) self.columns = tuple(self.share_group._info.keys()) self.cmd = osc_share_groups.ShowShareGroup(self.app, None) def test_share_group_show(self): arglist = [ self.share_group.id ] verifylist = [ ('share_group', self.share_group.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.groups_mock.get.assert_called_with( self.share_group.id ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestShareGroupSet(TestShareGroup): def setUp(self): super(TestShareGroupSet, self).setUp() self.share_group = ( manila_fakes.FakeShareGroup.create_one_share_group() ) self.share_group = manila_fakes.FakeShare.create_one_share( methods={"reset_state": None} ) self.groups_mock.get.return_value = self.share_group self.cmd = osc_share_groups.SetShareGroup(self.app, None) def test_set_share_group_name(self): new_name = uuid.uuid4().hex arglist = [ '--name', new_name, self.share_group.id, ] verifylist = [ ('name', new_name), ('share_group', self.share_group.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.groups_mock.update.assert_called_with( self.share_group.id, name=parsed_args.name) def test_set_share_group_description(self): new_description = uuid.uuid4().hex arglist = [ '--description', new_description, self.share_group.id, ] verifylist = [ ('description', new_description), ('share_group', self.share_group.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.groups_mock.update.assert_called_with( self.share_group.id, description=parsed_args.description) def test_share_group_set_status(self): new_status = 'available' arglist = [ self.share_group.id, '--status', new_status ] verifylist = [ ('share_group', self.share_group.id), ('status', new_status) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.share_group.reset_state.assert_called_with(new_status) self.assertIsNone(result) def test_share_group_set_status_exception(self): new_status = 'available' arglist = [ self.share_group.id, '--status', new_status ] verifylist = [ ('share_group', self.share_group.id), ('status', new_status) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.share_group.reset_state.side_effect = Exception() self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareGroupUnset(TestShareGroup): def setUp(self): super(TestShareGroupUnset, self).setUp() self.share_group = ( manila_fakes.FakeShareGroup.create_one_share_group() ) self.groups_mock.get.return_value = self.share_group self.cmd = osc_share_groups.UnsetShareGroup(self.app, None) def test_unset_share_group_name(self): arglist = [ self.share_group.id, '--name' ] verifylist = [ ('share_group', self.share_group.id), ('name', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.groups_mock.update.assert_called_with( self.share_group, name=None) self.assertIsNone(result) def test_unset_share_group_description(self): arglist = [ self.share_group.id, '--description' ] verifylist = [ ('share_group', self.share_group.id), ('description', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.groups_mock.update.assert_called_with( self.share_group, description=None) self.assertIsNone(result) def test_unset_share_group_name_exception(self): arglist = [ self.share_group.id, '--name', ] verifylist = [ ('share_group', self.share_group.id), ('name', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.groups_mock.update.side_effect = Exception() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareGroupList(TestShareGroup): columns = [ 'id', 'name', 'status', 'description' ] column_headers = utils.format_column_headers(columns) def setUp(self): super(TestShareGroupList, self).setUp() self.new_share_group = ( manila_fakes.FakeShareGroup.create_one_share_group() ) self.groups_mock.list.return_value = [self.new_share_group] self.share_group = ( manila_fakes.FakeShareGroup.create_one_share_group()) self.groups_mock.get.return_value = self.share_group self.share_groups_list = ( manila_fakes.FakeShareGroup.create_share_groups( count=2)) self.groups_mock.list.return_value = self.share_groups_list self.values = (oscutils.get_dict_properties( s._info, self.columns) for s in self.share_groups_list) self.cmd = osc_share_groups.ListShareGroup(self.app, None) def test_share_group_list(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.groups_mock.list.assert_called_with(search_opts={ 'all_tenants': False, 'name': None, 'status': None, 'share_server_id': None, 'share_group_type': None, 'snapshot': None, 'host': None, 'share_network': None, 'project_id': None, 'limit': None, 'offset': None, 'name~': None, 'description~': None, 'description': None }) self.assertEqual(self.column_headers, columns) self.assertEqual(list(self.values), list(data)) def test_list_share_group_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.35") arglist = [ '--description', 'Description' ] verifylist = [ ('description', 'Description') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_list_share_groups_all_projects(self): all_tenants_list = self.column_headers.copy() all_tenants_list.append('Project ID') list_values = (oscutils.get_dict_properties( s._info, all_tenants_list) for s in self.share_groups_list) arglist = [ '--all-projects' ] verifylist = [ ('all_projects', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.groups_mock.list.assert_called_with(search_opts={ 'all_tenants': True, 'name': None, 'status': None, 'share_server_id': None, 'share_group_type': None, 'snapshot': None, 'host': None, 'share_network': None, 'project_id': None, 'limit': None, 'offset': None, 'name~': None, 'description~': None, 'description': None }) self.assertEqual(all_tenants_list, columns) self.assertEqual(list(list_values), list(data)) def test_share_group_list_name(self): arglist = [ '--name', self.new_share_group.name ] verifylist = [ ('name', self.new_share_group.name) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) search_opts = { 'all_tenants': False, 'name': None, 'status': None, 'share_server_id': None, 'share_group_type': None, 'snapshot': None, 'host': None, 'share_network': None, 'project_id': None, 'limit': None, 'offset': None, 'name~': None, 'description~': None, 'description': None } search_opts['name'] = self.new_share_group.name self.groups_mock.list.assert_called_once_with( search_opts=search_opts, ) self.assertEqual(self.column_headers, columns) self.assertEqual(list(self.values), list(data)) def test_share_group_list_description(self): arglist = [ '--description', self.new_share_group.description ] verifylist = [ ('description', self.new_share_group.description) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) search_opts = { 'all_tenants': False, 'name': None, 'status': None, 'share_server_id': None, 'share_group_type': None, 'snapshot': None, 'host': None, 'share_network': None, 'project_id': None, 'limit': None, 'offset': None, 'name~': None, 'description~': None, 'description': None } search_opts['description'] = self.new_share_group.description self.groups_mock.list.assert_called_once_with( search_opts=search_opts, ) self.assertEqual(self.column_headers, columns) self.assertEqual(list(self.values), list(data)) def test_share_group_list_status(self): arglist = [ '--status', self.new_share_group.status, ] verifylist = [ ('status', self.new_share_group.status), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) search_opts = { 'all_tenants': False, 'name': None, 'status': None, 'share_server_id': None, 'share_group_type': None, 'snapshot': None, 'host': None, 'share_network': None, 'project_id': None, 'limit': None, 'offset': None, 'name~': None, 'description~': None, 'description': None } search_opts['status'] = self.new_share_group.status self.groups_mock.list.assert_called_once_with( search_opts=search_opts, ) self.assertEqual(self.column_headers, columns) self.assertEqual(list(self.values), list(data)) def test_share_group_list_marker_and_limit(self): arglist = [ "--marker", self.new_share_group.id, "--limit", "2", ] verifylist = [ ('marker', self.new_share_group.id), ('limit', 2), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) search_opts = { 'all_tenants': False, 'name': None, 'status': None, 'share_server_id': None, 'share_group_type': None, 'snapshot': None, 'host': None, 'share_network': None, 'project_id': None, 'limit': 2, 'offset': self.new_share_group.id, 'name~': None, 'description~': None, 'description': None } self.groups_mock.list.assert_called_once_with( search_opts=search_opts, ) self.assertEqual(self.column_headers, columns) self.assertEqual(list(self.values), list(data)) def test_share_group_list_negative_limit(self): arglist = [ "--limit", "-2", ] verifylist = [ ("limit", -2), ] self.assertRaises(argparse.ArgumentTypeError, self.check_parser, self.cmd, arglist, verifylist) # TODO(archanaserver): Add test cases for share-server-id, # share-group-type, snapshot, share-network and source- # share-group-share_group when the options have OSC support. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_instance_export_locations.py0000664000175000017500000001213000000000000033471 0ustar00zuulzuul00000000000000# Copyright 2021 NetApp, 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 osc_lib import utils as osc_lib_utils from manilaclient.osc.v2 \ import share_instance_export_locations \ as osc_share_instance_export_locations from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShareInstanceExportLocation(manila_fakes.TestShare): def setUp(self): super(TestShareInstanceExportLocation, self).setUp() self.instances_mock = self.app.client_manager.share.share_instances self.instances_mock.reset_mock() self.instance_export_locations_mock = ( self.app.client_manager.share.share_instance_export_locations ) self.instance_export_locations_mock.reset_mock() class TestShareInstanceExportLocationList(TestShareInstanceExportLocation): column_headers = [ 'ID', 'Path', 'Is Admin Only', 'Preferred', ] def setUp(self): super(TestShareInstanceExportLocationList, self).setUp() self.instance = ( manila_fakes.FakeShareInstance.create_one_share_instance() ) self.instances_mock.get.return_value = self.instance self.instance_export_locations = ( manila_fakes.FakeShareExportLocation. create_share_export_locations() ) self.instance_export_locations_mock.list.return_value = \ self.instance_export_locations self.data = (osc_lib_utils.get_dict_properties( i._info, self.column_headers) for i in self.instance_export_locations) self.cmd = ( osc_share_instance_export_locations. ShareInstanceListExportLocation(self.app, None) ) def test_share_instance_export_locations_list_missing_args(self): arglist = [] verifylist = [] self.assertRaises( osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_instance_export_locations_list(self): arglist = [ self.instance.id ] verifylist = [ ('instance', self.instance.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.instances_mock.get.assert_called_with( self.instance.id ) self.instance_export_locations_mock.list.assert_called_with( self.instance, search_opts=None ) self.assertCountEqual(self.column_headers, columns) self.assertCountEqual(self.data, data) class TestShareInstanceExportLocationShow(TestShareInstanceExportLocation): def setUp(self): super(TestShareInstanceExportLocationShow, self).setUp() self.share_instance_export_locations = ( manila_fakes.FakeShareExportLocation. create_one_export_location() ) self.instance_export_locations_mock.get.return_value = \ self.share_instance_export_locations self.instance = ( manila_fakes.FakeShareInstance.create_one_share_instance() ) self.instances_mock.get.return_value = self.instance self.cmd = ( osc_share_instance_export_locations. ShareInstanceShowExportLocation(self.app, None) ) self.data = tuple(self.share_instance_export_locations._info.values()) self.columns = tuple(self.share_instance_export_locations._info.keys()) def test_share_instance_export_locations_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises( osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_instance_export_locations_show(self): arglist = [ self.instance.id, self.share_instance_export_locations.id, ] verifylist = [ ('instance', self.instance.id), ('export_location', self.share_instance_export_locations.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.instances_mock.get.assert_called_with( self.instance.id ) self.instance_export_locations_mock.get.assert_called_with( self.instance.id, self.share_instance_export_locations.id ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_instances.py0000664000175000017500000003006300000000000030205 0ustar00zuulzuul00000000000000# 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 unittest import mock from osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient.common import cliutils from manilaclient.osc import utils from manilaclient.osc.v2 import share_instances as osc_share_instances from manilaclient import api_versions from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShareInstance(manila_fakes.TestShare): def setUp(self): super(TestShareInstance, self).setUp() self.shares_mock = self.app.client_manager.share.shares self.shares_mock.reset_mock() self.instances_mock = self.app.client_manager.share.share_instances self.instances_mock.reset_mock() self.share_instance_export_locations_mock = ( self.app.client_manager.share.share_instance_export_locations) self.share_instance_export_locations_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION) class TestShareInstanceList(TestShareInstance): columns = [ 'id', 'share_id', 'host', 'status', 'availability_zone', 'share_network_id', 'share_server_id', 'share_type_id', ] column_headers = utils.format_column_headers(columns) def setUp(self): super(TestShareInstanceList, self).setUp() self.instances_list = ( manila_fakes.FakeShareInstance.create_share_instances(count=2)) self.instances_mock.list.return_value = self.instances_list self.share = manila_fakes.FakeShare.create_one_share() self.shares_mock.get.return_value = self.share self.shares_mock.list_instances.return_value = self.instances_list self.shares_mock.list_instances.return_value = self.instances_list self.instance_values = (oscutils.get_dict_properties( instance._info, self.columns) for instance in self.instances_list) self.cmd = osc_share_instances.ShareInstanceList(self.app, None) def test_share_instance_list(self): argslist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, argslist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.assertIs(True, self.instances_mock.list.called) self.assertEqual(self.column_headers, columns) self.assertEqual(list(self.instance_values), list(data)) def test_share_instance_list_by_share(self): argslist = [ '--share', self.share['id'] ] verifylist = [ ('share', self.share.id) ] parsed_args = self.check_parser(self.cmd, argslist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.shares_mock.list_instances.assert_called_with(self.share) self.assertEqual(self.column_headers, columns) self.assertEqual(list(self.instance_values), list(data)) def test_share_instance_list_by_export_location(self): fake_export_location = '10.1.1.0:/fake_share_el' argslist = [ '--export-location', fake_export_location ] verifylist = [ ('export_location', fake_export_location) ] parsed_args = self.check_parser(self.cmd, argslist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.instances_mock.list.assert_called_with( export_location=fake_export_location) self.assertEqual(self.column_headers, columns) self.assertEqual(list(self.instance_values), list(data)) def test_share_instance_list_by_export_location_invalid_version(self): fake_export_location = '10.1.1.0:/fake_share_el' argslist = [ '--export-location', fake_export_location ] verifylist = [ ('export_location', fake_export_location) ] self.app.client_manager.share.api_version = api_versions.APIVersion( '2.34') parsed_args = self.check_parser(self.cmd, argslist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareInstanceDelete(TestShareInstance): def setUp(self): super(TestShareInstanceDelete, self).setUp() self.share_instance = ( manila_fakes.FakeShareInstance.create_one_share_instance()) self.instances_mock.get.return_value = self.share_instance self.cmd = osc_share_instances.ShareInstanceDelete(self.app, None) def test_share_instance_delete_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_instance_delete(self): arglist = [ self.share_instance.id ] verifylist = [ ('instance', [self.share_instance.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.instances_mock.force_delete.assert_called_with( self.share_instance) self.assertIsNone(result) def test_share_instance_delete_multiple(self): share_instances = ( manila_fakes.FakeShareInstance.create_share_instances(count=2)) instance_ids = [instance.id for instance in share_instances] arglist = instance_ids verifylist = [('instance', instance_ids)] self.instances_mock.get.side_effect = share_instances delete_calls = [ mock.call(instance) for instance in share_instances] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.instances_mock.force_delete.assert_has_calls(delete_calls) self.assertEqual(self.instances_mock.force_delete.call_count, len(share_instances)) self.assertIsNone(result) def test_share_instance_delete_exception(self): arglist = [ self.share_instance.id ] verifylist = [ ('instance', [self.share_instance.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.instances_mock.force_delete.side_effect = ( exceptions.CommandError()) self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_instance_delete_wait(self): arglist = [ self.share_instance.id, '--wait' ] verifylist = [ ('instance', [self.share_instance.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=True): result = self.cmd.take_action(parsed_args) self.instances_mock.force_delete.assert_called_with( self.share_instance) self.instances_mock.get.assert_called_with(self.share_instance.id) self.assertIsNone(result) def test_share_instance_delete_wait_exception(self): arglist = [ self.share_instance.id, '--wait' ] verifylist = [ ('instance', [self.share_instance.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=False): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestShareInstanceShow(TestShareInstance): def setUp(self): super(TestShareInstanceShow, self).setUp() self.share_instance = ( manila_fakes.FakeShareInstance.create_one_share_instance() ) self.instances_mock.get.return_value = self.share_instance self.export_locations = ( [manila_fakes.FakeShareExportLocation.create_one_export_location() for i in range(2)]) self.share_instance_export_locations_mock.list.return_value = ( self.export_locations) self.cmd = osc_share_instances.ShareInstanceShow(self.app, None) self.data = tuple(self.share_instance._info.values()) self.columns = tuple(self.share_instance._info.keys()) def test_share_instance_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises( osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_instance_show(self): expected_columns = tuple(self.share_instance._info.keys()) expected_data_dic = tuple() for column in expected_columns: expected_data_dic += (self.share_instance._info[column],) expected_columns += ('export_locations',) expected_data_dic += (dict(self.export_locations[0]),) cliutils.convert_dict_list_to_string = mock.Mock() cliutils.convert_dict_list_to_string.return_value = dict( self.export_locations[0]) arglist = [ self.share_instance.id ] verifylist = [ ('instance', self.share_instance.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.instances_mock.get.assert_called_with( self.share_instance.id ) self.assertCountEqual(expected_columns, columns) self.assertCountEqual(expected_data_dic, data) class TestShareInstanceSet(TestShareInstance): def setUp(self): super(TestShareInstanceSet, self).setUp() self.share_instance = ( manila_fakes.FakeShareInstance.create_one_share_instance()) self.instances_mock.get.return_value = self.share_instance self.cmd = osc_share_instances.ShareInstanceSet(self.app, None) def test_share_instance_set_status(self): new_status = 'available' arglist = [ self.share_instance.id, '--status', new_status ] verifylist = [ ('instance', self.share_instance.id), ('status', new_status) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.instances_mock.reset_state.assert_called_with( self.share_instance, new_status) self.assertIsNone(result) def test_share_instance_set_status_exception(self): new_status = 'available' arglist = [ self.share_instance.id, '--status', new_status ] verifylist = [ ('instance', self.share_instance.id), ('status', new_status) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.instances_mock.reset_state.side_effect = Exception() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_instance_set_nothing_defined(self): arglist = [ self.share_instance.id, ] verifylist = [ ('instance', self.share_instance.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_limits.py0000664000175000017500000000574400000000000027527 0ustar00zuulzuul00000000000000# Copyright 2021 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 ddt from manilaclient import api_versions from manilaclient.api_versions import MAX_VERSION from manilaclient.osc.v2 import share_limits as osc_share_limits from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShareLimits(manila_fakes.TestShare): def setUp(self): super(TestShareLimits, self).setUp() self.share_limits_mock = self.app.client_manager.share.limits self.share_limits_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( MAX_VERSION) @ddt.ddt class TestShareLimitsShow(TestShareLimits): def setUp(self): super(TestShareLimitsShow, self).setUp() # Get the command object to test self.cmd = osc_share_limits.ShareLimitsShow(self.app, None) self.absolute_limit_columns = ["Name", "Value"] self.rate_limit_columns = [ "Verb", "Regex", "URI", "Value", "Remaining", "Unit", "Next Available", ] @ddt.data('absolute', 'rate') def test_limits(self, limit_type): share_limits = ( manila_fakes.FakeShareLimits.create_one_share_limit() ) self.share_limits_mock.get.return_value = share_limits expected_data = share_limits._info[f"{limit_type}_limit"] arglist = [] verifylist = [] if limit_type == 'absolute': arglist.append('--absolute') verifylist.extend([ ('absolute', True), ('rate', False) ]) else: arglist.append('--rate') verifylist.extend([ ('absolute', False), ('rate', True) ]) parsed_args = self.check_parser(self.cmd, arglist, verifylist) actual_columns, actual_data = self.cmd.take_action(parsed_args) self.assertEqual(getattr(self, f"{limit_type}_limit_columns"), actual_columns) if limit_type == 'rate': expected_data_tuple = tuple(expected_data.values()) self.assertEqual(sorted(expected_data_tuple), sorted(next(actual_data))) else: expected_data_tuple = tuple(expected_data.items()) self.assertEqual(sorted(expected_data_tuple), sorted(actual_data)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_network_subnets.py0000664000175000017500000003774100000000000031464 0ustar00zuulzuul00000000000000# 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 unittest import mock import uuid import ddt from osc_lib import exceptions from manilaclient import api_versions from manilaclient.osc.v2 import share_network_subnets as osc_share_subnets from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShareNetworkSubnet(manila_fakes.TestShare): def setUp(self): super(TestShareNetworkSubnet, self).setUp() self.share_networks_mock = self.app.client_manager.share.share_networks self.share_networks_mock.reset_mock() self.share_subnets_mock = ( self.app.client_manager.share.share_network_subnets) self.share_subnets_mock.reset_mock() @ddt.ddt class TestShareNetworkSubnetCreate(TestShareNetworkSubnet): def setUp(self): super(TestShareNetworkSubnetCreate, self).setUp() self.share_network = ( manila_fakes.FakeShareNetwork.create_one_share_network()) self.share_networks_mock.get.return_value = self.share_network self.share_network_subnet = ( manila_fakes.FakeShareNetworkSubnet.create_one_share_subnet()) self.share_subnets_mock.create.return_value = self.share_network_subnet self.cmd = osc_share_subnets.CreateShareNetworkSubnet( self.app, None) self.data = self.share_network_subnet._info.values() self.columns = self.share_network_subnet._info.keys() def test_share_network_subnet_create_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_network_subnet_create(self): fake_neutron_net_id = str(uuid.uuid4()) fake_neutron_subnet_id = str(uuid.uuid4()) arglist = [ self.share_network.id, '--neutron-net-id', fake_neutron_net_id, '--neutron-subnet-id', fake_neutron_subnet_id, '--availability-zone', 'nova', ] verifylist = [ ('share_network', self.share_network.id), ('neutron_net_id', fake_neutron_net_id), ('neutron_subnet_id', fake_neutron_subnet_id), ('availability_zone', 'nova'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.share_subnets_mock.create.assert_called_once_with( neutron_net_id=fake_neutron_net_id, neutron_subnet_id=fake_neutron_subnet_id, availability_zone='nova', share_network_id=self.share_network.id, metadata={} ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_network_subnet_create_arg_group_exception(self): fake_neutron_net_id = str(uuid.uuid4()) arglist = [ self.share_network.id, '--neutron-net-id', fake_neutron_net_id ] verifylist = [ ('share_network', self.share_network.id), ('neutron_net_id', fake_neutron_net_id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) @ddt.data({'check_only': False, 'restart_check': True}, {'check_only': True, 'restart_check': True}, {'check_only': True, 'restart_check': False}) @ddt.unpack def test_share_network_subnet_create_check_api_version_exception( self, check_only, restart_check): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.69' ) arglist = [ self.share_network.id, ] verifylist = [ ('share_network', self.share_network.id), ] if check_only: arglist.append('--check-only') verifylist.append(('check_only', True)) if restart_check: arglist.append('--restart-check') verifylist.append(('restart_check', True)) parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) @ddt.data(True, False) def test_share_network_subnet_create_check(self, restart_check): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.70' ) self.share_networks_mock.share_network_subnet_create_check = ( mock.Mock(return_value=(200, {'compatible': True}))) arglist = [ self.share_network.id, '--check-only' ] verifylist = [ ('share_network', self.share_network.id), ('check_only', True), ] if restart_check: arglist.append('--restart-check') verifylist.append(('restart_check', True)) parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) (self.share_networks_mock.share_network_subnet_create_check .assert_called_once_with( share_network_id=self.share_network.id, neutron_net_id=None, neutron_subnet_id=None, availability_zone=None, reset_operation=restart_check)) def test_share_network_subnet_create_metadata(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.78' ) arglist = [ self.share_network.id, '--property', 'Manila=zorilla', '--property', 'Zorilla=manila' ] verifylist = [ ('share_network', self.share_network.id), ('property', {'Manila': 'zorilla', 'Zorilla': 'manila'}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.share_subnets_mock.create.assert_called_once_with( neutron_net_id=None, neutron_subnet_id=None, availability_zone=None, share_network_id=self.share_network.id, metadata={'Manila': 'zorilla', 'Zorilla': 'manila'}, ) self.assertEqual(set(self.columns), set(columns)) self.assertCountEqual(self.data, data) def test_share_network_subnet_create_metadata_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.77' ) arglist = [ self.share_network.id, '--property', 'Manila=zorilla', ] verifylist = [ ('share_network', self.share_network.id), ('property', {'Manila': 'zorilla'}) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareNetworkSubnetDelete(TestShareNetworkSubnet): def setUp(self): super(TestShareNetworkSubnetDelete, self).setUp() self.share_network = ( manila_fakes.FakeShareNetwork.create_one_share_network()) self.share_networks_mock.get.return_value = self.share_network self.share_network_subnets = ( manila_fakes.FakeShareNetworkSubnet.create_share_network_subnets()) self.cmd = osc_share_subnets.DeleteShareNetworkSubnet( self.app, None) def test_share_network_subnet_delete_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_network_subnets_delete(self): arglist = [ self.share_network.id, self.share_network_subnets[0].id, self.share_network_subnets[1].id, ] verifylist = [ ('share_network', self.share_network.id), ('share_network_subnet', [self.share_network_subnets[0].id, self.share_network_subnets[1].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertEqual(self.share_subnets_mock.delete.call_count, len(self.share_network_subnets)) self.assertIsNone(result) def test_share_network_subnet_delete_exception(self): arglist = [ self.share_network.id, self.share_network_subnets[0].id, ] verifylist = [ ('share_network', self.share_network.id), ('share_network_subnet', [self.share_network_subnets[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.share_subnets_mock.delete.side_effect = exceptions.CommandError() self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareNetworkSubnetShow(TestShareNetworkSubnet): def setUp(self): super(TestShareNetworkSubnetShow, self).setUp() self.share_network = ( manila_fakes.FakeShareNetwork.create_one_share_network()) self.share_networks_mock.get.return_value = self.share_network self.share_network_subnet = ( manila_fakes.FakeShareNetworkSubnet.create_one_share_subnet()) self.share_subnets_mock.get.return_value = self.share_network_subnet self.cmd = osc_share_subnets.ShowShareNetworkSubnet( self.app, None) self.data = self.share_network_subnet._info.values() self.columns = self.share_network_subnet._info.keys() def test_share_network_subnet_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_network_subnet_show(self): arglist = [ self.share_network.id, self.share_network_subnet.id, ] verifylist = [ ('share_network', self.share_network.id), ('share_network_subnet', self.share_network_subnet.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.share_subnets_mock.get.assert_called_once_with( self.share_network.id, self.share_network_subnet.id ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestShareNetworkSubnetSet(TestShareNetworkSubnet): def setUp(self): super(TestShareNetworkSubnetSet, self).setUp() self.share_network = ( manila_fakes.FakeShareNetwork.create_one_share_network()) self.share_networks_mock.get.return_value = self.share_network self.share_network_subnet = ( manila_fakes.FakeShareNetworkSubnet.create_one_share_subnet()) self.cmd = osc_share_subnets.SetShareNetworkSubnet( self.app, None) def test_set_share_network_subnet_property(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.78' ) arglist = [ self.share_network.id, self.share_network_subnet.id, '--property', 'Zorilla=manila', '--property', 'test=my_test', ] verifylist = [ ('share_network', self.share_network.id), ('share_network_subnet', self.share_network_subnet.id), ('property', {'Zorilla': 'manila', 'test': 'my_test'}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_subnets_mock.set_metadata.assert_called_once_with( self.share_network.id, {'Zorilla': 'manila', 'test': 'my_test'}, subresource=self.share_network_subnet.id) def test_set_share_network_subnet_property_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.78' ) arglist = [ self.share_network.id, self.share_network_subnet.id, '--property', 'key=1', ] verifylist = [ ('share_network', self.share_network.id), ('share_network_subnet', self.share_network_subnet.id), ('property', {'key': '1'}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_subnets_mock.set_metadata.assert_called_once_with( self.share_network.id, {'key': '1'}, subresource=self.share_network_subnet.id) self.share_subnets_mock.set_metadata.side_effect = ( exceptions.BadRequest) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareNetworkSubnetUnset(TestShareNetworkSubnet): def setUp(self): super(TestShareNetworkSubnetUnset, self).setUp() self.share_network = ( manila_fakes.FakeShareNetwork.create_one_share_network()) self.share_networks_mock.get.return_value = self.share_network self.share_network_subnet = ( manila_fakes.FakeShareNetworkSubnet.create_one_share_subnet()) self.cmd = osc_share_subnets.UnsetShareNetworkSubnet( self.app, None) def test_unset_share_network_subnet_property(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.78' ) arglist = [ self.share_network.id, self.share_network_subnet.id, '--property', 'Manila', ] verifylist = [ ('share_network', self.share_network.id), ('share_network_subnet', self.share_network_subnet.id), ('property', ['Manila']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_subnets_mock.delete_metadata.assert_called_once_with( self.share_network.id, ['Manila'], subresource=self.share_network_subnet.id) def test_unset_share_network_subnet_property_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.78' ) arglist = [ self.share_network.id, self.share_network_subnet.id, '--property', 'Manila', '--property', 'test', ] verifylist = [ ('share_network', self.share_network.id), ('share_network_subnet', self.share_network_subnet.id), ('property', ['Manila', 'test']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_subnets_mock.delete_metadata.assert_has_calls([ mock.call(self.share_network.id, ['Manila'], subresource=self.share_network_subnet.id), mock.call(self.share_network.id, ['test'], subresource=self.share_network_subnet.id)]) # 404 Not Found would be raised, if property 'Manila' doesn't exist. self.share_subnets_mock.delete_metadata.side_effect = ( exceptions.NotFound) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_networks.py0000664000175000017500000006544200000000000030103 0ustar00zuulzuul00000000000000# Copyright 2021 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. # from unittest import mock import uuid import ddt from osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.osc.v2 import share_networks as osc_share_networks from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes COLUMNS = ['ID', 'Name'] COLUMNS_DETAIL = [ 'ID', 'Name', 'Status', 'Created At', 'Updated At', 'Description', ] class TestShareNetwork(manila_fakes.TestShare): def setUp(self): super(TestShareNetwork, self).setUp() self.share_networks_mock = self.app.client_manager.share.share_networks self.share_networks_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION) @ddt.ddt class TestShareNetworkCreate(TestShareNetwork): def setUp(self): super(TestShareNetworkCreate, self).setUp() self.share_network = ( manila_fakes.FakeShareNetwork.create_one_share_network()) self.share_networks_mock.create.return_value = self.share_network self.cmd = osc_share_networks.CreateShareNetwork(self.app, None) self.data = self.share_network._info.values() self.columns = self.share_network._info.keys() @ddt.data('table', 'yaml') def test_share_network_create_formatter(self, formatter): arglist = [ '-f', formatter, ] verifylist = [ ('formatter', formatter), ] expected_data = self.share_network._info parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.share_networks_mock.create.assert_called_once_with( name=None, description=None, neutron_net_id=None, neutron_subnet_id=None, availability_zone=None) self.assertCountEqual(self.columns, columns) self.assertCountEqual(expected_data.values(), data) def test_share_network_create_with_args(self): fake_neutron_net_id = str(uuid.uuid4()) fake_neutron_subnet_id = str(uuid.uuid4()) fake_az = mock.Mock() fake_az.name = 'nova' fake_az.id = str(uuid.uuid4()) arglist = [ '--name', 'zorilla-net', '--description', 'fastest-backdoor-network-ever', '--neutron-net-id', fake_neutron_net_id, '--neutron-subnet-id', fake_neutron_subnet_id, '--availability-zone', 'nova', ] verifylist = [ ('name', 'zorilla-net'), ('description', 'fastest-backdoor-network-ever'), ('neutron_net_id', fake_neutron_net_id), ('neutron_subnet_id', fake_neutron_subnet_id), ('availability_zone', 'nova'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.find_resource', return_value=fake_az): columns, data = self.cmd.take_action(parsed_args) self.share_networks_mock.create.assert_called_once_with( name='zorilla-net', description='fastest-backdoor-network-ever', neutron_net_id=fake_neutron_net_id, neutron_subnet_id=fake_neutron_subnet_id, availability_zone='nova', ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) @ddt.ddt class TestShareNetworkDelete(TestShareNetwork): def setUp(self): super(TestShareNetworkDelete, self).setUp() self.share_network = ( manila_fakes.FakeShareNetwork.create_one_share_network()) self.share_networks_mock.get.return_value = self.share_network self.cmd = osc_share_networks.DeleteShareNetwork(self.app, None) def test_share_network_delete_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) @ddt.data(True, False) def test_share_network_delete_with_wait(self, wait): oscutils.wait_for_delete = mock.Mock(return_value=True) share_networks = ( manila_fakes.FakeShareNetwork.create_share_networks( count=2)) arglist = [ share_networks[0].id, share_networks[1].name, ] if wait: arglist.append('--wait') verifylist = [ ('share_network', [share_networks[0].id, share_networks[1].name]) ] if wait: verifylist.append(('wait', True)) parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.find_resource', side_effect=share_networks): result = self.cmd.take_action(parsed_args) self.assertEqual(self.share_networks_mock.delete.call_count, len(share_networks)) if wait: oscutils.wait_for_delete.assert_has_calls([ mock.call(manager=self.share_networks_mock, res_id=share_networks[0].id), mock.call(manager=self.share_networks_mock, res_id=share_networks[1].id) ]) self.assertIsNone(result) def test_share_network_delete_exception(self): arglist = [ self.share_network.id, ] verifylist = [ ('share_network', [self.share_network.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.share_networks_mock.delete.side_effect = exceptions.CommandError() self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_network_delete_wait_fails(self): oscutils.wait_for_delete = mock.Mock(return_value=False) arglist = [ self.share_network.id, '--wait', ] verifylist = [ ('share_network', [self.share_network.id]), ('wait', True), ] with mock.patch('osc_lib.utils.find_resource', return_value=self.share_network): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) self.share_networks_mock.delete.assert_called_once_with( self.share_network) @ddt.ddt class TestShareNetworkShow(TestShareNetwork): def setUp(self): super(TestShareNetworkShow, self).setUp() self.share_network = ( manila_fakes.FakeShareNetwork.create_one_share_network()) self.share_networks_mock.get.return_value = self.share_network self.security_services_mock = ( self.app.client_manager.share.security_services) self.cmd = osc_share_networks.ShowShareNetwork(self.app, None) self.data = self.share_network._info.values() self.columns = self.share_network._info.keys() def test_share_network_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) @ddt.data('name', 'id') def test_share_network_show_by(self, attr): network_to_show = getattr(self.share_network, attr) fake_security_service = mock.Mock() fake_security_service.id = str(uuid.uuid4()) fake_security_service.name = 'security-service-%s' % uuid.uuid4().hex self.security_services_mock.list = mock.Mock( return_value=[fake_security_service]) arglist = [ network_to_show, ] verifylist = [ ('share_network', network_to_show), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.find_resource', return_value=self.share_network) as find_resource: columns, data = self.cmd.take_action(parsed_args) find_resource.assert_called_once_with( self.share_networks_mock, network_to_show) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) @ddt.ddt class TestShareNetworkList(TestShareNetwork): def setUp(self): super(TestShareNetworkList, self).setUp() self.share_networks = ( manila_fakes.FakeShareNetwork.create_share_networks( count=2)) self.share_networks_list = oscutils.sort_items(self.share_networks, 'name:asc', str) self.share_networks_mock.list.return_value = self.share_networks_list self.values = (oscutils.get_dict_properties( s._info, COLUMNS) for s in self.share_networks_list) self.expected_search_opts = { 'all_tenants': False, 'project_id': None, 'name': None, 'created_since': None, 'created_before': None, 'neutron_net_id': None, 'neutron_subnet_id': None, 'network_type': None, 'segmentation_id': None, 'cidr': None, 'ip_version': None, 'security_service': None, 'name~': None, 'description~': None, 'description': None, } self.cmd = osc_share_networks.ListShareNetwork(self.app, None) @ddt.data(True, False) def test_list_share_networks_with_search_opts(self, with_search_opts): if with_search_opts: arglist = [ '--name', 'foo', '--ip-version', '4', '--description~', 'foo-share-network', ] verifylist = [ ('name', 'foo'), ('ip_version', '4'), ('description~', 'foo-share-network'), ] self.expected_search_opts.update({ 'name': 'foo', 'ip_version': '4', 'description~': 'foo-share-network', }) else: arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.share_networks_mock.list.assert_called_once_with( search_opts=self.expected_search_opts) self.assertEqual(COLUMNS, columns) self.assertEqual(list(self.values), list(data)) def test_list_share_networks_all_projects(self): all_tenants_list = COLUMNS.copy() all_tenants_list.append('Project ID') self.expected_search_opts.update({'all_tenants': True}) list_values = (oscutils.get_dict_properties( s._info, all_tenants_list) for s in self.share_networks_list) arglist = [ '--all-projects', ] verifylist = [ ('all_projects', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.share_networks_mock.list.assert_called_once_with( search_opts=self.expected_search_opts) self.assertEqual(all_tenants_list, columns) self.assertEqual(list(list_values), list(data)) def test_list_share_networks_detail(self): values = (oscutils.get_dict_properties( s._info, COLUMNS_DETAIL) for s in self.share_networks_list) arglist = [ '--detail', ] verifylist = [ ('detail', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.share_networks_mock.list.assert_called_once_with( search_opts=self.expected_search_opts) self.assertEqual(COLUMNS_DETAIL, columns) self.assertEqual(list(values), list(data)) def test_list_share_networks_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.35") arglist = [ '--description', 'Description', ] verifylist = [ ('description', 'Description'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) @ddt.ddt class TestShareNetworkUnset(TestShareNetwork): def setUp(self): super(TestShareNetworkUnset, self).setUp() self.share_network = ( manila_fakes.FakeShareNetwork.create_one_share_network()) self.share_networks_mock.get.return_value = self.share_network self.cmd = osc_share_networks.UnsetShareNetwork(self.app, None) def test_unset_share_network_name(self): arglist = [ self.share_network.id, '--name', ] verifylist = [ ('share_network', self.share_network.id), ('name', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.find_resource', return_value=self.share_network): result = self.cmd.take_action(parsed_args) self.share_networks_mock.update.assert_called_once_with( self.share_network, name='') self.assertIsNone(result) def test_unset_share_network_description(self): arglist = [ self.share_network.id, '--description', ] verifylist = [ ('share_network', self.share_network.id), ('description', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.share_networks_mock.update.assert_called_once_with( self.share_network, description='') self.assertIsNone(result) @ddt.data('name', 'security_service') def test_unset_share_network_exception_while_updating(self, attr): arglist = [ self.share_network.id, '--name', '--security-service', 'fake-security-service', ] verifylist = [ ('share_network', self.share_network.id), ('name', True), ('security_service', 'fake-security-service'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) if attr == 'name': self.share_networks_mock.update.side_effect = Exception() else: self.share_networks_mock.remove_security_service.side_effect = ( Exception()) with mock.patch('osc_lib.utils.find_resource', side_effect=[self.share_network, 'fake-security-service']): self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) self.share_networks_mock.update.assert_called_once_with( self.share_network, name='') if attr == 'security_service': self.share_networks_mock.remove_security_service\ .assert_called_once_with(self.share_network, 'fake-security-service') def test_unset_share_network_security_service(self): arglist = [ self.share_network.id, '--security-service', 'fake-security-service', ] verifylist = [ ('share_network', self.share_network.id), ('security_service', 'fake-security-service'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.find_resource', side_effect=[self.share_network, 'fake-security-service']): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) self.share_networks_mock.remove_security_service\ .assert_called_once_with(self.share_network, 'fake-security-service') @ddt.ddt class TestShareNetworkSet(TestShareNetwork): def setUp(self): super(TestShareNetworkSet, self).setUp() self.share_network = ( manila_fakes.FakeShareNetwork.create_one_share_network()) self.share_networks_mock.get.return_value = self.share_network self.cmd = osc_share_networks.SetShareNetwork(self.app, None) @ddt.data({'status': 'error', 'current_security_service': str(uuid.uuid4()), 'check_only': True, 'restart_check': True}, {'status': None, 'current_security_service': str(uuid.uuid4()), 'check_only': True, 'restart_check': None}, {'status': None, 'current_security_service': str(uuid.uuid4()), 'check_only': True, 'restart_check': True}, ) @ddt.unpack def test_set_share_network_api_version_exception(self, status, current_security_service, check_only, restart_check): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.62") arglist = [self.share_network.id] verifylist = [('share_network', self.share_network.id)] if status: arglist.extend(['--status', status]) verifylist.append(('status', status)) if current_security_service: arglist.extend(['--current-security-service', current_security_service]) verifylist.append(('current_security_service', current_security_service)) if check_only and restart_check: arglist.extend(['--check-only', '--restart-check']) verifylist.extend([('check_only', True), ('restart_check', True)]) parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_set_network_properties(self): new_name = 'share-network-name-' + uuid.uuid4().hex new_description = 'share-network-description-' + uuid.uuid4().hex new_neutron_subnet_id = str(uuid.uuid4()) arglist = [ self.share_network.id, '--name', new_name, '--description', new_description, '--neutron-subnet-id', new_neutron_subnet_id, ] verifylist = [ ('share_network', self.share_network.id), ('name', new_name), ('description', new_description), ('neutron_subnet_id', new_neutron_subnet_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.find_resource', return_value=self.share_network): result = self.cmd.take_action(parsed_args) self.share_networks_mock.update.assert_called_once_with( self.share_network, name=parsed_args.name, description=new_description, neutron_subnet_id=new_neutron_subnet_id, ) self.assertIsNone(result) def test_set_share_network_status(self): arglist = [ self.share_network.id, '--status', 'error' ] verifylist = [ ('share_network', self.share_network.id), ('status', 'error') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.find_resource', return_value=self.share_network): result = self.cmd.take_action(parsed_args) self.share_networks_mock.reset_state.assert_called_once_with( self.share_network, parsed_args.status) self.assertIsNone(result) def test_set_network_update_exception(self): share_network_name = 'share-network-name-' + uuid.uuid4().hex arglist = [ self.share_network.id, '--name', share_network_name ] verifylist = [ ('share_network', self.share_network.id), ('name', share_network_name) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.share_networks_mock.update.side_effect = Exception() with mock.patch('osc_lib.utils.find_resource', return_value=self.share_network): self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) self.share_networks_mock.update.assert_called_once_with( self.share_network, name=parsed_args.name) def test_set_share_network_status_exception(self): arglist = [ self.share_network.id, '--status', 'error' ] verifylist = [ ('share_network', self.share_network.id), ('status', 'error') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.share_networks_mock.reset_state.side_effect = Exception() with mock.patch('osc_lib.utils.find_resource', return_value=self.share_network): self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) self.share_networks_mock.reset_state.assert_called_once_with( self.share_network, parsed_args.status) @ddt.data({'check_only': False, 'restart_check': False}, {'check_only': True, 'restart_check': True}, {'check_only': True, 'restart_check': False}) @ddt.unpack def test_set_share_network_add_new_security_service_check_reset( self, check_only, restart_check): self.share_networks_mock .add_security_service_check = mock.Mock( return_value=(200, {'compatible': True})) arglist = [ self.share_network.id, '--new-security-service', 'new-security-service-name', ] verifylist = [ ('share_network', self.share_network.id), ('new_security_service', 'new-security-service-name'), ] if check_only: arglist.append('--check-only') verifylist.append(('check_only', True)) if restart_check: arglist.append('--restart-check') verifylist.append(('restart_check', True)) parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.find_resource', side_effect=[self.share_network, 'new-security-service']): result = self.cmd.take_action(parsed_args) if check_only: self.share_networks_mock.add_security_service_check\ .assert_called_once_with(self.share_network, 'new-security-service', reset_operation=restart_check) self.share_networks_mock.add_security_service.assert_not_called() else: self.share_networks_mock.add_security_service_check\ .assert_not_called() self.share_networks_mock.add_security_service\ .assert_called_once_with(self.share_network, 'new-security-service') self.assertIsNone(result) @ddt.data({'check_only': False, 'restart_check': False}, {'check_only': True, 'restart_check': True}, {'check_only': True, 'restart_check': False}) @ddt.unpack def test_set_share_network_update_security_service_check_reset( self, check_only, restart_check): self.share_networks_mock\ .update_share_network_security_service_check = mock.Mock( return_value=(200, {'compatible': True})) arglist = [ self.share_network.id, '--new-security-service', 'new-security-service-name', '--current-security-service', 'current-security-service-name' ] verifylist = [ ('share_network', self.share_network.id), ('new_security_service', 'new-security-service-name'), ('current_security_service', 'current-security-service-name'), ] if check_only: arglist.append('--check-only') verifylist.append(('check_only', True)) if restart_check: arglist.append('--restart-check') verifylist.append(('restart_check', True)) parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.find_resource', side_effect=[self.share_network, 'new-security-service', 'current-security-service']): result = self.cmd.take_action(parsed_args) if check_only: self.share_networks_mock\ .update_share_network_security_service_check\ .assert_called_once_with(self.share_network, 'current-security-service', 'new-security-service', reset_operation=restart_check) self.share_networks_mock.update_share_network_security_service\ .assert_not_called() else: self.share_networks_mock\ .update_share_network_security_service_check\ .assert_not_called() self.share_networks_mock.update_share_network_security_service\ .assert_called_once_with(self.share_network, 'current-security-service', 'new-security-service') self.assertIsNone(result) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_pools.py0000664000175000017500000001242300000000000027352 0ustar00zuulzuul00000000000000# 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 osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.osc.v2 import share_pools from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestPool(manila_fakes.TestShare): def setUp(self): super(TestPool, self).setUp() self.pools_mock = self.app.client_manager.share.pools self.pools_mock.reset_mock() self.share_types_mock = self.app.client_manager.share.share_types self.share_types_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION) class TestPoolList(TestPool): columns = ['Name', 'Host', 'Backend', 'Pool'] def setUp(self): super(TestPoolList, self).setUp() self.share_type = manila_fakes.FakeShareType.create_one_sharetype() self.share_types_mock.get.return_value = self.share_type self.share_pools = manila_fakes.FakeSharePools.create_share_pools() self.pools_mock.list.return_value = self.share_pools self.values = (oscutils.get_dict_properties( pool._info, self.columns) for pool in self.share_pools) self.cmd = share_pools.ListSharePools(self.app, None) def test_list_share_pools(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.pools_mock.list.assert_called_with( detailed=False, search_opts={ 'host': None, 'backend': None, 'pool': None, 'share_type': None, }) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) def test_list_share_pools_filters(self): arglist = [ '--host', self.share_pools[0].host, '--backend', self.share_pools[0].backend, '--pool', self.share_pools[0].pool ] verifylist = [ ('host', self.share_pools[0].host), ('backend', self.share_pools[0].backend), ('pool', self.share_pools[0].pool) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.pools_mock.list.assert_called_with( detailed=False, search_opts={ 'host': self.share_pools[0].host, 'backend': self.share_pools[0].backend, 'pool': self.share_pools[0].pool, 'share_type': None, }) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) def test_list_share_pools_share_type(self): arglist = [ '--share-type', 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.pools_mock.list.assert_called_with( detailed=False, search_opts={ 'host': None, 'backend': None, 'pool': None, 'share_type': self.share_type.id, }) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) def test_list_share_pools_share_type_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.22") arglist = [ '--share-type', self.share_type.id ] verifylist = [ ('share_type', self.share_type.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_list_share_pools_detail(self): detail_columns = ['Name', 'Host', 'Backend', 'Pool', 'Capabilities'] detail_values = (oscutils.get_dict_properties( pool._info, detail_columns) for pool in self.share_pools) arglist = [ '--detail' ] verifylist = [ ('detail', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.pools_mock.list.assert_called_with( detailed=True, search_opts={ 'host': None, 'backend': None, 'pool': None, 'share_type': None, }) self.assertEqual(detail_columns, columns) self.assertEqual(list(detail_values), list(data)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_replica_export_locations.py0000664000175000017500000000763400000000000033321 0ustar00zuulzuul00000000000000# 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.osc.v2 import ( share_replica_export_locations as osc_replica_el ) from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShareReplica(manila_fakes.TestShare): def setUp(self): super(TestShareReplica, self).setUp() self.replicas_mock = self.app.client_manager.share.share_replicas self.replicas_mock.reset_mock() self.export_locations_mock = ( self.app.client_manager.share.share_replica_export_locations) self.export_locations_mock.reset_mock() class TestShareReplicaExportLocationList(TestShareReplica): columns = [ 'ID', 'Availability Zone', 'Replica State', 'Preferred', 'Path' ] def setUp(self): super(TestShareReplicaExportLocationList, self).setUp() self.share_replica = ( manila_fakes.FakeShareReplica.create_one_replica()) self.replicas_mock.get.return_value = self.share_replica self.export_locations = ([ manila_fakes.FakeShareExportLocation.create_one_export_location() ]) self.export_locations_mock.list.return_value = self.export_locations self.values = (oscutils.get_dict_properties( e._info, self.columns) for e in self.export_locations) self.cmd = osc_replica_el.ShareReplicaListExportLocation( self.app, None) def test_replica_export_locations_list(self): arglist = [ self.share_replica.id ] verifylist = [ ('replica', self.share_replica.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.replicas_mock.get.assert_called_with(self.share_replica.id) self.export_locations_mock.list.assert_called_with( self.share_replica) self.assertEqual(self.columns, columns) self.assertCountEqual(self.values, data) class TestShareReplicaExportLocationShow(TestShareReplica): def setUp(self): super(TestShareReplicaExportLocationShow, self).setUp() self.share_replica = ( manila_fakes.FakeShareReplica.create_one_replica()) self.replicas_mock.get.return_value = self.share_replica self.export_location = ( manila_fakes.FakeShareExportLocation.create_one_export_location()) self.export_locations_mock.get.return_value = self.export_location self.cmd = osc_replica_el.ShareReplicaShowExportLocation( self.app, None) def test_replica_export_locations_show(self): arglist = [ self.share_replica.id, self.export_location.id ] verifylist = [ ('replica', self.share_replica.id), ('export_location', self.export_location.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.replicas_mock.get.assert_called_with(self.share_replica.id) self.export_locations_mock.get.assert_called_with( self.share_replica, self.export_location.id) self.assertCountEqual( tuple(self.export_location._info.keys()), columns) self.assertCountEqual(self.export_location._info.values(), data) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_replicas.py0000664000175000017500000005402000000000000030017 0ustar00zuulzuul00000000000000# 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 unittest import mock from osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.common import cliutils from manilaclient.osc import utils from manilaclient.osc.v2 import share_replicas as osc_share_replicas from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShareReplica(manila_fakes.TestShare): def setUp(self): super(TestShareReplica, self).setUp() self.shares_mock = self.app.client_manager.share.shares self.shares_mock.reset_mock() self.replicas_mock = self.app.client_manager.share.share_replicas self.replicas_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION) self.replica_el_mock = ( self.app.client_manager .share.share_replica_export_locations) self.replica_el_mock.reset_mock() class TestShareReplicaCreate(TestShareReplica): def setUp(self): super(TestShareReplicaCreate, self).setUp() self.share = manila_fakes.FakeShare.create_one_share() self.shares_mock.get.return_value = self.share self.share_replica = ( manila_fakes.FakeShareReplica.create_one_replica( attrs={ 'availability_zone': 'manila-zone-1', 'status': 'available'} )) self.replicas_mock.create.return_value = self.share_replica self.replicas_mock.get.return_value = self.share_replica self.cmd = osc_share_replicas.CreateShareReplica(self.app, None) self.data = tuple(self.share_replica._info.values()) self.columns = tuple(self.share_replica._info.keys()) def test_share_replica_create_missing_args(self): arglist = [] verifylist = [] self.assertRaises( osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_replica_create(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.replicas_mock.create.assert_called_with( share=self.share, availability_zone=None ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_replica_create_az(self): arglist = [ self.share.id, '--availability-zone', self.share.availability_zone ] verifylist = [ ('share', self.share.id), ('availability_zone', self.share.availability_zone) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.replicas_mock.create.assert_called_with( share=self.share, availability_zone=self.share.availability_zone ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_replica_create_scheduler_hint_valid(self): arglist = [ self.share.id, '--availability-zone', self.share.availability_zone, '--scheduler-hint', ('only_host=host1@backend1#pool1'), ] verifylist = [ ('share', self.share.id), ('availability_zone', self.share.availability_zone), ('scheduler_hint', {'only_host': 'host1@backend1#pool1'}) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.replicas_mock.create.assert_called_with( share=self.share, availability_zone=self.share.availability_zone, scheduler_hints={'only_host': 'host1@backend1#pool1'} ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_replica_create_scheduler_hint_invalid_hint(self): arglist = [ self.share.id, '--availability-zone', self.share.availability_zone, '--scheduler-hint', 'fake_hint=host1@backend1#pool1' ] verifylist = [ ('share', self.share.id), ('availability_zone', self.share.availability_zone), ('scheduler_hint', {'fake_hint': 'host1@backend1#pool1'}) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_replica_create_scheduler_hint_invalid_version(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.66") arglist = [ self.share.id, '--availability-zone', self.share.availability_zone, '--scheduler-hint', 'only_host=host1@backend1#pool1' ] verifylist = [ ('share', self.share.id), ('availability_zone', self.share.availability_zone), ('scheduler_hint', {'only_host': 'host1@backend1#pool1'}) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_replica_create_share_network(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.72") arglist = [ self.share.id, '--availability-zone', self.share.availability_zone, '--share-network', self.share.share_network_id ] verifylist = [ ('share', self.share.id), ('availability_zone', self.share.availability_zone), ('share_network', self.share.share_network_id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) if self.share.share_network_id: self.replicas_mock.create.assert_called_with( share=self.share, availability_zone=self.share.availability_zone, share_network=self.share.share_network_id ) else: self.replicas_mock.create.assert_called_with( share=self.share, availability_zone=self.share.availability_zone, ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_replica_create_wait(self): arglist = [ self.share.id, '--wait' ] verifylist = [ ('share', self.share.id), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.replicas_mock.create.assert_called_with( share=self.share, availability_zone=None ) self.replicas_mock.get.assert_called_with(self.share_replica.id) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) @mock.patch('manilaclient.osc.v2.share_replicas.LOG') def test_share_replica_create_wait_exception(self, mock_logger): arglist = [ self.share.id, '--wait' ] verifylist = [ ('share', self.share.id), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_status', return_value=False): columns, data = self.cmd.take_action(parsed_args) self.replicas_mock.create.assert_called_with( share=self.share, availability_zone=None ) mock_logger.error.assert_called_with( "ERROR: Share replica is in error state.") self.replicas_mock.get.assert_called_with(self.share_replica.id) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestShareReplicaDelete(TestShareReplica): def setUp(self): super(TestShareReplicaDelete, self).setUp() self.share_replica = ( manila_fakes.FakeShareReplica.create_one_replica()) self.replicas_mock.get.return_value = self.share_replica self.cmd = osc_share_replicas.DeleteShareReplica(self.app, None) def test_share_replica_delete_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_replica_delete(self): arglist = [ self.share_replica.id ] verifylist = [ ('replica', [self.share_replica.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.replicas_mock.delete.assert_called_with( self.share_replica, force=False) self.assertIsNone(result) def test_share_replica_delete_force(self): arglist = [ self.share_replica.id, '--force' ] verifylist = [ ('replica', [self.share_replica.id]), ('force', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.replicas_mock.delete.assert_called_with( self.share_replica, force=True) self.assertIsNone(result) def test_share_replica_delete_multiple(self): share_replicas = ( manila_fakes.FakeShareReplica.create_share_replicas( count=2)) arglist = [ share_replicas[0].id, share_replicas[1].id ] verifylist = [ ('replica', [share_replicas[0].id, share_replicas[1].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertEqual(self.replicas_mock.delete.call_count, len(share_replicas)) self.assertIsNone(result) def test_share_snapshot_delete_exception(self): arglist = [ self.share_replica.id ] verifylist = [ ('replica', [self.share_replica.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.replicas_mock.delete.side_effect = exceptions.CommandError() self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_replica_delete_wait(self): arglist = [ self.share_replica.id, '--wait' ] verifylist = [ ('replica', [self.share_replica.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=True): result = self.cmd.take_action(parsed_args) self.replicas_mock.delete.assert_called_with( self.share_replica, force=False) self.replicas_mock.get.assert_called_with(self.share_replica.id) self.assertIsNone(result) def test_share_replica_delete_wait_exception(self): arglist = [ self.share_replica.id, '--wait' ] verifylist = [ ('replica', [self.share_replica.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=False): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestShareReplicaList(TestShareReplica): columns = [ 'id', 'status', 'replica_state', 'share_id', 'host', 'availability_zone', 'updated_at', ] column_headers = utils.format_column_headers(columns) def setUp(self): super(TestShareReplicaList, self).setUp() self.share = manila_fakes.FakeShare.create_one_share() self.shares_mock.get.return_value = self.share self.replicas_list = ( manila_fakes.FakeShareReplica.create_share_replicas( count=2)) self.replicas_mock.list.return_value = self.replicas_list self.values = (oscutils.get_dict_properties( i._info, self.columns) for i in self.replicas_list) self.cmd = osc_share_replicas.ListShareReplica(self.app, None) def test_share_replica_list(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.replicas_mock.list.assert_called_with(share=None) self.assertEqual(self.column_headers, columns) self.assertEqual(list(self.values), list(data)) def test_share_replica_list_for_share(self): arglist = [ '--share', 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.replicas_mock.list.assert_called_with(share=self.share) self.assertEqual(self.column_headers, columns) self.assertEqual(list(self.values), list(data)) class TestShareReplicaShow(TestShareReplica): def setUp(self): super(TestShareReplicaShow, self).setUp() self.share_replica = ( manila_fakes.FakeShareReplica.create_one_replica() ) self.replicas_mock.get.return_value = self.share_replica self.replica_el_list = ( manila_fakes.FakeShareExportLocation. create_share_export_locations(count=2) ) self.replica_el_mock.list.return_value = ( self.replica_el_list) self.cmd = osc_share_replicas.ShowShareReplica(self.app, None) self.share_replica._info['export_locations'] = ( cliutils.convert_dict_list_to_string( self.replica_el_list)) self.data = tuple(self.share_replica._info.values()) self.columns = tuple(self.share_replica._info.keys()) def test_share_replica_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises( osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_replica_show(self): arglist = [ self.share_replica.id ] verifylist = [ ('replica', self.share_replica.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.replicas_mock.get.assert_called_with( self.share_replica.id ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestShareReplicaSet(TestShareReplica): def setUp(self): super(TestShareReplicaSet, self).setUp() self.share_replica = ( manila_fakes.FakeShareReplica.create_one_replica() ) self.replicas_mock.get.return_value = self.share_replica self.cmd = osc_share_replicas.SetShareReplica(self.app, None) def test_share_replica_set_replica_state(self): new_replica_state = 'in_sync' arglist = [ self.share_replica.id, '--replica-state', new_replica_state ] verifylist = [ ('replica', self.share_replica.id), ('replica_state', new_replica_state) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.replicas_mock.reset_replica_state.assert_called_with( self.share_replica, new_replica_state) self.assertIsNone(result) def test_share_replica_set_replica_state_exception(self): new_replica_state = 'in_sync' arglist = [ self.share_replica.id, '--replica-state', new_replica_state ] verifylist = [ ('replica', self.share_replica.id), ('replica_state', new_replica_state) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.replicas_mock.reset_replica_state.side_effect = Exception() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_replica_set_status(self): new_status = 'available' arglist = [ self.share_replica.id, '--status', new_status ] verifylist = [ ('replica', self.share_replica.id), ('status', new_status) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.replicas_mock.reset_state.assert_called_with( self.share_replica, new_status) self.assertIsNone(result) def test_share_replica_set_status_exception(self): new_status = 'available' arglist = [ self.share_replica.id, '--status', new_status ] verifylist = [ ('replica', self.share_replica.id), ('status', new_status) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.replicas_mock.reset_state.side_effect = Exception() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_replica_set_nothing_defined(self): arglist = [ self.share_replica.id, ] verifylist = [ ('replica', self.share_replica.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareReplicaPromote(TestShareReplica): def setUp(self): super(TestShareReplicaPromote, self).setUp() self.share_replica = ( manila_fakes.FakeShareReplica.create_one_replica() ) self.replicas_mock.get.return_value = self.share_replica self.cmd = osc_share_replicas.PromoteShareReplica( self.app, None) def test_share_replica_promote(self): arglist = [ self.share_replica.id, ] verifylist = [ ('replica', self.share_replica.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.replicas_mock.promote.assert_called_with( self.share_replica) self.assertIsNone(result) def test_share_replica_promote_quiesce_wait_time(self): wait_time = '5' arglist = [ self.share_replica.id, '--quiesce-wait-time', wait_time ] verifylist = [ ('replica', self.share_replica.id), ('quiesce_wait_time', wait_time) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.replicas_mock.promote.assert_called_with( self.share_replica, wait_time) self.assertIsNone(result) def test_share_replica_promote_exception(self): arglist = [ self.share_replica.id, ] verifylist = [ ('replica', self.share_replica.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.replicas_mock.promote.side_effect = Exception() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareReplicaResync(TestShareReplica): def setUp(self): super(TestShareReplicaResync, self).setUp() self.share_replica = ( manila_fakes.FakeShareReplica.create_one_replica() ) self.replicas_mock.get.return_value = self.share_replica self.cmd = osc_share_replicas.ResyncShareReplica( self.app, None) def test_share_replica_resync(self): arglist = [ self.share_replica.id, ] verifylist = [ ('replica', self.share_replica.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.replicas_mock.resync.assert_called_with( self.share_replica) self.assertIsNone(result) def test_share_replica_resync_exception(self): arglist = [ self.share_replica.id, ] verifylist = [ ('replica', self.share_replica.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.replicas_mock.resync.side_effect = Exception() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_servers.py0000664000175000017500000006064300000000000027716 0ustar00zuulzuul00000000000000# 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 unittest import mock from osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.osc.v2 import share_servers as osc_share_servers from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShareServer(manila_fakes.TestShare): def setUp(self): super(TestShareServer, self).setUp() self.servers_mock = self.app.client_manager.share.share_servers self.servers_mock.reset_mock() self.share_networks_mock = self.app.client_manager.share.share_networks self.share_networks_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION) class TestDeleteShareServer(TestShareServer): def setUp(self): super(TestDeleteShareServer, self).setUp() self.share_server = ( manila_fakes.FakeShareServer.create_one_server()) self.servers_mock.get.return_value = self.share_server self.cmd = osc_share_servers.DeleteShareServer(self.app, None) def test_share_server_delete_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_server_delete(self): arglist = [ self.share_server.id ] verifylist = [ ('share_servers', [self.share_server.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.servers_mock.delete.assert_called_once_with( self.share_server) self.assertIsNone(result) def test_share_server_delete_wait(self): arglist = [ self.share_server.id, '--wait' ] verifylist = [ ('share_servers', [self.share_server.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=True): result = self.cmd.take_action(parsed_args) self.servers_mock.delete.assert_called_once_with( self.share_server) self.assertIsNone(result) def test_share_server_delete_wait_exception(self): arglist = [ self.share_server.id, '--wait' ] verifylist = [ ('share_servers', [self.share_server.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=False): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestShowShareServer(TestShareServer): def setUp(self): super(TestShowShareServer, self).setUp() self.share_server = ( manila_fakes.FakeShareServer.create_one_server()) self.servers_mock.get.return_value = self.share_server self.cmd = osc_share_servers.ShowShareServer(self.app, None) self.data = tuple(self.share_server._info.values()) self.columns = tuple(self.share_server._info.keys()) def test_share_server_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises( osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_server_show(self): arglist = [ self.share_server.id ] verifylist = [ ('share_server', self.share_server.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.servers_mock.get.assert_called_with( self.share_server.id ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestListShareServer(TestShareServer): columns = [ 'ID', 'Host', 'Status', 'Share Network ID', 'Project ID', ] def setUp(self): super(TestListShareServer, self).setUp() self.servers_list = ( manila_fakes.FakeShareServer.create_share_servers( count=2)) self.servers_mock.list.return_value = self.servers_list self.values = (oscutils.get_dict_properties( i._info, self.columns) for i in self.servers_list) self.cmd = osc_share_servers.ListShareServer(self.app, None) def test_list_share_server(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.servers_mock.list.assert_called_with(search_opts={ 'status': None, 'host': None, 'project_id': None, }) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) def test_share_server_list_by_status(self): arglist = [ '--status', self.servers_list[0].status, ] verifylist = [ ('status', self.servers_list[0].status), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) search_opts = { 'status': None, 'host': None, 'project_id': None, } self.servers_mock.list.assert_called_once_with( search_opts=search_opts, ) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) class TestAdoptShareServer(TestShareServer): def setUp(self): super(TestAdoptShareServer, self).setUp() self.share_server = ( manila_fakes.FakeShareServer.create_one_server( attrs={'status': 'available'} )) self.servers_mock.get.return_value = self.share_server self.servers_mock.manage.return_value = self.share_server self.share_network_subnets_mock = ( self.app.client_manager.share.share_network_subnets) self.share_network = ( manila_fakes.FakeShareNetwork.create_one_share_network( attrs={'status': 'available'} )) self.share_network_subnet = ( manila_fakes.FakeShareNetworkSubnet.create_one_share_subnet()) self.share_networks_mock.get.return_value = self.share_network self.share_network_subnets_mock.get.return_value = ( self.share_network_subnet) self.cmd = osc_share_servers.AdoptShareServer(self.app, None) self.data = tuple(self.share_server._info.values()) self.columns = tuple(self.share_server._info.keys()) def test_share_server_adopt_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_server_adopt(self): arglist = [ 'somehost@backend', self.share_network['id'], 'share_server_identifier', '--share-network-subnet', self.share_network_subnet['id'], ] verifylist = [ ('host', 'somehost@backend'), ('share_network', self.share_network['id']), ('identifier', 'share_server_identifier'), ('share_network_subnet', self.share_network_subnet['id']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.servers_mock.manage.assert_called_with( host='somehost@backend', share_network_id=self.share_network['id'], identifier='share_server_identifier', driver_options={}, share_network_subnet_id=self.share_network_subnet['id'], ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_server_adopt_wait(self): arglist = [ 'somehost@backend', self.share_network['id'], 'share_server_identifier', '--share-network-subnet', self.share_network_subnet['id'], '--wait' ] verifylist = [ ('host', 'somehost@backend'), ('share_network', self.share_network['id']), ('identifier', 'share_server_identifier'), ('share_network_subnet', self.share_network_subnet['id']), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_status', return_value=True): self.cmd.take_action(parsed_args) self.servers_mock.manage.assert_called_with( host='somehost@backend', share_network_id=self.share_network['id'], identifier='share_server_identifier', driver_options={}, share_network_subnet_id=self.share_network_subnet['id'] ) def test_share_server_adopt_subnet_not_supported(self): arglist = [ 'somehost@backend', self.share_network['id'], 'share_server_identifier', '--share-network-subnet', self.share_network_subnet['id'], '--wait' ] verifylist = [ ('host', 'somehost@backend'), ('share_network', self.share_network['id']), ('identifier', 'share_server_identifier'), ('share_network_subnet', self.share_network_subnet['id']), ('wait', True) ] self.app.client_manager.share.api_version = api_versions.APIVersion( "2.50") parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) class TestAbandonShareServer(TestShareServer): def setUp(self): super(TestAbandonShareServer, self).setUp() self.share_server = ( manila_fakes.FakeShareServer.create_one_server()) self.servers_mock.get.return_value = self.share_server self.cmd = osc_share_servers.AbandonShareServer(self.app, None) self.data = tuple(self.share_server._info.values()) self.columns = tuple(self.share_server._info.keys()) def test_share_server_abandon_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_server_abandon(self): arglist = [ self.share_server.id ] verifylist = [ ('share_server', [self.share_server.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.servers_mock.unmanage.assert_called_with( self.share_server) self.assertIsNone(result) def test_share_server_abandon_multiple(self): share_servers = ( manila_fakes.FakeShareServer.create_share_servers( count=2)) arglist = [ share_servers[0].id, share_servers[1].id ] verifylist = [ ('share_server', [share_servers[0].id, share_servers[1].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertEqual(self.servers_mock.unmanage.call_count, len(share_servers)) self.assertIsNone(result) def test_share_server_abandon_force(self): arglist = [ self.share_server.id, '--force' ] verifylist = [ ('share_server', [self.share_server.id]), ('force', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.servers_mock.unmanage.assert_called_with( self.share_server, force=True) self.assertIsNone(result) def test_share_server_abandon_force_exception(self): arglist = [ self.share_server.id, ] verifylist = [ ('share_server', [self.share_server.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.servers_mock.unmanage.side_effect = exceptions.CommandError() self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_server_abandon_wait(self): arglist = [ self.share_server.id, '--wait' ] verifylist = [ ('share_server', [self.share_server.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=True): result = self.cmd.take_action(parsed_args) self.servers_mock.unmanage.assert_called_with( self.share_server) self.assertIsNone(result) def test_share_server_abandon_wait_error(self): arglist = [ self.share_server.id, '--wait' ] verifylist = [ ('share_server', [self.share_server.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=False): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestSetShareServer(TestShareServer): def setUp(self): super(TestSetShareServer, self).setUp() self.share_server = ( manila_fakes.FakeShareServer.create_one_server( methods={'reset_task_state': None} ) ) self.servers_mock.get.return_value = self.share_server self.cmd = osc_share_servers.SetShareServer(self.app, None) def test_share_server_set_status(self): arglist = [ self.share_server.id, '--status', 'active' ] verifylist = [ ('share_server', self.share_server.id), ('status', 'active') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.servers_mock.reset_state.assert_called_with( self.share_server, parsed_args.status) self.assertIsNone(result) def test_share_server_set_task_state(self): arglist = [ self.share_server.id, '--task-state', 'migration_in_progress' ] verifylist = [ ('share_server', self.share_server.id), ('task_state', 'migration_in_progress') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.servers_mock.reset_task_state.assert_called_with( self.share_server, parsed_args.task_state) self.assertIsNone(result) def test_share_server_set_status_exception(self): arglist = [ self.share_server.id, '--status', 'active' ] verifylist = [ ('share_server', self.share_server.id), ('status', 'active') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.servers_mock.reset_state.side_effect = Exception() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareServerMigrationCancel(TestShareServer): def setUp(self): super(TestShareServerMigrationCancel, self).setUp() self.share_server = ( manila_fakes.FakeShareServer.create_one_server( attrs={ 'status': 'migrating', }, methods={'migration_cancel': None} ) ) self.servers_mock.get.return_value = self.share_server # Get the command objects to test self.cmd = osc_share_servers.ShareServerMigrationCancel(self.app, None) def test_share_server_migration_cancel(self): arglist = [ self.share_server.id ] verifylist = [ ('share_server', self.share_server.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_server.migration_cancel.assert_called class TestShareServerMigrationComplete(TestShareServer): def setUp(self): super(TestShareServerMigrationComplete, self).setUp() self.share_server = ( manila_fakes.FakeShareServer.create_one_server( attrs={ 'status': 'migrating', }, methods={'migration_complete': None} ) ) self.servers_mock.get.return_value = self.share_server # Get the command objects to test self.cmd = osc_share_servers.ShareServerMigrationComplete( self.app, None) def test_share_server_migration_complete(self): arglist = [ self.share_server.id ] verifylist = [ ('share_server', self.share_server.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_server.migration_complete.assert_called class TestShareServerMigrationShow(TestShareServer): def setUp(self): super(TestShareServerMigrationShow, self).setUp() self.new_share_network = manila_fakes.FakeShareNetwork \ .create_one_share_network() self.share_networks_mock.get.return_value = self.new_share_network self.share_server = ( manila_fakes.FakeShareServer.create_one_server( attrs={ 'status': 'migrating', 'task_state': 'migration_in_progress' }, methods={'migration_get_progress': None} ) ) self.servers_mock.get.return_value = self.share_server # Get the command objects to test self.cmd = osc_share_servers.ShareServerMigrationShow(self.app, None) def test_share_server_migration_show(self): arglist = [ self.share_server.id ] verifylist = [ ('share_server', self.share_server.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_server.migration_get_progress.assert_called class TestShareServerMigrationStart(TestShareServer): def setUp(self): super(TestShareServerMigrationStart, self).setUp() self.new_share_network = manila_fakes.FakeShareNetwork \ .create_one_share_network() self.share_networks_mock.get.return_value = self.new_share_network self.share_server = ( manila_fakes.FakeShareServer.create_one_server( attrs={ 'check_only': 'False', }, methods={'migration_start': None, 'migration_check': None} )) self.servers_mock.get.return_value = self.share_server # Get the command objects to test self.cmd = osc_share_servers.ShareServerMigrationStart(self.app, None) def test_share_server_migration_start_with_new_share_network(self): """Test share server migration with new_share_network""" arglist = [ '1234', 'host@backend', '--preserve-snapshots', 'False', '--writable', 'False', '--nondisruptive', 'False', '--new-share-network', self.new_share_network.id ] verifylist = [ ('share_server', '1234'), ('host', 'host@backend'), ('preserve_snapshots', 'False'), ('writable', 'False'), ('nondisruptive', 'False'), ('new_share_network', self.new_share_network.id) ] self.app.client_manager.share.api_version = api_versions.APIVersion( "2.57") parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.share_server.migration_start.assert_called_with( 'host@backend', 'False', 'False', 'False', self.new_share_network.id ) self.assertEqual(result, ({}, {})) def test_share_server_migration_start_with_check_only(self): """Test share server migration start with check only""" arglist = [ '1234', 'host@backend', '--preserve-snapshots', 'True', '--writable', 'True', '--nondisruptive', 'False', '--new-share-network', self.new_share_network.id, '--check-only', ] verifylist = [ ('share_server', '1234'), ('host', 'host@backend'), ('preserve_snapshots', 'True'), ('writable', 'True'), ('nondisruptive', 'False'), ('new_share_network', self.new_share_network.id), ('check_only', True) ] expected_result = { 'compatible': True, 'requested_capabilities': { 'writable': 'True', 'nondisruptive': 'False', 'preserve_snapshots': 'True', 'share_network_id': None, 'host': 'host@backend' }, 'supported_capabilities': { 'writable': True, 'nondisruptive': False, 'preserve_snapshots': True, 'share_network_id': self.new_share_network.id, 'migration_cancel': True, 'migration_get_progress': True } } self.share_server.migration_check.return_value = expected_result self.app.client_manager.share.api_version = api_versions.APIVersion( "2.57") parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.share_server.migration_check.assert_called_with( 'host@backend', 'True', 'False', 'True', self.new_share_network.id, ) result_dict = {} for count, column in enumerate(columns): result_dict[column] = data[count] self.assertEqual(expected_result, result_dict) def test_share_server_migration_start_with_api_version_exception(self): """Test share server migration start with API microversion exception""" self.app.client_manager.share.api_version = api_versions.APIVersion( "2.50") arglist = [ '1234', 'host@backend', '--preserve-snapshots', 'False', '--writable', 'False', '--nondisruptive', 'False', '--new-share-network', self.new_share_network.id ] verifylist = [ ('share_server', '1234'), ('host', 'host@backend'), ('preserve_snapshots', 'False'), ('writable', 'False'), ('nondisruptive', 'False'), ('new_share_network', self.new_share_network.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) ././@PaxHeader0000000000000000000000000000021100000000000011447 xustar0000000000000000115 path=python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_snapshot_instance_export_locations.py 22 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_snapshot_instance_export_locatio0000664000175000017500000001306700000000000034432 0ustar00zuulzuul00000000000000# Copyright 2021 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. # from osc_lib import utils as osc_lib_utils from manilaclient.osc.v2 import (share_snapshot_instance_export_locations as osc_snapshot_instance_locations) from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes COLUMNS = ['ID', 'Path', 'Is Admin only'] class TestShareSnapshotInstanceExportLocation(manila_fakes.TestShare): def setUp(self): super(TestShareSnapshotInstanceExportLocation, self).setUp() self.share_snapshot_instances_mock = ( self.app.client_manager.share.share_snapshot_instances) self.share_snapshot_instances_mock.reset_mock() self.share_snapshot_instances_el_mock = ( self.app.client_manager .share.share_snapshot_instance_export_locations) self.share_snapshot_instances_el_mock.reset_mock() class TestShareSnapshotInstanceExportLocationList( TestShareSnapshotInstanceExportLocation): def setUp(self): super(TestShareSnapshotInstanceExportLocationList, self).setUp() self.share_snapshot_instance = ( manila_fakes.FakeShareSnapshotIntances .create_one_snapshot_instance()) self.share_snapshot_instances_export_locations = ( manila_fakes.FakeShareSnapshotInstancesExportLocations .create_share_snapshot_instances(count=2) ) self.share_snapshot_instances_mock.get.return_value = ( self.share_snapshot_instance) self.share_snapshot_instances_el_mock.list.return_value = ( self.share_snapshot_instances_export_locations) self.cmd = (osc_snapshot_instance_locations .ShareSnapshotInstanceExportLocationList(self.app, None)) def test_share_snapshot_instance_export_location_list_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_snapshot_instance_export_location_list(self): values = (osc_lib_utils.get_dict_properties( s._info, COLUMNS) for s in self.share_snapshot_instances_export_locations) arglist = [ self.share_snapshot_instance.id ] verifylist = [ ('instance', self.share_snapshot_instance.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.assertEqual(COLUMNS, columns) self.assertEqual(list(values), list(data)) class TestShareSnapshotInstanceExportLocationShow( TestShareSnapshotInstanceExportLocation): def setUp(self): super(TestShareSnapshotInstanceExportLocationShow, self).setUp() self.share_snapshot_instance = ( manila_fakes.FakeShareSnapshotIntances .create_one_snapshot_instance()) self.share_snapshot_instances_export_location = ( manila_fakes.FakeShareSnapshotInstancesExportLocations .create_one_snapshot_instance() ) self.share_snapshot_instances_mock.get.return_value = ( self.share_snapshot_instance) self.share_snapshot_instances_el_mock.get.return_value = ( self.share_snapshot_instances_export_location) self.cmd = (osc_snapshot_instance_locations .ShareSnapshotInstanceExportLocationShow(self.app, None)) self.data = (self.share_snapshot_instances_export_location. _info.values()) self.columns = (self.share_snapshot_instances_export_location. _info.keys()) def test_share_snapshot_instance_export_location_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_snapshot_instance_export_location_show(self): arglist = [ self.share_snapshot_instance.id, self.share_snapshot_instances_export_location.id ] verifylist = [ ('snapshot_instance', self.share_snapshot_instance.id), ('export_location', self.share_snapshot_instances_export_location.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.share_snapshot_instances_mock.get.assert_called_with( self.share_snapshot_instance.id) self.share_snapshot_instances_el_mock.get.assert_called_with( self.share_snapshot_instances_export_location.id, snapshot_instance=self.share_snapshot_instance) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_snapshot_instances.py0000664000175000017500000002007000000000000032121 0ustar00zuulzuul00000000000000# Copyright 2021 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. # from osc_lib import exceptions as osc_exceptions from osc_lib import utils as osc_lib_utils from manilaclient.common.apiclient import exceptions as api_exceptions from manilaclient.common import cliutils from manilaclient.osc.v2 import ( share_snapshot_instances as osc_share_snapshot_instances) from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes COLUMNS = ['ID', 'Snapshot ID', 'Status'] COLUMNS_DETAIL = [ 'ID', 'Snapshot ID', 'Status', 'Created At', 'Updated At', 'Share ID', 'Share Instance ID', 'Progress', 'Provider Location' ] class TestShareSnapshotInstance(manila_fakes.TestShare): def setUp(self): super(TestShareSnapshotInstance, self).setUp() self.share_snapshots_mock = ( self.app.client_manager.share.share_snapshots) self.share_snapshots_mock.reset_mock() self.share_snapshot_instances_mock = ( self.app.client_manager.share.share_snapshot_instances) self.share_snapshot_instances_mock.reset_mock() self.share_snapshot_instances_el_mock = ( self.app.client_manager .share.share_snapshot_instance_export_locations) self.share_snapshot_instances_el_mock.reset_mock() class TestShareSnapshotInstanceList(TestShareSnapshotInstance): def setUp(self): super(TestShareSnapshotInstanceList, self).setUp() self.share_snapshot_instances = ( manila_fakes.FakeShareSnapshotIntances .create_share_snapshot_instances(count=2)) self.share_snapshot_instances_mock.list.return_value = ( self.share_snapshot_instances) self.cmd = ( osc_share_snapshot_instances.ListShareSnapshotInstance(self.app, None)) def test_share_snapshot_instance_list(self): values = (osc_lib_utils.get_dict_properties( s._info, COLUMNS) for s in self.share_snapshot_instances) arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.assertEqual(COLUMNS, columns) self.assertEqual(list(values), list(data)) def test_share_snapshot_instance_list_detail(self): values = (osc_lib_utils.get_dict_properties( s._info, COLUMNS_DETAIL) for s in self.share_snapshot_instances) arglist = [ '--detailed' ] verifylist = [ ('detailed', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.assertEqual(COLUMNS_DETAIL, columns) self.assertEqual(list(values), list(data)) def test_share_snapshot_instance_list_snapshot_id(self): self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot()) self.share_snapshots_mock.get.return_value = self.share_snapshot self.share_snapshot_instances_mock.list.return_value = ( [self.share_snapshot_instances[0]]) values = (osc_lib_utils.get_dict_properties( s._info, COLUMNS) for s in [self.share_snapshot_instances[0]]) arglist = [ '--snapshot', self.share_snapshot.id ] verifylist = [ ('snapshot', self.share_snapshot.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.assertEqual(COLUMNS, columns) self.assertEqual(list(values), list(data)) class TestShareSnapshotInstanceShow(TestShareSnapshotInstance): def setUp(self): super(TestShareSnapshotInstanceShow, self).setUp() self.share_snapshot_instance = ( manila_fakes.FakeShareSnapshotIntances .create_one_snapshot_instance()) self.share_snapshot_instances_mock.get.return_value = ( self.share_snapshot_instance) self.share_snapshot_instances_el_list = ( manila_fakes.FakeShareSnapshotInstancesExportLocations .create_share_snapshot_instances(count=2) ) self.share_snapshot_instances_el_mock.list.return_value = ( self.share_snapshot_instances_el_list) self.cmd = (osc_share_snapshot_instances .ShowShareSnapshotInstance(self.app, None)) self.share_snapshot_instance._info['export_locations'] = ( cliutils.convert_dict_list_to_string( self.share_snapshot_instances_el_list)) self.data = self.share_snapshot_instance._info.values() self.columns = self.share_snapshot_instance._info.keys() def test_share_snapshot_instance_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_snapshot_instance_show(self): arglist = [ self.share_snapshot_instance.id ] verifylist = [ ('snapshot_instance', self.share_snapshot_instance.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.share_snapshot_instances_mock.get.assert_called_with( self.share_snapshot_instance.id) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestShareSnapshotInstanceSet(TestShareSnapshotInstance): def setUp(self): super(TestShareSnapshotInstanceSet, self).setUp() self.share_snapshot_instance = ( manila_fakes.FakeShareSnapshotIntances .create_one_snapshot_instance()) self.snapshot_instance_status = 'available' self.cmd = (osc_share_snapshot_instances .SetShareSnapshotInstance(self.app, None)) def test_share_snapshot_instance_set_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_snapshot_instance_set_instance_not_found(self): arglist = [ self.share_snapshot_instance.id, '--status', self.snapshot_instance_status ] verifylist = [ ('snapshot_instance', self.share_snapshot_instance.id), ('status', self.snapshot_instance_status) ] self.share_snapshot_instances_mock.reset_state.side_effect = ( api_exceptions.NotFound()) parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises(osc_exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_snapshot_instance_set(self): arglist = [ self.share_snapshot_instance.id, '--status', self.snapshot_instance_status ] verifylist = [ ('snapshot_instance', self.share_snapshot_instance.id), ('status', self.snapshot_instance_status) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_snapshot_instances_mock.reset_state.assert_called_with( self.share_snapshot_instance.id, self.snapshot_instance_status) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_snapshots.py0000664000175000017500000012661200000000000030246 0ustar00zuulzuul00000000000000# 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. # from unittest import mock import uuid import ddt from osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.common import cliutils from manilaclient.osc.v2 import share_snapshots as osc_share_snapshots from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes COLUMNS = ['ID', 'Name'] COLUMNS_DETAIL = [ 'ID', 'Name', 'Status', 'Description', 'Created At', 'Size', 'Share ID', 'Share Proto', 'Share Size', 'User ID' ] class TestShareSnapshot(manila_fakes.TestShare): def setUp(self): super(TestShareSnapshot, self).setUp() self.shares_mock = self.app.client_manager.share.shares self.shares_mock.reset_mock() self.snapshots_mock = self.app.client_manager.share.share_snapshots self.snapshots_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION) self.export_locations_mock = ( self.app.client_manager.share.share_snapshot_export_locations) self.export_locations_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION) class TestShareSnapshotCreate(TestShareSnapshot): def setUp(self): super(TestShareSnapshotCreate, self).setUp() self.share = manila_fakes.FakeShare.create_one_share() self.shares_mock.create.return_value = self.share self.shares_mock.get.return_value = self.share self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot( attrs={'status': 'available'} )) self.snapshots_mock.get.return_value = self.share_snapshot self.snapshots_mock.create.return_value = self.share_snapshot self.cmd = osc_share_snapshots.CreateShareSnapshot(self.app, None) self.data = tuple(self.share_snapshot._info.values()) self.columns = tuple(self.share_snapshot._info.keys()) def test_share_snapshot_create_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_snapshot_create_required_args(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.snapshots_mock.create.assert_called_with( share=self.share, force=False, name=None, description=None, metadata={} ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_snapshot_create_force(self): arglist = [ self.share.id, '--force' ] verifylist = [ ('share', self.share.id), ('force', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.create.assert_called_with( share=self.share, force=True, name=None, description=None, metadata={} ) self.assertCountEqual(columns, columns) self.assertCountEqual(self.data, data) def test_share_snapshot_create(self): arglist = [ self.share.id, '--name', self.share_snapshot.name, '--description', self.share_snapshot.description ] verifylist = [ ('share', self.share.id), ('name', self.share_snapshot.name), ('description', self.share_snapshot.description) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.create.assert_called_with( share=self.share, force=False, name=self.share_snapshot.name, description=self.share_snapshot.description, metadata={} ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_snapshot_create_metadata(self): arglist = [ self.share.id, '--name', self.share_snapshot.name, '--description', self.share_snapshot.description, '--property', 'Manila=zorilla', '--property', 'Zorilla=manila' ] verifylist = [ ('share', self.share.id), ('name', self.share_snapshot.name), ('description', self.share_snapshot.description), ('property', {'Manila': 'zorilla', 'Zorilla': 'manila'}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.create.assert_called_with( share=self.share, force=False, name=self.share_snapshot.name, description=self.share_snapshot.description, metadata={'Manila': 'zorilla', 'Zorilla': 'manila'}, ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_snapshot_create_wait(self): arglist = [ self.share.id, '--wait' ] verifylist = [ ('share', self.share.id), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.create.assert_called_with( share=self.share, force=False, name=None, description=None, metadata={} ) self.snapshots_mock.get.assert_called_with( self.share_snapshot.id) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) @mock.patch('manilaclient.osc.v2.share_snapshots.LOG') def test_share_snapshot_create_wait_error(self, mock_logger): arglist = [ self.share.id, '--wait' ] verifylist = [ ('share', self.share.id), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_status', return_value=False): columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.create.assert_called_with( share=self.share, force=False, name=None, description=None, metadata={} ) mock_logger.error.assert_called_with( "ERROR: Share snapshot is in error state.") self.snapshots_mock.get.assert_called_with( self.share_snapshot.id) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestShareSnapshotDelete(TestShareSnapshot): def setUp(self): super(TestShareSnapshotDelete, self).setUp() self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot()) self.snapshots_mock.get.return_value = self.share_snapshot self.cmd = osc_share_snapshots.DeleteShareSnapshot(self.app, None) def test_share_snapshot_delete_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_snapshot_delete(self): arglist = [ self.share_snapshot.id ] verifylist = [ ('snapshot', [self.share_snapshot.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.snapshots_mock.delete.assert_called_with(self.share_snapshot) self.assertIsNone(result) def test_share_snapshot_delete_force(self): arglist = [ self.share_snapshot.id, '--force' ] verifylist = [ ('snapshot', [self.share_snapshot.id]), ('force', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.snapshots_mock.force_delete.assert_called_with( self.share_snapshot) self.assertIsNone(result) def test_share_snapshot_delete_multiple(self): share_snapshots = ( manila_fakes.FakeShareSnapshot.create_share_snapshots( count=2)) arglist = [ share_snapshots[0].id, share_snapshots[1].id ] verifylist = [ ('snapshot', [share_snapshots[0].id, share_snapshots[1].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertEqual(self.snapshots_mock.delete.call_count, len(share_snapshots)) self.assertIsNone(result) def test_share_snapshot_delete_exception(self): arglist = [ self.share_snapshot.id ] verifylist = [ ('snapshot', [self.share_snapshot.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.snapshots_mock.delete.side_effect = exceptions.CommandError() self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) def test_share_snapshot_delete_wait(self): arglist = [ self.share_snapshot.id, '--wait' ] verifylist = [ ('snapshot', [self.share_snapshot.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=True): result = self.cmd.take_action(parsed_args) self.snapshots_mock.delete.assert_called_with(self.share_snapshot) self.assertIsNone(result) def test_share_snapshot_delete_wait_error(self): arglist = [ self.share_snapshot.id, '--wait' ] verifylist = [ ('snapshot', [self.share_snapshot.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=False): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestShareSnapshotShow(TestShareSnapshot): def setUp(self): super(TestShareSnapshotShow, self).setUp() self.export_location = ( manila_fakes.FakeShareExportLocation.create_one_export_location()) self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot( attrs={ 'export_locations': self.export_location } )) self.snapshots_mock.get.return_value = self.share_snapshot self.cmd = osc_share_snapshots.ShowShareSnapshot(self.app, None) self.data = self.share_snapshot._info.values() self.columns = self.share_snapshot._info.keys() self.export_locations_mock.list.return_value = [self.export_location] def test_share_snapshot_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_snapshot_show(self): arglist = [ self.share_snapshot.id ] verifylist = [ ('snapshot', self.share_snapshot.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) cliutils.convert_dict_list_to_string = mock.Mock() cliutils.convert_dict_list_to_string.return_value = ( self.export_location) columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.get.assert_called_with(self.share_snapshot.id) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestShareSnapshotSet(TestShareSnapshot): def setUp(self): super(TestShareSnapshotSet, self).setUp() self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot( methods={"set_metadata": None} )) self.snapshots_mock.get.return_value = self.share_snapshot self.cmd = osc_share_snapshots.SetShareSnapshot(self.app, None) def test_set_snapshot_name(self): snapshot_name = 'snapshot-name-' + uuid.uuid4().hex arglist = [ self.share_snapshot.id, '--name', snapshot_name ] verifylist = [ ('snapshot', self.share_snapshot.id), ('name', snapshot_name) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.snapshots_mock.update.assert_called_with( self.share_snapshot, display_name=parsed_args.name) self.assertIsNone(result) def test_set_snapshot_description(self): description = 'snapshot-description-' + uuid.uuid4().hex arglist = [ self.share_snapshot.id, '--description', description ] verifylist = [ ('snapshot', self.share_snapshot.id), ('description', description) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.snapshots_mock.update.assert_called_with( self.share_snapshot, display_description=parsed_args.description) self.assertIsNone(result) def test_set_snapshot_status(self): arglist = [ self.share_snapshot.id, '--status', 'available' ] verifylist = [ ('snapshot', self.share_snapshot.id), ('status', 'available') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.snapshots_mock.reset_state.assert_called_with( self.share_snapshot, parsed_args.status) self.assertIsNone(result) def test_set_snapshot_property(self): arglist = [ self.share_snapshot.id, '--property', 'Zorilla=manila', ] verifylist = [ ('snapshot', self.share_snapshot.id), ('property', {'Zorilla': 'manila'}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_snapshot.set_metadata.assert_called_with( {'Zorilla': 'manila'}) def test_set_snapshot_update_exception(self): snapshot_name = 'snapshot-name-' + uuid.uuid4().hex arglist = [ self.share_snapshot.id, '--name', snapshot_name ] verifylist = [ ('snapshot', self.share_snapshot.id), ('name', snapshot_name) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.snapshots_mock.update.side_effect = Exception() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_set_snapshot_status_exception(self): arglist = [ self.share_snapshot.id, '--status', 'available' ] verifylist = [ ('snapshot', self.share_snapshot.id), ('status', 'available') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.snapshots_mock.reset_state.side_effect = Exception() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_set_snapshot_property_exception(self): arglist = [ '--property', 'key=', self.share_snapshot.id, ] verifylist = [ ('property', {'key': ''}), ('snapshot', self.share_snapshot.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_snapshot.set_metadata.assert_called_with( {'key': ''}) # '--property' takes key=value arguments # missing a value would raise a BadRequest self.share_snapshot.set_metadata.side_effect = exceptions.BadRequest self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareSnapshotUnset(TestShareSnapshot): def setUp(self): super(TestShareSnapshotUnset, self).setUp() self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot( methods={"delete_metadata": None} )) self.snapshots_mock.get.return_value = self.share_snapshot self.cmd = osc_share_snapshots.UnsetShareSnapshot(self.app, None) def test_unset_snapshot_name(self): arglist = [ self.share_snapshot.id, '--name' ] verifylist = [ ('snapshot', self.share_snapshot.id), ('name', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.snapshots_mock.update.assert_called_with( self.share_snapshot, display_name=None) self.assertIsNone(result) def test_unset_snapshot_description(self): arglist = [ self.share_snapshot.id, '--description' ] verifylist = [ ('snapshot', self.share_snapshot.id), ('description', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.snapshots_mock.update.assert_called_with( self.share_snapshot, display_description=None) self.assertIsNone(result) def test_unset_snapshot_property(self): arglist = [ '--property', 'Manila', self.share_snapshot.id, ] verifylist = [ ('property', ['Manila']), ('snapshot', self.share_snapshot.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_snapshot.delete_metadata.assert_called_with( parsed_args.property) def test_unset_snapshot_name_exception(self): arglist = [ self.share_snapshot.id, '--name' ] verifylist = [ ('snapshot', self.share_snapshot.id), ('name', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.snapshots_mock.update.side_effect = Exception() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_unset_snapshot_property_exception(self): arglist = [ '--property', 'Manila', self.share_snapshot.id, ] verifylist = [ ('property', ['Manila']), ('snapshot', self.share_snapshot.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_snapshot.delete_metadata.assert_called_with( parsed_args.property) # 404 Not Found would be raised, if property 'Manila' doesn't exist self.share_snapshot.delete_metadata.side_effect = exceptions.NotFound self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) @ddt.ddt class TestShareSnapshotList(TestShareSnapshot): def setUp(self): super(TestShareSnapshotList, self).setUp() self.share_snapshots = ( manila_fakes.FakeShareSnapshot.create_share_snapshots( count=2)) self.snapshots_list = oscutils.sort_items( self.share_snapshots, 'name:asc', str) self.snapshots_mock.list.return_value = self.snapshots_list self.values = (oscutils.get_dict_properties( s._info, COLUMNS) for s in self.snapshots_list) self.cmd = osc_share_snapshots.ListShareSnapshot(self.app, None) def test_list_snapshots(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.list.assert_called_with( search_opts={ 'offset': None, 'limit': None, 'all_tenants': False, 'name': None, 'status': None, 'share_id': None, 'usage': None, 'metadata': {}, 'name~': None, 'description~': None, 'description': None, }) self.assertEqual(COLUMNS, columns) self.assertEqual(list(self.values), list(data)) def test_list_snapshots_all_projects(self): all_tenants_list = COLUMNS.copy() all_tenants_list.append('Project ID') list_values = (oscutils.get_dict_properties( s._info, all_tenants_list) for s in self.snapshots_list) arglist = [ '--all-projects' ] verifylist = [ ('all_projects', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.list.assert_called_with( search_opts={ 'offset': None, 'limit': None, 'all_tenants': True, 'name': None, 'status': None, 'share_id': None, 'usage': None, 'metadata': {}, 'name~': None, 'description~': None, 'description': None, }) self.assertEqual(all_tenants_list, columns) self.assertEqual(list(list_values), list(data)) def test_list_snapshots_detail(self): values = (oscutils.get_dict_properties( s._info, COLUMNS_DETAIL) for s in self.snapshots_list) arglist = [ '--detail' ] verifylist = [ ('detail', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.list.assert_called_with( search_opts={ 'offset': None, 'limit': None, 'all_tenants': False, 'name': None, 'status': None, 'share_id': None, 'usage': None, 'metadata': {}, 'name~': None, 'description~': None, 'description': None }) self.assertEqual(COLUMNS_DETAIL, columns) self.assertEqual(list(values), list(data)) @ddt.data('2.35', '2.78') def test_list_snapshots_api_version_exception(self, v): self.app.client_manager.share.api_version = api_versions.APIVersion(v) if v == "2.35": arglist = [ '--description', 'Description' ] verifylist = [ ('description', 'Description') ] elif v == "2.78": arglist = [ '--count', ] verifylist = [ ('count', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) def test_list_snapshots_share_id(self): self.share = manila_fakes.FakeShare.create_one_share( attrs={'id': self.snapshots_list[0].id}) self.shares_mock.get.return_value = self.share self.snapshots_mock.list.return_value = [self.snapshots_list[0]] values = (oscutils.get_dict_properties( s._info, COLUMNS) for s in [self.snapshots_list[0]]) arglist = [ '--share', 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.snapshots_mock.list.assert_called_with( search_opts={ 'offset': None, 'limit': None, 'all_tenants': False, 'name': None, 'status': None, 'share_id': self.share.id, 'usage': None, 'metadata': {}, 'name~': None, 'description~': None, 'description': None }) self.assertEqual(COLUMNS, columns) self.assertEqual(list(values), list(data)) def test_list_snapshots_with_count(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.79') self.snapshots_mock.list.return_value = self.snapshots_list, 2 arglist = [ '--count', ] verifylist = [ ('count', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.list.assert_called_with( search_opts={ 'offset': None, 'limit': None, 'all_tenants': False, 'name': None, 'status': None, 'share_id': None, 'usage': None, 'metadata': {}, 'name~': None, 'description~': None, 'description': None, 'with_count': True, }) class TestShareSnapshotAdopt(TestShareSnapshot): def setUp(self): super(TestShareSnapshotAdopt, self).setUp() self.share = manila_fakes.FakeShare.create_one_share() self.shares_mock.get.return_value = self.share self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot( attrs={ 'status': 'available' } )) self.snapshots_mock.get.return_value = self.share_snapshot self.export_location = ( manila_fakes.FakeShareExportLocation.create_one_export_location()) self.snapshots_mock.manage.return_value = self.share_snapshot self.cmd = osc_share_snapshots.AdoptShareSnapshot(self.app, None) self.data = tuple(self.share_snapshot._info.values()) self.columns = tuple(self.share_snapshot._info.keys()) def test_share_snapshot_adopt_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_snapshot_adopt(self): arglist = [ self.share.id, self.export_location.fake_path ] verifylist = [ ('share', self.share.id), ('provider_location', self.export_location.fake_path) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.manage.assert_called_with( share=self.share, provider_location=self.export_location.fake_path, driver_options={}, name=None, description=None ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_snapshot_adopt_name(self): name = 'name-' + uuid.uuid4().hex arglist = [ self.share.id, self.export_location.fake_path, '--name', name, ] verifylist = [ ('share', self.share.id), ('provider_location', self.export_location.fake_path), ('name', name) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.manage.assert_called_with( share=self.share, provider_location=self.export_location.fake_path, driver_options={}, name=name, description=None ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_snapshot_adopt_driver_option(self): arglist = [ self.share.id, self.export_location.fake_path, '--driver-option', 'key1=value1', '--driver-option', 'key2=value2' ] verifylist = [ ('share', self.share.id), ('provider_location', self.export_location.fake_path), ('driver_option', {'key1': 'value1', 'key2': 'value2'}) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.manage.assert_called_with( share=self.share, provider_location=self.export_location.fake_path, driver_options={ 'key1': 'value1', 'key2': 'value2' }, name=None, description=None ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_snapshot_adopt_wait(self): arglist = [ self.share.id, self.export_location.fake_path, '--wait' ] verifylist = [ ('share', self.share.id), ('provider_location', self.export_location.fake_path), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.get.assert_called_with(self.share_snapshot.id) self.snapshots_mock.manage.assert_called_with( share=self.share, provider_location=self.export_location.fake_path, driver_options={}, name=None, description=None ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_snapshot_adopt_wait_error(self): arglist = [ self.share.id, self.export_location.fake_path, '--wait' ] verifylist = [ ('share', self.share.id), ('provider_location', self.export_location.fake_path), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_status', return_value=False): columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.get.assert_called_with(self.share_snapshot.id) self.snapshots_mock.manage.assert_called_with( share=self.share, provider_location=self.export_location.fake_path, driver_options={}, name=None, description=None ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestShareSnapshotAbandon(TestShareSnapshot): def setUp(self): super(TestShareSnapshotAbandon, self).setUp() self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot( attrs={'status': 'available'} )) self.snapshots_mock.get.return_value = self.share_snapshot self.cmd = osc_share_snapshots.AbandonShareSnapshot(self.app, None) def test_share_snapshot_abandon_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_snapshot_abandon(self): arglist = [ self.share_snapshot.id ] verifylist = [ ('snapshot', [self.share_snapshot.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.snapshots_mock.unmanage.assert_called_with(self.share_snapshot) self.assertIsNone(result) def test_share_snapshot_abandon_multiple(self): share_snapshots = ( manila_fakes.FakeShareSnapshot.create_share_snapshots( count=2)) arglist = [ share_snapshots[0].id, share_snapshots[1].id ] verifylist = [ ('snapshot', [share_snapshots[0].id, share_snapshots[1].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertEqual(self.snapshots_mock.unmanage.call_count, len(share_snapshots)) self.assertIsNone(result) def test_share_snapshot_abandon_wait(self): arglist = [ self.share_snapshot.id, '--wait' ] verifylist = [ ('snapshot', [self.share_snapshot.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=True): result = self.cmd.take_action(parsed_args) self.snapshots_mock.unmanage.assert_called_with( self.share_snapshot) self.assertIsNone(result) def test_share_snapshot_abandon_wait_error(self): arglist = [ self.share_snapshot.id, '--wait' ] verifylist = [ ('snapshot', [self.share_snapshot.id]), ('wait', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch('osc_lib.utils.wait_for_delete', return_value=False): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareSnapshotAccessAllow(TestShareSnapshot): def setUp(self): super(TestShareSnapshotAccessAllow, self).setUp() self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot()) self.snapshots_mock.get.return_value = self.share_snapshot self.access_rule = ( manila_fakes.FakeSnapshotAccessRule.create_one_access_rule()) self.snapshots_mock.allow.return_value = self.access_rule._info self.cmd = osc_share_snapshots.ShareSnapshotAccessAllow( self.app, None) def test_share_snapshot_access_allow(self): arglist = [ self.share_snapshot.id, 'user', 'demo' ] verifylist = [ ('snapshot', self.share_snapshot.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.snapshots_mock.allow.assert_called_with( snapshot=self.share_snapshot, access_type='user', access_to='demo' ) self.assertEqual(tuple(self.access_rule._info.keys()), columns) self.assertCountEqual(self.access_rule._info.values(), data) def test_share_snapshot_access_allow_exception(self): arglist = [ self.share_snapshot.id, 'user', 'demo' ] verifylist = [ ('snapshot', self.share_snapshot.id), ('access_type', 'user'), ('access_to', 'demo') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.snapshots_mock.allow.side_effect = exceptions.CommandError() self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareSnapshotAccessDeny(TestShareSnapshot): def setUp(self): super(TestShareSnapshotAccessDeny, self).setUp() self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot()) self.snapshots_mock.get.return_value = self.share_snapshot self.access_rule = ( manila_fakes.FakeSnapshotAccessRule.create_one_access_rule()) self.cmd = osc_share_snapshots.ShareSnapshotAccessDeny( self.app, None) def test_share_snapshot_access_deny(self): arglist = [ self.share_snapshot.id, self.access_rule.id, ] verifylist = [ ('snapshot', self.share_snapshot.id), ('id', [self.access_rule.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.snapshots_mock.deny.assert_called_with( snapshot=self.share_snapshot, id=self.access_rule.id ) self.assertIsNone(result) def test_share_snapshot_access_deny_multiple(self): access_rules = ( manila_fakes.FakeSnapshotAccessRule.create_access_rules( count=2)) arglist = [ self.share_snapshot.id, access_rules[0].id, access_rules[1].id, ] verifylist = [ ('snapshot', self.share_snapshot.id), ('id', [access_rules[0].id, access_rules[1].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertEqual(self.snapshots_mock.deny.call_count, len(access_rules)) self.assertIsNone(result) def test_share_snapshot_access_deny_exception(self): arglist = [ self.share_snapshot.id, self.access_rule.id, ] verifylist = [ ('snapshot', self.share_snapshot.id), ('id', [self.access_rule.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.snapshots_mock.deny.side_effect = exceptions.CommandError() self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareSnapshotAccessList(TestShareSnapshot): access_rules_columns = [ 'ID', 'Access Type', 'Access To', 'State', ] def setUp(self): super(TestShareSnapshotAccessList, self).setUp() self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot()) self.snapshots_mock.get.return_value = self.share_snapshot self.access_rules = ( manila_fakes.FakeSnapshotAccessRule.create_access_rules( count=2)) self.snapshots_mock.access_list.return_value = self.access_rules self.cmd = osc_share_snapshots.ShareSnapshotAccessList( self.app, None) self.values = (oscutils.get_dict_properties( a._info, self.access_rules_columns) for a in self.access_rules) def test_share_snapshot_access_list(self): arglist = [ self.share_snapshot.id ] verifylist = [ ('snapshot', self.share_snapshot.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.snapshots_mock.access_list.assert_called_with( self.share_snapshot) self.assertEqual(self.access_rules_columns, columns) self.assertCountEqual(self.values, data) class TestShareSnapshotExportLocationList(TestShareSnapshot): columns = ["ID", "Path"] def setUp(self): super(TestShareSnapshotExportLocationList, self).setUp() self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot()) self.snapshots_mock.get.return_value = self.share_snapshot self.export_locations = ( manila_fakes.FakeSnapshotExportLocation.create_export_locations() ) self.export_locations_mock.list.return_value = self.export_locations self.values = (oscutils.get_dict_properties( e._info, self.columns) for e in self.export_locations) self.cmd = osc_share_snapshots.ShareSnapshotListExportLocation( self.app, None) def test_snapshot_export_locations_list(self): arglist = [ self.share_snapshot.id ] verifylist = [ ('snapshot', self.share_snapshot.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.export_locations_mock.list.assert_called_with( snapshot=self.share_snapshot) self.assertEqual(self.columns, columns) self.assertCountEqual(self.values, data) class TestShareSnapshotExportLocationShow(TestShareSnapshot): def setUp(self): super(TestShareSnapshotExportLocationShow, self).setUp() self.share_snapshot = ( manila_fakes.FakeShareSnapshot.create_one_snapshot()) self.snapshots_mock.get.return_value = self.share_snapshot self.export_location = ( manila_fakes.FakeSnapshotExportLocation.create_one_export_location() # noqa E501 ) self.export_locations_mock.get.return_value = self.export_location self.cmd = osc_share_snapshots.ShareSnapshotShowExportLocation( self.app, None) def test_snapshot_export_locations_list(self): arglist = [ self.share_snapshot.id, self.export_location.id ] verifylist = [ ('snapshot', self.share_snapshot.id), ('export_location', self.export_location.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.export_locations_mock.get.assert_called_with( export_location=self.export_location.id, snapshot=self.share_snapshot) self.assertEqual(tuple(self.export_location._info.keys()), columns) self.assertCountEqual(self.export_location._info.values(), data) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_transfers.py0000664000175000017500000002135200000000000030226 0ustar00zuulzuul00000000000000# Copyright (c) 2022 China Telecom Digital Intelligence. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.osc.v2 import share_transfers as osc_share_transfers from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes COLUMNS = [ 'ID', 'Name', 'Resource Type', 'Resource Id', 'Created At', 'Source Project Id', 'Destination Project Id', 'Accepted', 'Expires At' ] class TestShareTransfer(manila_fakes.TestShare): def setUp(self): super(TestShareTransfer, self).setUp() self.shares_mock = self.app.client_manager.share.shares self.shares_mock.reset_mock() self.transfers_mock = self.app.client_manager.share.transfers self.transfers_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION) class TestShareTransferCreate(TestShareTransfer): def setUp(self): super(TestShareTransferCreate, self).setUp() self.share = manila_fakes.FakeShare.create_one_share() self.shares_mock.create.return_value = self.share self.shares_mock.get.return_value = self.share self.share_transfer = ( manila_fakes.FakeShareTransfer.create_one_transfer()) self.transfers_mock.get.return_value = self.share_transfer self.transfers_mock.create.return_value = self.share_transfer self.cmd = osc_share_transfers.CreateShareTransfer(self.app, None) self.data = tuple(self.share_transfer._info.values()) self.columns = tuple(self.share_transfer._info.keys()) def test_share_transfer_create_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_transfer_create_required_args(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.transfers_mock.create.assert_called_with( self.share.id, name=None ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestShareTransferDelete(TestShareTransfer): def setUp(self): super(TestShareTransferDelete, self).setUp() self.transfer = ( manila_fakes.FakeShareTransfer.create_one_transfer()) self.transfers_mock.get.return_value = self.transfer self.cmd = osc_share_transfers.DeleteShareTransfer(self.app, None) def test_share_transfer_delete_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_transfer_delete(self): arglist = [ self.transfer.id ] verifylist = [ ('transfer', [self.transfer.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.transfers_mock.delete.assert_called_with(self.transfer.id) self.assertIsNone(result) def test_share_transfer_delete_multiple(self): transfers = ( manila_fakes.FakeShareTransfer.create_share_transfers( count=2)) arglist = [ transfers[0].id, transfers[1].id ] verifylist = [ ('transfer', [transfers[0].id, transfers[1].id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertEqual(self.transfers_mock.delete.call_count, len(transfers)) self.assertIsNone(result) def test_share_transfer_delete_exception(self): arglist = [ self.transfer.id ] verifylist = [ ('transfer', [self.transfer.id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.transfers_mock.delete.side_effect = exceptions.CommandError() self.assertRaises(exceptions.CommandError, self.cmd.take_action, parsed_args) class TestShareTransferShow(TestShareTransfer): def setUp(self): super(TestShareTransferShow, self).setUp() self.transfer = ( manila_fakes.FakeShareTransfer.create_one_transfer()) self.transfers_mock.get.return_value = self.transfer self.cmd = osc_share_transfers.ShowShareTransfer(self.app, None) self.data = self.transfer._info.values() self.transfer._info.pop('auth_key') self.columns = self.transfer._info.keys() def test_share_transfer_show_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_transfer_show(self): arglist = [ self.transfer.id ] verifylist = [ ('transfer', self.transfer.id) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.transfers_mock.get.assert_called_with(self.transfer.id) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) class TestShareTransferList(TestShareTransfer): def setUp(self): super(TestShareTransferList, self).setUp() self.transfers = ( manila_fakes.FakeShareTransfer.create_share_transfers( count=2)) self.transfers_mock.list.return_value = self.transfers self.values = (oscutils.get_dict_properties( m._info, COLUMNS) for m in self.transfers) self.cmd = osc_share_transfers.ListShareTransfer(self.app, None) def test_list_transfers(self): arglist = [ '--detailed' ] verifylist = [ ('detailed', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.transfers_mock.list.assert_called_with( detailed=1, search_opts={ 'all_tenants': False, 'id': None, 'name': None, 'limit': None, 'offset': None, 'resource_type': None, 'resource_id': None, 'source_project_id': None}, sort_key=None, sort_dir=None ) self.assertEqual(COLUMNS, columns) self.assertEqual(list(self.values), list(data)) class TestShareTransferAccept(TestShareTransfer): def setUp(self): super(TestShareTransferAccept, self).setUp() self.transfer = ( manila_fakes.FakeShareTransfer.create_one_transfer()) self.transfers_mock.get.return_value = self.transfer self.cmd = osc_share_transfers.AcceptShareTransfer(self.app, None) def test_share_transfer_accept_missing_args(self): arglist = [] verifylist = [] self.assertRaises(osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist) def test_share_transfer_accept(self): arglist = [ self.transfer.id, self.transfer.auth_key ] verifylist = [ ('transfer', self.transfer.id), ('auth_key', self.transfer.auth_key) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.transfers_mock.accept.assert_called_with(self.transfer.id, self.transfer.auth_key, clear_access_rules=False) self.assertIsNone(result) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_type.py0000664000175000017500000005040700000000000027203 0ustar00zuulzuul00000000000000# 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 unittest import mock 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 import utils 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( api_versions.MAX_VERSION) 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, ] self.raw_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', 'mount_snapshot_support': False, 'revert_to_snapshot_support': False, 'create_share_from_snapshot_support': True, '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_json_fomrat(self): """Verifies --format json.""" arglist = [ self.new_share_type.name, 'True', '-f', 'json' ] 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.raw_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(mock.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() columns = utils.format_column_headers(COLUMNS) 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(self.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(self.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(self.columns, columns) self.assertEqual(list(self.values), 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) 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, ] self.raw_data = [ self.share_type.id, self.share_type.name, 'public', self.share_type.is_default, {'driver_handles_share_servers': True}, {'replication_type': 'readable', 'mount_snapshot_support': False, 'revert_to_snapshot_support': False, 'create_share_from_snapshot_support': True, '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) def test_share_type_show_json_format(self): arglist = [ self.share_type.id, '-f', 'json', ] 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.raw_data, data) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/osc/v2/test_share_type_access.py0000664000175000017500000001432500000000000030523 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/test_api_versions.py0000664000175000017500000003507300000000000026430 0ustar00zuulzuul00000000000000# 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. from unittest import mock import ddt 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(str(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")) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/test_base.py0000664000175000017500000000525600000000000024641 0ustar00zuulzuul00000000000000# 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 unittest import mock from manilaclient import 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 = 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 = base.Resource(None, {'id': 1, 'name': 'hi'}) r2 = base.Resource(None, {'id': 1, 'name': 'hello'}) self.assertTrue(r1 == r2) self.assertFalse(r1 != r2) # Two resources of different types: never equal r1 = 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 = base.Resource(None, {'name': 'joe', 'age': 12}) r2 = 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_with( search_opts={'all_tenants': 1, 'is_soft_deleted': True}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/test_client.py0000664000175000017500000001227100000000000025200 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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), ]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/test_functional_utils.py0000664000175000017500000001711700000000000027310 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/test_shell.py0000664000175000017500000004252000000000000025031 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import io import re import sys from unittest import mock import ddt import fixtures from tempest.lib.cli import output_parser from testtools import matchers import manilaclient from manilaclient.common import cliutils from manilaclient.common import constants from manilaclient import exceptions from manilaclient import shell from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes @ddt.ddt class OpenstackManilaShellTest(utils.TestCase): FAKE_ENV = { 'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', 'OS_TENANT_NAME': 'tenant_name', 'OS_AUTH_URL': 'http://no.where', } # Patch os.environ to avoid required auth info. def set_env_vars(self, env_vars): for k, v in env_vars.items(): self.useFixture(fixtures.EnvironmentVariable(k, v)) def shell_discover_client(self, current_client, os_api_version, os_endpoint_type, os_service_type, client_args): return current_client, manilaclient.API_MAX_VERSION def shell(self, argstr): orig = sys.stdout try: sys.stdout = io.StringIO() _shell = shell.OpenStackManilaShell() _shell._discover_client = self.shell_discover_client _shell.main(argstr.split()) except SystemExit: exc_type, exc_value, exc_traceback = sys.exc_info() self.assertEqual(exc_value.code, 0) finally: out = sys.stdout.getvalue() sys.stdout.close() sys.stdout = orig return out @ddt.data( {}, {'OS_AUTH_URL': 'http://foo.bar'}, {'OS_AUTH_URL': 'http://foo.bar', 'OS_USERNAME': 'foo'}, {'OS_AUTH_URL': 'http://foo.bar', 'OS_USERNAME': 'foo_user', 'OS_PASSWORD': 'foo_password'}, {'OS_TENANT_NAME': 'foo_tenant', 'OS_USERNAME': 'foo_user', 'OS_PASSWORD': 'foo_password'}, {'OS_TOKEN': 'foo_token'}, {'OS_MANILA_BYPASS_URL': 'http://foo.foo'}, ) def test_main_failure(self, env_vars): self.set_env_vars(env_vars) with mock.patch.object(shell, 'client') as mock_client: self.assertRaises(exceptions.CommandError, self.shell, 'list') self.assertFalse(mock_client.Client.called) @ddt.data(None, 'foo_key') def test_main_success(self, os_key): env_vars = { 'OS_AUTH_URL': 'http://foo.bar', 'OS_USERNAME': 'foo_username', 'OS_USER_ID': 'foo_user_id', 'OS_PASSWORD': 'foo_password', 'OS_TENANT_NAME': 'foo_tenant', 'OS_TENANT_ID': 'foo_tenant_id', 'OS_PROJECT_NAME': 'foo_project', 'OS_PROJECT_ID': 'foo_project_id', 'OS_PROJECT_DOMAIN_ID': 'foo_project_domain_id', 'OS_PROJECT_DOMAIN_NAME': 'foo_project_domain_name', 'OS_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', 'OS_KEY': os_key, } self.set_env_vars(env_vars) cert = env_vars['OS_CERT'] if os_key: cert = (cert, env_vars['OS_KEY']) with mock.patch.object(shell, 'client') as mock_client: self.shell('list') mock_client.Client.assert_called_with( manilaclient.API_MAX_VERSION, username=env_vars['OS_USERNAME'], password=env_vars['OS_PASSWORD'], project_name=env_vars['OS_PROJECT_NAME'], auth_url=env_vars['OS_AUTH_URL'], insecure=False, region_name='', tenant_id=env_vars['OS_PROJECT_ID'], endpoint_type='publicURL', extensions=mock.ANY, service_type=constants.V2_SERVICE_TYPE, service_name='', retries=0, http_log_debug=False, cacert=None, use_keyring=False, force_new_token=False, user_id=env_vars['OS_USER_ID'], user_domain_id=env_vars['OS_USER_DOMAIN_ID'], user_domain_name=env_vars['OS_USER_DOMAIN_NAME'], project_domain_id=env_vars['OS_PROJECT_DOMAIN_ID'], project_domain_name=env_vars['OS_PROJECT_DOMAIN_NAME'], cert=cert, input_auth_token='', service_catalog_url='', ) @ddt.data( {"env_vars": {"OS_MANILA_BYPASS_URL": "http://foo.url", "OS_TOKEN": "foo_token"}, "kwargs": {"--os-token": "bar_token", "--bypass-url": "http://bar.url"}, "expected": {"input_auth_token": "bar_token", "service_catalog_url": "http://bar.url"}}, {"env_vars": {"OS_MANILA_BYPASS_URL": "http://foo.url", "OS_TOKEN": "foo_token"}, "kwargs": {}, "expected": {"input_auth_token": "foo_token", "service_catalog_url": "http://foo.url"}}, {"env_vars": {}, "kwargs": {"--os-token": "bar_token", "--bypass-url": "http://bar.url"}, "expected": {"input_auth_token": "bar_token", "service_catalog_url": "http://bar.url"}}, {"env_vars": {"MANILACLIENT_BYPASS_URL": "http://foo.url", "OS_TOKEN": "foo_token"}, "kwargs": {}, "expected": {"input_auth_token": "foo_token", "service_catalog_url": "http://foo.url"}}, {"env_vars": {"OS_TOKEN": "foo_token"}, "kwargs": {"--bypass-url": "http://bar.url"}, "expected": {"input_auth_token": "foo_token", "service_catalog_url": "http://bar.url"}}, {"env_vars": {"MANILACLIENT_BYPASS_URL": "http://foo.url", "OS_MANILA_BYPASS_URL": "http://bar.url", "OS_TOKEN": "foo_token"}, "kwargs": {"--os-token": "bar_token"}, "expected": {"input_auth_token": "bar_token", "service_catalog_url": "http://bar.url"}}, ) @ddt.unpack def test_main_success_with_token(self, env_vars, kwargs, expected): self.set_env_vars(env_vars) with mock.patch.object(shell, "client") as mock_client: cmd = "" for k, v in kwargs.items(): cmd += "%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=None, input_auth_token=expected["input_auth_token"], service_catalog_url=expected["service_catalog_url"], ) @ddt.data( # default without any env var or kwargs { "env_vars": {"OS_TOKEN": "foo_token", "OS_MANILA_BYPASS_URL": "http://bar.url"}, "kwargs": {}, "expected": {"input_auth_token": "foo_token", "service_catalog_url": "http://bar.url", "os_endpoint_type": "publicURL"} }, # only env var { "env_vars": {"OS_TOKEN": "foo_token", "OS_MANILA_BYPASS_URL": "http://bar.url", "OS_MANILA_ENDPOINT_TYPE": "custom-endpoint-type"}, "kwargs": {}, "expected": {"input_auth_token": "foo_token", "service_catalog_url": "http://bar.url", "os_endpoint_type": "custom-endpoint-type"}, }, # only kwargs { "env_vars": {"OS_TOKEN": "foo_token", "OS_MANILA_BYPASS_URL": "http://bar.url"}, "kwargs": {"--endpoint-type": "custom-kwargs-endpoint-type"}, "expected": {"input_auth_token": "foo_token", "service_catalog_url": "http://bar.url", "os_endpoint_type": "custom-kwargs-endpoint-type"}, }, # env var *and* kwargs (kwargs should win) { "env_vars": {"OS_TOKEN": "foo_token", "OS_MANILA_BYPASS_URL": "http://bar.url", "os_endpoint_type": "custom-env-endpoint-type"}, "kwargs": {"--endpoint-type": "custom-kwargs-endpoint-type"}, "expected": {"input_auth_token": "foo_token", "service_catalog_url": "http://bar.url", "os_endpoint_type": "custom-kwargs-endpoint-type"}, } ) @ddt.unpack def test_main_success_with_os_endpoint(self, env_vars, kwargs, expected): self.set_env_vars(env_vars) with mock.patch.object(shell, "client") as mock_client: cmd = "" for k, v in kwargs.items(): cmd += "%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=None, input_auth_token=expected["input_auth_token"], service_catalog_url=expected["service_catalog_url"], ) def test_help_unknown_command(self): self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo') @ddt.data('list --help', '--help list', 'help list') def test_help_on_subcommand(self, cmd): required = [ '.*?^usage: manila list', '.*?^List NAS shares with filters.', ] help_text = self.shell(cmd) for r in required: self.assertThat(help_text, matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) def test_common_args_in_help_message(self): expected_args = ( '--version', '', '--debug', '--os-cache', '--os-reset-cache', '--os-user-id', '--os-username', '--os-password', '--os-tenant-name', '--os-project-name', '--os-tenant-id', '--os-project-id', '--os-user-domain-id', '--os-user-domain-name', '--os-project-domain-id', '--os-project-domain-name', '--os-auth-url', '--os-region-name', '--service-type', '--service-name', '--share-service-name', '--endpoint-type', '--os-share-api-version', '--os-cacert', '--retries', '--os-cert', '--os-key', ) help_text = self.shell('help') for expected_arg in expected_args: self.assertIn(expected_arg, help_text) class CustomOpenStackManilaShell(shell.OpenStackManilaShell): @staticmethod @cliutils.arg( '--default-is-none', '--default_is_none', type=str, metavar='', action='single_alias', help='Default value is None and metavar set.', default=None) def do_foo(cs, args): cliutils.print_dict({'key': args.default_is_none}) @staticmethod @cliutils.arg( '--default-is-not-none', '--default_is_not_none', type=str, action='single_alias', help='Default value is not None and metavar not set.', default='bar') def do_bar(cs, args): cliutils.print_dict({'key': args.default_is_not_none}) @staticmethod @cliutils.arg( '--list-like', '--list_like', nargs='*', action='single_alias', help='Default value is None, metavar not set and result is list.', default=None) def do_quuz(cs, args): cliutils.print_dict({'key': args.list_like}) @ddt.ddt class AllowOnlyOneAliasAtATimeActionTest(utils.TestCase): FAKE_ENV = { 'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', 'OS_TENANT_NAME': 'tenant_name', 'OS_AUTH_URL': 'http://no.where', } def setUp(self): super(self.__class__, self).setUp() for k, v in self.FAKE_ENV.items(): self.useFixture(fixtures.EnvironmentVariable(k, v)) self.mock_object( shell.client, 'get_client_class', mock.Mock(return_value=fakes.FakeClient)) def shell_discover_client(self, current_client, os_api_version, os_endpoint_type, os_service_type, client_args): return current_client, manilaclient.API_MAX_VERSION def shell(self, argstr): orig = sys.stdout try: sys.stdout = io.StringIO() _shell = CustomOpenStackManilaShell() _shell._discover_client = self.shell_discover_client _shell.main(argstr.split()) except SystemExit: exc_type, exc_value, exc_traceback = sys.exc_info() self.assertEqual(exc_value.code, 0) finally: out = sys.stdout.getvalue() sys.stdout.close() sys.stdout = orig return out @ddt.data( ('--default-is-none foo', 'foo'), ('--default-is-none foo --default-is-none foo', 'foo'), ('--default-is-none foo --default_is_none foo', 'foo'), ('--default_is_none None', 'None'), ) @ddt.unpack def test_foo_success(self, options_str, expected_result): output = self.shell('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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/utils.py0000664000175000017500000000604000000000000024020 0ustar00zuulzuul00000000000000# 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 from unittest import mock import fixtures 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 def mock_completion(self): patcher = mock.patch( 'manilaclient.base.Manager.write_to_completion_cache') patcher.start() self.addCleanup(patcher.stop) patcher = mock.patch('manilaclient.base.Manager.completion_cache') patcher.start() self.addCleanup(patcher.stop) 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5852752 python-manilaclient-4.8.0/manilaclient/tests/unit/v1/0000775000175000017500000000000000000000000022634 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v1/test_limits.py0000664000175000017500000000224300000000000025547 0ustar00zuulzuul00000000000000# 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.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") % str(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v1/test_quota_classes.py0000664000175000017500000000227400000000000027120 0ustar00zuulzuul00000000000000# 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.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") % str(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v1/test_quotas.py0000664000175000017500000000221100000000000025555 0ustar00zuulzuul00000000000000# 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.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") % str(e) assert False, msg for cls in ('QuotaSet', 'QuotaSetManager'): msg = "Module 'quotas' has no '%s' attr." % cls self.assertTrue(hasattr(quotas, cls), msg) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v1/test_scheduler_stats.py0000664000175000017500000000226600000000000027447 0ustar00zuulzuul00000000000000# 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.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") % str(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v1/test_security_services.py0000664000175000017500000000233000000000000030015 0ustar00zuulzuul00000000000000# 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.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") % str(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v1/test_services.py0000664000175000017500000000222300000000000026067 0ustar00zuulzuul00000000000000# 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.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") % str(e) assert False, msg for cls in ('Service', 'ServiceManager'): msg = "Module 'services' has no '%s' attr." % cls self.assertTrue(hasattr(services, cls), msg) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v1/test_share_networks.py0000664000175000017500000000230000000000000027276 0ustar00zuulzuul00000000000000# 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.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") % str(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v1/test_share_servers.py0000664000175000017500000000227000000000000027121 0ustar00zuulzuul00000000000000# 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.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") % str(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v1/test_share_snapshots.py0000664000175000017500000000231000000000000027445 0ustar00zuulzuul00000000000000# 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.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") % str(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v1/test_share_type_access.py0000664000175000017500000000232700000000000027735 0ustar00zuulzuul00000000000000# 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.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") % str(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v1/test_share_types.py0000664000175000017500000000225000000000000026572 0ustar00zuulzuul00000000000000# 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.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") % str(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v1/test_shares.py0000664000175000017500000000220300000000000025527 0ustar00zuulzuul00000000000000# 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.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") % str(e) assert False, msg for cls in ('Share', 'ShareManager'): msg = "Module 'shares' has no '%s' attr." % cls self.assertTrue(hasattr(shares, cls), msg) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5932772 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/0000775000175000017500000000000000000000000022635 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/__init__.py0000664000175000017500000000000000000000000024734 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/fake_clients.py0000664000175000017500000001535100000000000025643 0ustar00zuulzuul00000000000000# 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 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, }) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/fakes.py0000664000175000017500000013425100000000000024306 0ustar00zuulzuul00000000000000# 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. 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', 'status': 'available', }, } 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"}, ], }, ], } if kw.get('with_count'): shares.update({'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', }]} if kw.get('with_count'): snapshots.update({'count': 2}) 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] elif action in ( 'migration_cancel', 'migration_complete', 'migration_get_progress'): assert body[action] is None if 'migration_get_progress' == action: _body = {'total_progress': 50, 'task_state': 'fake_task_state', 'destination_share_server_id': 'fake_dest_id'} return 200, {}, _body elif 'migration_complete' == action: _body = {'destination_share_server_id': 'fake_dest_id'} return 200, {}, _body elif action in ( 'migration_start', 'migration_check'): assert 'host' in body[action] if 'migration-check': _body = { 'compatible': True, 'capacity': True, 'capability': True, 'writable': True, 'nondisruptive': True, 'preserve_snapshots': True, } return 200, {}, _body elif action == 'reset_task_state': assert 'task_state' 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] elif action in ('soft_delete', 'restore'): assert body[action] is None 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': {'id': '1234', 'status': 'creating'}}) def post_snapshots(self, **kwargs): return (202, {}, {'snapshot': {}}) def delete_shares_1234(self, **kw): return (202, {}, None) def delete_shares_share_abc(self, **kw): return (202, {}, None) def delete_shares_share_xyz(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'): assert body[action] is None else: if action not in ('promote'): 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 delete_snapshots_1234_metadata_test_key(self, **kw): return (204, {}, None) def delete_snapshots_1234_metadata_key1(self, **kw): return (204, {}, None) def delete_snapshots_1234_metadata_key2(self, **kw): return (204, {}, None) def post_snapshots_1234_metadata(self, **kw): return (204, {}, {'metadata': {'test_key': 'test_value'}}) def put_snapshots_1234_metadata(self, **kw): return (200, {}, {"metadata": {"key1": "val1", "key2": "val2"}}) def get_snapshots_1234_metadata(self, **kw): return (200, {}, {"metadata": {"key1": "val1", "key2": "val2"}}) 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', } fake_transfer = { "id": "f21c72c4-2b77-445b-aa12-e8d1b44163a2", "created_at": "2022-09-06T08:17:43.629495", "name": "test_transfer", "resource_type": "share", "resource_id": "29476819-28a9-4b1a-a21d-3b2d203025a0", "auth_key": "406a2d67cdb09afe", "source_project_id": "714198c7ac5e45a4b785de732ea4695d", "destination_project_id": None, "accepted": False, "expires_at": None, } 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 post_share_transfers(self, **kw): transfer = {'transfer': self.fake_transfer} return 202, {}, transfer def get_share_transfers_5678(self, **kw): transfer = {'transfer': self.fake_transfer} return 202, {}, transfer def get_share_transfers_detail(self, **kw): transfer = {'transfers': [self.fake_transfer]} return 202, {}, transfer def delete_share_transfers_5678(self, **kw): return 202, {}, None def post_share_transfers_5678_accept(self, **kw): transfer = {'transfer': self.fake_transfer} return 202, {}, transfer 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' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_availability_zones.py0000664000175000017500000000412100000000000030134 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_client.py0000664000175000017500000003254500000000000025535 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt from oslo_utils import uuidutils import manilaclient from manilaclient import exceptions from manilaclient.tests.unit import utils from manilaclient.v2 import client @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, cert=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, cert=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', 'password': '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, cert=client_args['cert'], 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'), 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', 'password': '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, cert=client_args['cert'], 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'), 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_limits.py0000664000175000017500000001252200000000000025551 0ustar00zuulzuul00000000000000# 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. from unittest import mock import ddt 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 li in lim.absolute: self.assertEqual(l1, li) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_messages.py0000664000175000017500000001042700000000000026061 0ustar00zuulzuul00000000000000# 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. from unittest import mock import ddt 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 = str(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_quota_classes.py0000664000175000017500000000533400000000000027121 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_quotas.py0000664000175000017500000002746500000000000025600 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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'] if (api_versions.APIVersion(microversion) >= api_versions.APIVersion('2.62')): expected_body['quota_set']['per_share_gigabytes'] = 10 kwargs['per_share_gigabytes'] = expected_body['quota_set'][ 'per_share_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} if microversion >= '2.62': expected_body['quota_set']['per_share_gigabytes'] = 10 kwargs = {'per_share_gigabytes': 10} 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() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_scheduler_stats.py0000664000175000017500000000562200000000000027447 0ustar00zuulzuul00000000000000# 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 unittest 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_security_services.py0000664000175000017500000002054100000000000030022 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt from manilaclient import exceptions from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import security_services @ddt.ddt 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) @ddt.data({'server': 'fake.ad.server'}, {'default_ad_site': 'fake.ad.default_ad_site'}) def test_create_all_fields_active_directory(self, option): values = { 'type': 'active_directory', 'dns_ip': 'fake dns ip', 'ou': 'fake ou', 'domain': 'fake.ad.domain', 'user': 'fake user', 'password': 'fake password', 'name': 'fake name', 'description': 'fake description', } values.update(option) 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_services.py0000664000175000017500000000725600000000000026103 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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}, ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_backups.py0000664000175000017500000000702400000000000027063 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt from manilaclient import api_versions from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import share_backups FAKE_BACKUP = 'fake_backup' @ddt.ddt class ShareBackupsTest(utils.TestCase): class _FakeShareBackup(object): id = 'fake_share_backup_id' def setUp(self): super(ShareBackupsTest, self).setUp() microversion = api_versions.APIVersion("2.80") self.manager = share_backups.ShareBackupManager( fakes.FakeClient(api_version=microversion)) def test_delete_str(self): with mock.patch.object(self.manager, '_delete', mock.Mock()): self.manager.delete(FAKE_BACKUP) self.manager._delete.assert_called_once_with( share_backups.RESOURCE_PATH % FAKE_BACKUP) def test_delete_obj(self): backup = self._FakeShareBackup with mock.patch.object(self.manager, '_delete', mock.Mock()): self.manager.delete(backup) self.manager._delete.assert_called_once_with( share_backups.RESOURCE_PATH % backup.id) def test_get(self): with mock.patch.object(self.manager, '_get', mock.Mock()): self.manager.get(FAKE_BACKUP) self.manager._get.assert_called_once_with( share_backups.RESOURCE_PATH % FAKE_BACKUP, share_backups.RESOURCE_NAME) def test_restore(self): with mock.patch.object(self.manager, '_action', mock.Mock()): self.manager.restore(FAKE_BACKUP) self.manager._action.assert_called_once_with( 'restore', FAKE_BACKUP) def test_list(self): with mock.patch.object(self.manager, '_list', mock.Mock()): self.manager.list() self.manager._list.assert_called_once_with( share_backups.RESOURCES_PATH + '/detail', share_backups.RESOURCES_NAME) def test_list_with_share(self): with mock.patch.object(self.manager, '_list', mock.Mock()): self.manager.list(search_opts={'share_id': 'fake_share_id'}) share_uri = '?share_id=fake_share_id' self.manager._list.assert_called_once_with( (share_backups.RESOURCES_PATH + '/detail' + share_uri), share_backups.RESOURCES_NAME) def test_reset_state(self): with mock.patch.object(self.manager, '_action', mock.Mock()): self.manager.reset_status(FAKE_BACKUP, 'fake_status') self.manager._action.assert_called_once_with( 'reset_status', FAKE_BACKUP, {'status': 'fake_status'}) def test_update(self): backup = self._FakeShareBackup with mock.patch.object(self.manager, '_update', mock.Mock()): data = dict(name='backup1') self.manager.update(backup, **data) self.manager._update.assert_called_once_with( share_backups.RESOURCE_PATH % backup.id, {'share_backup': data}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_export_locations.py0000664000175000017500000000363100000000000031027 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_group_snapshots.py0000664000175000017500000002333300000000000030672 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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 = str(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'}}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_group_type_access.py0000664000175000017500000000776300000000000031163 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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 = { 'share_group_type_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 = str(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_group_types.py0000664000175000017500000001735300000000000030021 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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 = str(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_groups.py0000664000175000017500000002654100000000000026757 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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 = str(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'}}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_instance_export_locations.py0000664000175000017500000000413600000000000032714 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_instances.py0000664000175000017500000001052400000000000027421 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_network_subnets.py0000664000175000017500000000577500000000000030702 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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', 'metadata': 'fake_metadata' } 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 }) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_networks.py0000664000175000017500000002677300000000000027323 0ustar00zuulzuul00000000000000# 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 itertools from unittest import mock import ddt 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 = 'add_security_service' expected_body = { 'security_service_id': security_service, } with mock.patch.object(self.manager, '_action', mock.Mock()): self.manager.add_security_service(share_nw, security_service) self.manager._action.assert_called_once_with( expected_path, share_nw, expected_body) def test_add_security_service_to_share_nw_object(self): security_service = self._FakeSecurityService() share_nw = self._FakeShareNetwork() expected_path = 'add_security_service' expected_body = { 'security_service_id': security_service.id, } with mock.patch.object(self.manager, '_action', mock.Mock()): self.manager.add_security_service(share_nw, security_service) self.manager._action.assert_called_once_with( expected_path, share_nw, expected_body) 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) def test_update_share_network_security_service(self): current_security_service = self._FakeSecurityService() new_security_service = self._FakeSecurityService() share_nw = self._FakeShareNetwork() expected_path = 'update_security_service' expected_body = { 'current_service_id': current_security_service.id, 'new_service_id': new_security_service.id } with mock.patch.object(self.manager, '_action', mock.Mock()): self.manager.update_share_network_security_service( share_nw, current_security_service, new_security_service) self.manager._action.assert_called_once_with( expected_path, share_nw, expected_body) def test_share_network_reset_state(self): share_nw = self._FakeShareNetwork() state = 'active' expected_path = 'reset_status' expected_body = { 'status': state, } with mock.patch.object(self.manager, '_action', mock.Mock()): self.manager.reset_state( share_nw, state) self.manager._action.assert_called_once_with( expected_path, share_nw, expected_body) def test_share_network_security_service_update_check(self): current_security_service = self._FakeSecurityService() new_security_service = self._FakeSecurityService() share_nw = self._FakeShareNetwork() expected_path = 'update_security_service_check' expected_body = { 'current_service_id': current_security_service.id, 'new_service_id': new_security_service.id, 'reset_operation': False } with mock.patch.object(self.manager, '_action', mock.Mock()): self.manager.update_share_network_security_service_check( share_nw, current_security_service, new_security_service) self.manager._action.assert_called_once_with( expected_path, share_nw, expected_body) def test_add_security_service_check(self): current_security_service = self._FakeSecurityService() share_nw = self._FakeShareNetwork() expected_path = 'add_security_service_check' expected_body = { 'security_service_id': current_security_service.id, 'reset_operation': False } with mock.patch.object(self.manager, '_action', mock.Mock()): self.manager.add_security_service_check( share_nw, current_security_service, False) self.manager._action.assert_called_once_with( expected_path, share_nw, expected_body) def test_share_network_subnet_create_check(self): share_nw = self._FakeShareNetwork() expected_path = 'share_network_subnet_create_check' expected_body = { 'neutron_net_id': self.values['neutron_net_id'], 'neutron_subnet_id': self.values['neutron_subnet_id'], 'reset_operation': False } with mock.patch.object(self.manager, '_action', mock.Mock()): self.manager.share_network_subnet_create_check( share_nw, self.values['neutron_net_id'], self.values['neutron_subnet_id']) self.manager._action.assert_called_once_with( expected_path, share_nw, expected_body) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_replica_export_locations.py0000664000175000017500000000354600000000000032533 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_replicas.py0000664000175000017500000001333300000000000027235 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt from manilaclient import api_versions from manilaclient.common import constants 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.67") self.manager = share_replicas.ShareReplicaManager( fakes.FakeClient(api_version=microversion)) @ddt.data("2.11", constants.REPLICA_PRE_GRADUATION_VERSION, constants.REPLICA_GRADUATION_VERSION) def test_create(self, microversion): api_version = api_versions.APIVersion(microversion) values = { 'availability_zone': 'az1', 'share': 's1', } manager = share_replicas.ShareReplicaManager( fakes.FakeClient(api_version=api_version)) with mock.patch.object(manager, '_create', fakes.fake_create): result = 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']) @ddt.data("2.72") def test_create_with_share_network(self, microversion): api_version = api_versions.APIVersion(microversion) values = { 'availability_zone': 'az1', 'share': 's1', 'share_network': 'sn1', } manager = share_replicas.ShareReplicaManager( fakes.FakeClient(api_version=api_version)) with mock.patch.object(manager, '_create', fakes.fake_create): result = manager.create(**values) values['share_id'] = values.pop('share') values['share_network_id'] = values.pop('share_network') 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'}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_servers.py0000664000175000017500000002705700000000000027134 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt from manilaclient import 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(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') def test_migration_check(self): share_server = "fake_share_server" host = "fake_host" returned = { 'compatible': True, 'capacity': True, 'capability': True, 'writable': True, 'nondisruptive': True, 'preserve_snapshots': True, } with mock.patch.object(self.manager, "_action", mock.Mock(return_value=['200', returned])): result = self.manager.migration_check( share_server, host, writable=True, nondisruptive=True, preserve_snapshots=True) self.manager._action.assert_called_once_with( 'migration_check', share_server, { "host": host, "writable": True, "nondisruptive": True, "preserve_snapshots": True, "new_share_network_id": None, }) self.assertEqual(returned, result) def test_migration_start(self): share_server = "fake_share_server" host = "fake_host" returned = "fake" with mock.patch.object(self.manager, "_action", mock.Mock(return_value=returned)): result = self.manager.migration_start( share_server, host, writable=True, nondisruptive=True, preserve_snapshots=True) self.manager._action.assert_called_once_with( 'migration_start', share_server, { "host": host, "writable": True, "nondisruptive": True, "preserve_snapshots": True, "new_share_network_id": None, }) self.assertEqual(returned, result) def test_migration_complete(self): share_server = "fake_share_server" returned = "fake" with mock.patch.object(self.manager, "_action", mock.Mock(return_value=['200', returned])): result = self.manager.migration_complete(share_server) self.manager._action.assert_called_once_with( "migration_complete", share_server) self.assertEqual(returned, result) def test_migration_get_progress(self): share_server = "fake_share_server" returned = "fake" with mock.patch.object(self.manager, "_action", mock.Mock(return_value=['200', returned])): result = self.manager.migration_get_progress(share_server) self.manager._action.assert_called_once_with( "migration_get_progress", share_server) self.assertEqual(returned, result) def test_reset_task_state(self): share_server = "fake_share_server" state = "fake_state" returned = "fake" with mock.patch.object(self.manager, "_action", mock.Mock(return_value=returned)): result = self.manager.reset_task_state(share_server, state) self.manager._action.assert_called_once_with( "reset_task_state", share_server, {'task_state': state}) self.assertEqual(returned, result) def test_migration_cancel(self): share_server = "fake_share_server" returned = "fake" with mock.patch.object(self.manager, "_action", mock.Mock(return_value=returned)): result = self.manager.migration_cancel(share_server) self.manager._action.assert_called_once_with( "migration_cancel", share_server) self.assertEqual(returned, result) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_snapshot_export_locations.py0000664000175000017500000000316500000000000032750 0ustar00zuulzuul00000000000000# 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}) ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_snapshot_instance_export_locations.py 22 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_snapshot_instance_export_locations.p0000664000175000017500000000344700000000000034446 0ustar00zuulzuul00000000000000# 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}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_snapshot_instances.py0000664000175000017500000000703400000000000031342 0ustar00zuulzuul00000000000000# 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 unittest import mock import ddt 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_snapshots.py0000664000175000017500000002337000000000000027457 0ustar00zuulzuul00000000000000# 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. from unittest import mock import ddt 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_list_share_snapshots_detail_with_count(self): search_opts = { 'with_count': 'True', } snapshots, count = cs.share_snapshots.list(detailed=True, search_opts=search_opts) cs.assert_called( 'GET', '/snapshots/detail?with_count=True') self.assertEqual(2, count) self.assertEqual(1, len(snapshots)) 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') def test_get_metadata(self): cs.share_snapshots.get_metadata(1234) cs.assert_called('GET', '/snapshots/1234/metadata') def test_set_metadata(self): cs.share_snapshots.set_metadata(1234, {'k1': 'v2'}) cs.assert_called('POST', '/snapshots/1234/metadata', {'metadata': {'k1': 'v2'}}) @ddt.data( type('SnapshotUUID', (object, ), {'uuid': '1234'}), type('SnapshotID', (object, ), {'id': '1234'}), '1234') def test_delete_metadata(self, snapshot): keys = ['key1'] cs.share_snapshots.delete_metadata(snapshot, keys) cs.assert_called('DELETE', '/snapshots/1234/metadata/key1') @ddt.data( type('SnapshotUUID', (object, ), {'uuid': '1234'}), type('SnapshotID', (object, ), {'id': '1234'}), '1234') def test_metadata_update_all(self, snapshot): cs.share_snapshots.update_all_metadata(snapshot, {'k1': 'v1'}) cs.assert_called('PUT', '/snapshots/1234/metadata', {'metadata': {'k1': 'v1'}}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_share_transfers.py0000664000175000017500000000341100000000000027436 0ustar00zuulzuul00000000000000# Copyright (c) 2022 China Telecom Digital Intelligence. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from manilaclient import api_versions from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes TRANSFER_URL = 'share-transfers' cs = fakes.FakeClient(api_versions.APIVersion('2.77')) class ShareTransfersTest(utils.TestCase): def test_create(self): cs.transfers.create('1234') cs.assert_called('POST', '/%s' % TRANSFER_URL, body={'transfer': {'share_id': '1234', 'name': None}}) def test_get(self): transfer_id = '5678' cs.transfers.get(transfer_id) cs.assert_called('GET', '/%s/%s' % (TRANSFER_URL, transfer_id)) def test_list(self): cs.transfers.list() cs.assert_called('GET', '/%s/detail' % TRANSFER_URL) def test_delete(self): cs.transfers.delete('5678') cs.assert_called('DELETE', '/%s/5678' % TRANSFER_URL) def test_accept(self): transfer_id = '5678' auth_key = '12345' cs.transfers.accept(transfer_id, auth_key) cs.assert_called('POST', '/%s/%s/accept' % (TRANSFER_URL, transfer_id)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_shares.py0000664000175000017500000007331600000000000025545 0ustar00zuulzuul00000000000000# 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. from unittest import mock import ddt 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) # 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, 'scheduler_hints': dict(), } 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, 'scheduler_hints': dict(), } 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, 'scheduler_hints': dict(), } 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, 'scheduler_hints': {}, } } 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') def test_soft_delete_share(self): share = cs.shares.get('1234') cs.shares.soft_delete(share) body = {"soft_delete": None} cs.assert_called('POST', '/shares/1234/action', body=body) def test_restore_share(self): share = cs.shares.get('1234') cs.shares.restore(share) body = {"restore": None} cs.assert_called('POST', '/shares/1234/action', body=body) @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') @ddt.data("1.0", "2.35", "2.69") def test_list_shares_index_diff_api_version(self, microversion): version = api_versions.APIVersion(microversion) mock_microversion = mock.Mock(api_version=version) manager = shares.ShareManager(api=mock_microversion) search_opts1 = {} search_opts2 = { 'export_location': 'fake_export_id', } search_opts3 = { 'export_location': 'fake_export_id', 'is_soft_deleted': 'True', } with mock.patch.object(manager, "do_list", mock.Mock(return_value="fake")): manager.list(detailed=False, search_opts=search_opts3) if version >= api_versions.APIVersion('2.69'): manager.do_list.assert_called_once_with( detailed=False, search_opts=search_opts3, sort_key=None, sort_dir=None, return_raw=False) elif version >= api_versions.APIVersion('2.35'): manager.do_list.assert_called_once_with( detailed=False, search_opts=search_opts2, sort_key=None, sort_dir=None, return_raw=False) else: manager.do_list.assert_called_once_with( detailed=False, search_opts=search_opts1, sort_key=None, sort_dir=None, return_raw=False) 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')) @ddt.data(True, False) def test_list_shares_index_with_is_soft_deleted(self, detailed): search_opts = { 'is_soft_deleted': 'True', } cs.shares.list(detailed=detailed, search_opts=search_opts) if detailed: cs.assert_called( 'GET', ('/shares/detail?is_public=True' + '&is_soft_deleted=True')) else: cs.assert_called( 'GET', ('/shares?is_public=True' + '&is_soft_deleted=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", False), ("2.7", "extend", False), ("2.64", "extend", True), ) @ddt.unpack def test_extend_share(self, microversion, action_name, force): 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")): if not force: result = manager.extend(share, size) manager._action.assert_called_once_with( action_name, share, {"new_size": size}) else: result = manager.extend(share, size, force=force) manager._action.assert_called_once_with( action_name, share, {"new_size": size, "force": "true"}) 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_shell.py0000664000175000017500000046372200000000000025373 0ustar00zuulzuul00000000000000# -*- 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 itertools from unittest import mock import ddt import fixtures from oslo_utils import strutils from manilaclient import api_versions from manilaclient import client from manilaclient.common.apiclient import utils as apiclient_utils from manilaclient.common import cliutils from manilaclient.common import constants from manilaclient import exceptions from manilaclient import shell from manilaclient.tests.unit import utils as test_utils from manilaclient.tests.unit.v2 import fakes from manilaclient import utils from manilaclient.v2 import messages from manilaclient.v2 import security_services from manilaclient.v2 import share_access_rules from manilaclient.v2 import share_group_types from manilaclient.v2 import share_groups from manilaclient.v2 import share_instances from manilaclient.v2 import share_network_subnets from manilaclient.v2 import share_networks from manilaclient.v2 import share_servers from manilaclient.v2 import share_snapshots from manilaclient.v2 import share_types from manilaclient.v2 import shares from manilaclient.v2 import shell as shell_v2 @ddt.ddt class ShellTest(test_utils.TestCase): FAKE_ENV = { 'MANILA_USERNAME': 'username', 'MANILA_PASSWORD': 'password', 'MANILA_PROJECT_ID': 'project_id', 'MANILA_URL': 'http://no.where', } # Patch os.environ to avoid required auth info. def setUp(self): """Run before each test.""" super(ShellTest, self).setUp() for var in self.FAKE_ENV: self.useFixture(fixtures.EnvironmentVariable(var, self.FAKE_ENV[var])) self.mock_completion() self.shell = shell.OpenStackManilaShell() # HACK(bcwaldon): replace this when we start using stubs self.old_get_client_class = client.get_client_class client.get_client_class = lambda *_: fakes.FakeClient # Following shows available separators for optional params # and its values self.separators = [' ', '='] self.create_share_body = { "share": { "share_type": None, "name": None, "snapshot_id": None, "description": None, "metadata": {}, "share_proto": "nfs", "share_network_id": None, "size": 1, "is_public": False, "availability_zone": None, "scheduler_hints": {}, } } def tearDown(self): # For some method like test_image_meta_bad_action we are # testing a SystemExit to be thrown and object self.shell has # no time to get instantatiated which is OK in this case, so # we make sure the method is there before launching it. if hasattr(self.shell, 'cs') and hasattr(self.shell.cs, 'clear_callstack'): self.shell.cs.clear_callstack() # HACK(bcwaldon): replace this when we start using stubs client.get_client_class = self.old_get_client_class super(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 key = ('availability_zone_id' if key == 'availability_zone' else key) self.assert_called('GET', '/shares/detail?sort_key=' + key) def test_list_with_fake_sort_key(self): self.assertRaises( ValueError, self.run_command, 'list --sort-key fake_sort_key', ) def test_list_filter_by_snapshot(self): fake_s = type('Empty', (object,), {'id': 'fake_snapshot_id'}) for separator in self.separators: with mock.patch.object( apiclient_utils, 'find_resource', mock.Mock(return_value=fake_s)): self.run_command('list --snapshot' + separator + fake_s.id) self.assert_called( 'GET', '/shares/detail?snapshot_id=' + fake_s.id) def test_list_filter_by_snapshot_not_found(self): self.assertRaises( exceptions.CommandError, self.run_command, 'list --snapshot not_found_expected', ) self.assert_called('GET', '/snapshots/detail?all_tenants=1') def test_list_filter_by_host(self): for separator in self.separators: self.run_command('list --host' + separator + 'fake_host') self.assert_called('GET', '/shares/detail?host=fake_host') @ddt.data(('id', 'b4991315-eb7d-43ec-979e-5715d4399827'), ('path', 'fake_path')) @ddt.unpack def test_share_list_filter_by_export_location(self, filter_type, value): for separator in self.separators: self.run_command('list --export_location' + separator + value) self.assert_called( 'GET', '/shares/detail?export_location_' + filter_type + '=' + value) @ddt.data('list', 'share-instance-list') def test_share_or_instance_list_filter_by_export_location_version_invalid( self, cmd): self.assertRaises( exceptions.CommandError, self.run_command, cmd + ' --export_location=fake', '2.34' ) def test_list_filter_by_share_network(self): aliases = ['--share-network', '--share_network', ] fake_sn = type('Empty', (object,), {'id': 'fake_share_network_id'}) for alias in aliases: for separator in self.separators: with mock.patch.object( apiclient_utils, 'find_resource', mock.Mock(return_value=fake_sn)): self.run_command('list ' + alias + separator + fake_sn.id) self.assert_called( 'GET', '/shares/detail?share_network_id=' + fake_sn.id) def test_list_filter_by_share_network_not_found(self): self.assertRaises( exceptions.CommandError, self.run_command, 'list --share-network not_found_expected', ) self.assert_called('GET', '/share-networks/detail?all_tenants=1') @ddt.data('True', 'False') def test_list_filter_with_count(self, value): except_url = '/shares/detail?with_count=' + value if value == 'False': except_url = '/shares/detail' for separator in self.separators: self.run_command('list --count' + separator + value) self.assert_called('GET', except_url) @ddt.data('True', 'False') def test_list_filter_with_count_invalid_version(self, value): self.assertRaises( exceptions.CommandError, self.run_command, 'list --count ' + value, version='2.41' ) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_share_instance_list(self): self.run_command('share-instance-list') self.assert_called('GET', '/share_instances') cliutils.print_list.assert_called_once_with( mock.ANY, ['ID', 'Share ID', 'Host', 'Status', 'Availability Zone', 'Share Network ID', 'Share Server ID', 'Share Type ID']) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_share_instance_list_select_column(self): self.run_command('share-instance-list --column id,host,status') self.assert_called('GET', '/share_instances') cliutils.print_list.assert_called_once_with( mock.ANY, ['Id', 'Host', 'Status']) @mock.patch.object(cliutils, 'print_list', mock.Mock()) @ddt.data(('id', 'b4991315-eb7d-43ec-979e-5715d4399827'), ('path', 'fake_path')) @ddt.unpack def test_share_instance_list_filter_by_export_location(self, filter_type, value): for separator in self.separators: self.run_command('share-instance-list --export_location' + separator + value) self.assert_called( 'GET', ('/share_instances?export_location_' + filter_type + '=' + value)) @mock.patch.object(apiclient_utils, 'find_resource', mock.Mock(return_value='fake')) def test_share_instance_list_with_share(self): self.run_command('share-instance-list --share-id=fake') self.assert_called('GET', '/shares/fake/instances') def test_share_instance_list_invalid_share(self): self.assertRaises( exceptions.CommandError, self.run_command, 'share-instance-list --share-id=not-found-id', ) def test_share_instance_show(self): self.run_command('share-instance-show 1234') self.assert_called_anytime('GET', '/share_instances/1234') def test_share_instance_export_location_list(self): self.run_command('share-instance-export-location-list 1234') self.assert_called_anytime( 'GET', '/share_instances/1234/export_locations') @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_share_instance_export_location_list_with_columns(self): self.run_command( 'share-instance-export-location-list 1234 --columns uuid,path') self.assert_called_anytime( 'GET', '/share_instances/1234/export_locations') cliutils.print_list.assert_called_once_with(mock.ANY, ['Uuid', 'Path']) def test_share_instance_export_location_show(self): self.run_command( 'share-instance-export-location-show 1234 fake_el_uuid') self.assert_called_anytime( 'GET', '/share_instances/1234/export_locations/fake_el_uuid') def test_share_instance_reset_state(self): self.run_command('share-instance-reset-state 1234') expected = {'reset_status': {'status': 'available'}} self.assert_called('POST', '/share_instances/1234/action', body=expected) def test_share_instance_force_delete(self): manager_mock = mock.Mock() share_instance = share_instances.ShareInstance( manager_mock, {'id': 'fake'}, True) with mock.patch.object(shell_v2, '_find_share_instance', mock.Mock(return_value=share_instance)): self.run_command('share-instance-force-delete 1234') manager_mock.force_delete.assert_called_once_with(share_instance) @ddt.data(('share_instance_xyz', ), ('share_instance_abc', 'share_instance_xyz')) def test_share_instance_force_delete_wait(self, instances_to_delete): fake_manager = mock.Mock() fake_instances = [ share_instances.ShareInstance(fake_manager, {'id': '1234'}) for instance in instances_to_delete ] instance_not_found_error = ("Delete for instance %s failed: No " "instance with a name or " "ID of '%s' exists.") instances_are_not_found_errors = [ exceptions.CommandError( instance_not_found_error % (instance, instance)) for instance in instances_to_delete ] self.mock_object( shell_v2, '_find_share_instance', mock.Mock(side_effect=( fake_instances + instances_are_not_found_errors))) self.run_command( 'share-instance-force-delete %s --wait' % ' '.join( instances_to_delete)) shell_v2._find_share_instance.assert_has_calls([ mock.call(self.shell.cs, instance) for instance in instances_to_delete ]) fake_manager.force_delete.assert_has_calls([ mock.call(instance) for instance in fake_instances]) self.assertEqual(len(instances_to_delete), fake_manager.force_delete.call_count) def test_type_show_details(self): self.run_command('type-show 1234') self.assert_called_anytime('GET', '/types/1234') @mock.patch.object(cliutils, 'print_list', mock.Mock()) @ddt.data(*itertools.product( ('type-list --columns id,is_default', 'type-list --columns id,name', 'type-list --columns is_default', 'type-list'), {'2.45', '2.46', api_versions.MAX_VERSION})) @ddt.unpack def test_type_list(self, command, version): self.run_command(command, version=version) columns_requested = ['ID', 'Name', 'visibility', 'is_default', 'required_extra_specs', 'optional_extra_specs', 'Description'] if 'columns' in command: columns_requested = command.split('--columns ')[1].split(',') is_default_in_api = (api_versions.APIVersion(version) >= api_versions.APIVersion('2.46')) if not is_default_in_api and 'is_default' in columns_requested: self.assert_called('GET', '/types/default') self.assert_called_anytime('GET', '/types') else: self.assert_called('GET', '/types') cliutils.print_list.assert_called_with( mock.ANY, columns_requested, mock.ANY) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_type_list_select_column(self): self.run_command('type-list --columns id,name') self.assert_called('GET', '/types') cliutils.print_list.assert_called_once_with( mock.ANY, ['id', 'name'], mock.ANY) def test_type_list_all(self): self.run_command('type-list --all') self.assert_called_anytime('GET', '/types?is_public=all') @ddt.data(True, False) def test_type_create_with_access(self, public): expected = { 'share_type': { 'name': 'test-type-3', 'extra_specs': { 'driver_handles_share_servers': False, }, 'share_type_access:is_public': public } } self.run_command( 'type-create test-type-3 false --is-public %s' % str(public)) self.assert_called('POST', '/types', body=expected) def test_type_access_list(self): self.run_command('type-access-list 3') self.assert_called('GET', '/types/3/share_type_access') def test_type_access_add_project(self): expected = {'addProjectAccess': {'project': '101'}} self.run_command('type-access-add 3 101') self.assert_called('POST', '/types/3/action', body=expected) def test_type_access_remove_project(self): expected = {'removeProjectAccess': {'project': '101'}} self.run_command('type-access-remove 3 101') self.assert_called('POST', '/types/3/action', body=expected) def test_list_filter_by_project_id(self): aliases = ['--project-id', '--project_id'] for alias in aliases: for separator in self.separators: self.run_command('list ' + alias + separator + 'fake_id') self.assert_called('GET', '/shares/detail?project_id=fake_id') @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_list_with_public_shares(self): listed_fields = [ 'ID', 'Name', 'Size', 'Share Proto', 'Status', 'Is Public', 'Share Type Name', 'Host', 'Availability Zone', 'Project ID' ] self.run_command('list --public') self.assert_called('GET', '/shares/detail?is_public=True') cliutils.print_list.assert_called_with(mock.ANY, listed_fields, sortby_index=None) def test_show(self): self.run_command('show 1234') self.assert_called_anytime('GET', '/shares/1234') def test_share_export_location_list(self): self.run_command('share-export-location-list 1234') self.assert_called_anytime( 'GET', '/shares/1234/export_locations') @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_share_export_location_list_with_columns(self): self.run_command('share-export-location-list 1234 --columns uuid,path') self.assert_called_anytime( 'GET', '/shares/1234/export_locations') cliutils.print_list.assert_called_once_with(mock.ANY, ['Uuid', 'Path']) def test_share_export_location_show(self): self.run_command('share-export-location-show 1234 fake_el_uuid') self.assert_called_anytime( 'GET', '/shares/1234/export_locations/fake_el_uuid') @ddt.data({'cmd_args': '--driver_options opt1=opt1 opt2=opt2' ' --share_type fake_share_type', 'valid_params': { 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, 'share_type': 'fake_share_type', 'share_server_id': None, }}, {'cmd_args': '--share_type fake_share_type', 'valid_params': { 'driver_options': {}, 'share_type': 'fake_share_type', 'share_server_id': None, }}, {'cmd_args': '', 'valid_params': { 'driver_options': {}, 'share_type': None, 'share_server_id': None, }}, {'cmd_args': '--public' ' --wait', 'valid_params': { 'driver_options': {}, 'share_type': None, 'share_server_id': None, }, 'is_public': True, 'version': '--os-share-api-version 2.8', }, {'cmd_args': '', 'valid_params': { 'driver_options': {}, 'share_type': None, 'share_server_id': None, }, 'is_public': False, 'version': '--os-share-api-version 2.8', }, {'cmd_args': '--driver_options opt1=opt1 opt2=opt2' ' --share_type fake_share_type' ' --wait', 'valid_params': { 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, 'share_type': 'fake_share_type', 'share_server_id': None, }, 'version': '--os-share-api-version 2.49', }, {'cmd_args': '--driver_options opt1=opt1 opt2=opt2' ' --share_type fake_share_type' ' --share_server_id fake_server', 'valid_params': { 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, 'share_type': 'fake_share_type', 'share_server_id': 'fake_server', }, 'version': '--os-share-api-version 2.49', }, {'cmd_args': '--driver_options opt1=opt1 opt2=opt2' ' --share_type fake_share_type' ' --share_server_id fake_server' ' --wait', 'valid_params': { 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, 'share_type': 'fake_share_type', 'share_server_id': 'fake_server', }}, ) @ddt.unpack def test_manage(self, cmd_args, valid_params, is_public=False, version=None): share_to_be_managed = shares.Share( 'fake_share', { 'id': 'fake' } ) self.mock_object( shell_v2, '_wait_for_resource_status', mock.Mock(return_value=share_to_be_managed) ) if version is not None: self.run_command(version + ' manage fake_service fake_protocol ' + ' fake_export_path ' + cmd_args) else: self.run_command(' manage fake_service fake_protocol ' + ' fake_export_path ' + cmd_args) expected = { 'share': { 'service_host': 'fake_service', 'protocol': 'fake_protocol', 'export_path': 'fake_export_path', 'name': None, 'description': None, 'is_public': is_public, 'share_server_id': valid_params['share_server_id'], } } expected['share'].update(valid_params) self.assert_called('POST', '/shares/manage', body=expected) if '--wait' in cmd_args: shell_v2._wait_for_resource_status.assert_called_once_with( self.shell.cs, share_to_be_managed, resource_type='share', expected_status='available') else: shell_v2._wait_for_resource_status.assert_not_called() def test_manage_invalid_param_share_server_id(self): self.assertRaises( exceptions.CommandError, self.run_command, '--os-share-api-version 2.48' + ' manage fake_service fake_protocol ' + ' fake_export_path ' + ' --driver_options opt1=opt1 opt2=opt2' + ' --share_type fake_share_type' + ' --share_server_id fake_server') def test_share_server_manage_unsupported_version(self): self.assertRaises( exceptions.UnsupportedVersion, self.run_command, '--os-share-api-version 2.48 ' + 'share-server-manage fake_host fake_share_net_id fake_id') def test_share_server_manage_invalid_param_subnet_id(self): self.assertRaises( exceptions.CommandError, self.run_command, '--os-share-api-version 2.49 ' + 'share-server-manage fake_host fake_share_net_id fake_id ' + '--share-network-subnet fake_subnet_id') @ddt.data({'driver_args': '--driver_options opt1=opt1 opt2=opt2', 'valid_params': { 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, }}, {'driver_args': '--driver_options opt1=opt1 opt2=opt2', 'subnet_id': 'fake_subnet_1', 'valid_params': { 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, }}, {'driver_args': '--driver_options opt1=opt1 opt2=opt2', 'valid_params': { 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, }, 'version': '2.51', }, {'driver_args': '--driver_options opt1=opt1 opt2=opt2', 'subnet_id': 'fake_subnet_1', 'valid_params': { 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, }, 'version': '2.51', }, {'driver_args': "", 'valid_params': { 'driver_options': {} }, 'version': '2.51', }, {'driver_args': '--driver_options opt1=opt1 opt2=opt2', 'valid_params': { 'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'}, }, 'version': '2.49', }, {'driver_args': '', 'valid_params': { 'driver_options': {}, }, 'network_id': 'fake_network_id', 'version': '2.49', }, {'driver_args': "", 'valid_params': { 'driver_options': {} }, 'version': '2.49', }, ) @ddt.unpack def test_share_server_manage_wait(self, driver_args, valid_params, version=None, network_id=None, subnet_id=None): fake_manager = mock.Mock() subnet_support = (version is None or api_versions.APIVersion(version) >= api_versions.APIVersion('2.51')) network_id = '3456' if network_id is None else network_id fake_share_network = share_networks.ShareNetwork( fake_manager, {'id': network_id, 'uuid': network_id}) self.mock_object( shell_v2, '_find_share_network', mock.Mock(return_value=fake_share_network)) fake_share_server = share_servers.ShareServer( fake_manager, {'id': 'fake'}) self.mock_object( shell_v2, '_find_share_server', mock.Mock(return_value=fake_share_server)) self.mock_object( shell_v2, '_wait_for_resource_status', mock.Mock() ) command = ('share-server-manage ' '%(host)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) shell_v2._wait_for_resource_status.assert_has_calls([ mock.call(self.shell.cs, fake_share_server, resource_type='share_server', expected_status='active') ]) @ddt.data(constants.STATUS_ERROR, constants.STATUS_ACTIVE, constants.STATUS_MANAGE_ERROR, constants.STATUS_UNMANAGE_ERROR, constants.STATUS_DELETING, constants.STATUS_CREATING) def test_share_server_reset_state(self, status): self.run_command('share-server-reset-state 1234 --state %s ' % status) expected = {'reset_status': {'status': status}} self.assert_called('POST', '/share-servers/1234/action', body=expected) @ddt.data('--wait', '') def test_unmanage(self, wait_option): version = api_versions.APIVersion('2.46') api = mock.Mock(api_version=version) manager = shares.ShareManager(api=api) fake_share = shares.Share( manager, { 'id': 'xyzzyspoon', 'api_version': version, 'status': 'available', } ) share_not_found_error = ("ERROR: No share with " "a name or ID of '%s' exists.") share_not_found_error = exceptions.CommandError( share_not_found_error % (fake_share.id) ) self.mock_object( shell_v2, '_find_share', mock.Mock(side_effect=([fake_share, fake_share, fake_share, share_not_found_error]))) self.mock_object( shares.ShareManager, 'get', mock.Mock(return_value=fake_share)) self.run_command('unmanage %s xyzzyspoon' % wait_option) expected_get_share_calls = 4 if wait_option else 1 shell_v2._find_share.assert_has_calls( [mock.call(self.shell.cs, fake_share.id)] * expected_get_share_calls ) uri = '/shares/%s/action' % fake_share.id api.client.post.assert_called_once_with(uri, body={'unmanage': None}) def test_share_server_unmanage(self): self.run_command('share-server-unmanage 1234') self.assert_called('POST', '/share-servers/1234/action', body={'unmanage': {'force': False}}) def test_share_server_unmanage_force(self): self.run_command('share-server-unmanage 1234 --force') self.assert_called('POST', '/share-servers/1234/action', body={'unmanage': {'force': True}}) @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' ) @ddt.data(('share_xyz', ), ('share_abc', 'share_xyz')) def test_delete_wait(self, shares_to_delete): fake_shares = [ shares.Share('fake', {'id': share}) for share in shares_to_delete ] share_not_found_error = ("Delete for share %s failed: No share with " "a name or ID of '%s' exists.") shares_are_not_found_errors = [ exceptions.CommandError(share_not_found_error % (share, share)) for share in shares_to_delete ] self.mock_object( shell_v2, '_find_share', mock.Mock(side_effect=(fake_shares + shares_are_not_found_errors))) self.run_command('delete %s --wait' % ' '.join(shares_to_delete)) shell_v2._find_share.assert_has_calls([ mock.call(self.shell.cs, share) for share in shares_to_delete ]) for share in fake_shares: uri = '/shares/%s' % share.id self.assert_called_anytime('DELETE', uri, clear_callstack=False) @ddt.data(('share_xyz', ), ('share_abc', 'share_xyz')) def test_force_delete_wait(self, shares_to_delete): fake_manager = mock.Mock() fake_shares = [ shares.Share(fake_manager, {'id': '1234'}) for share in shares_to_delete ] share_not_found_error = ("Delete for share %s failed: No share with " "a name or ID of '%s' exists.") shares_are_not_found_errors = [ exceptions.CommandError(share_not_found_error % (share, share)) for share in shares_to_delete ] self.mock_object( shell_v2, '_find_share', mock.Mock(side_effect=(fake_shares + shares_are_not_found_errors))) self.run_command('force-delete %s --wait' % ' '.join(shares_to_delete)) shell_v2._find_share.assert_has_calls([ mock.call(self.shell.cs, share) for share in shares_to_delete ]) fake_manager.force_delete.assert_has_calls([ mock.call(share) for share in fake_shares]) self.assertEqual(len(shares_to_delete), fake_manager.force_delete.call_count) def test_soft_delete(self): self.run_command('soft-delete 1234') expected = {'soft_delete': None} self.assert_called('POST', '/shares/1234/action', body=expected) def test_restore(self): self.run_command('restore 1234') expected = {'restore': None} self.assert_called('POST', '/shares/1234/action', body=expected) def test_list_snapshots(self): self.run_command('snapshot-list') self.assert_called('GET', '/snapshots/detail') @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_snapshot_list_select_column(self): self.run_command('snapshot-list --columns id,name') self.assert_called('GET', '/snapshots/detail') cliutils.print_list.assert_called_once_with( mock.ANY, ['Id', 'Name'], sortby_index=None) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_list_snapshots_all_tenants_only_key(self): self.run_command('snapshot-list --all-tenants') self.assert_called('GET', '/snapshots/detail?all_tenants=1') cliutils.print_list.assert_called_once_with( mock.ANY, ['ID', 'Share ID', 'Status', 'Name', 'Share Size', 'Project ID'], sortby_index=None) def test_list_snapshots_all_tenants_key_and_value_1(self): for separator in self.separators: self.run_command('snapshot-list --all-tenants' + separator + '1') self.assert_called( 'GET', '/snapshots/detail?all_tenants=1') def test_list_snapshots_all_tenants_key_and_value_0(self): for separator in self.separators: self.run_command('snapshot-list --all-tenants' + separator + '0') self.assert_called('GET', '/snapshots/detail') def test_list_snapshots_filter_by_name(self): for separator in self.separators: self.run_command('snapshot-list --name' + separator + '1234') self.assert_called( 'GET', '/snapshots/detail?name=1234') def test_list_snapshots_filter_by_status(self): for separator in self.separators: self.run_command('snapshot-list --status' + separator + '1234') self.assert_called( 'GET', '/snapshots/detail?status=1234') def test_list_snapshots_filter_by_share_id(self): aliases = ['--share_id', '--share-id'] for alias in aliases: for separator in self.separators: self.run_command('snapshot-list ' + alias + separator + '1234') self.assert_called( 'GET', '/snapshots/detail?share_id=1234') def test_list_snapshots_only_used(self): for separator in self.separators: self.run_command('snapshot-list --usage' + separator + 'used') self.assert_called('GET', '/snapshots/detail?usage=used') def test_list_snapshots_only_unused(self): for separator in self.separators: self.run_command('snapshot-list --usage' + separator + 'unused') self.assert_called('GET', '/snapshots/detail?usage=unused') def test_list_snapshots_any(self): for separator in self.separators: self.run_command('snapshot-list --usage' + separator + 'any') self.assert_called('GET', '/snapshots/detail?usage=any') def test_list_snapshots_with_limit(self): for separator in self.separators: self.run_command('snapshot-list --limit' + separator + '50') self.assert_called( 'GET', '/snapshots/detail?limit=50') def test_list_snapshots_with_offset(self): for separator in self.separators: self.run_command('snapshot-list --offset' + separator + '50') self.assert_called( 'GET', '/snapshots/detail?offset=50') def test_list_snapshots_filter_by_inexact_name(self): for separator in self.separators: self.run_command('snapshot-list --name~' + separator + 'fake_name') self.assert_called( 'GET', '/snapshots/detail?name~=fake_name') def test_list_snapshots_filter_by_inexact_description(self): for separator in self.separators: self.run_command('snapshot-list --description~' + separator + 'fake_description') self.assert_called( 'GET', '/snapshots/detail?description~=fake_description') def test_list_snapshots_filter_by_inexact_unicode_name(self): for separator in self.separators: self.run_command('snapshot-list --name~' + separator + 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', ) @ddt.data('True', 'False') def test_list_snapshots_filter_with_count(self, value): except_url = '/snapshots/detail?with_count=' + value if value == 'False': except_url = '/snapshots/detail' for separator in self.separators: self.run_command('snapshot-list --count' + separator + value) self.assert_called('GET', except_url) @ddt.data('True', 'False') def test_list_snapshots_filter_with_count_invalid_version(self, value): self.assertRaises( exceptions.CommandError, self.run_command, 'snapshot-list --count ' + value, version='2.78' ) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_extra_specs_list(self): self.run_command('extra-specs-list') self.assert_called('GET', '/types?is_public=all') cliutils.print_list.assert_called_once_with( mock.ANY, ['ID', 'Name', 'all_extra_specs'], mock.ANY) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_extra_specs_list_select_column(self): self.run_command('extra-specs-list --columns id,name') self.assert_called('GET', '/types?is_public=all') cliutils.print_list.assert_called_once_with( mock.ANY, ['id', 'name'], mock.ANY) @ddt.data('fake', 'FFFalse', 'trueee') def test_type_create_invalid_dhss_value(self, value): self.assertRaises( exceptions.CommandError, self.run_command, 'type-create test ' + value, ) @ddt.data('True', 'False') def test_type_create_duplicate_dhss(self, value): self.assertRaises( exceptions.CommandError, self.run_command, 'type-create test ' + value + ' --extra-specs driver_handles_share_servers=' + value, ) @ddt.data(*itertools.product( ['snapshot_support', 'create_share_from_snapshot_support'], ['True', 'False']) ) @ddt.unpack def test_type_create_duplicate_switch_and_extra_spec(self, key, value): cmd = ('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]) @ddt.data('--wait', '') def test_extend_with_wait_option(self, wait_option): available_share = shares.Share( 'fake', {'id': '1234', 'status': 'available'}) share_to_extend = shares.Share('fake', {'id': '1234', 'status': 'extending'}) fake_shares = [ available_share, share_to_extend, share_to_extend, available_share ] self.mock_object(shell_v2, '_find_share', mock.Mock(side_effect=fake_shares)) expected_extend_body = {'extend': {'new_size': 77}} self.run_command('extend 1234 77 %s' % wait_option) self.assert_called_anytime('POST', '/shares/1234/action', body=expected_extend_body, clear_callstack=False) if wait_option: shell_v2._find_share.assert_has_calls( [mock.call(self.shell.cs, '1234')] * 4) self.assertEqual(4, shell_v2._find_share.call_count) else: shell_v2._find_share.assert_called_with( self.shell.cs, '1234') self.assertEqual(2, shell_v2._find_share.call_count) def test_reset_state(self): self.run_command('reset-state 1234') expected = {'reset_status': {'status': 'available'}} self.assert_called('POST', '/shares/1234/action', body=expected) @ddt.data('--wait', '') def test_shrink_with_wait_option(self, wait_option): available_share = shares.Share( 'fake', {'id': '1234', 'status': 'available'}) share_to_shrink = shares.Share('fake', {'id': '1234', 'status': 'shrinking'}) fake_shares = [ available_share, share_to_shrink, share_to_shrink, available_share ] self.mock_object(shell_v2, '_find_share', mock.Mock(side_effect=fake_shares)) expected_shrink_body = {'shrink': {'new_size': 77}} self.run_command('shrink 1234 77 %s' % wait_option) self.assert_called_anytime('POST', '/shares/1234/action', body=expected_shrink_body, clear_callstack=False) if wait_option: shell_v2._find_share.assert_has_calls( [mock.call(self.shell.cs, '1234')] * 4) self.assertEqual(4, shell_v2._find_share.call_count) else: shell_v2._find_share.assert_called_with( self.shell.cs, '1234') self.assertEqual(2, shell_v2._find_share.call_count) def test_reset_state_with_flag(self): self.run_command('reset-state --state error 1234') expected = {'reset_status': {'status': 'error'}} self.assert_called('POST', '/shares/1234/action', body=expected) def test_snapshot_reset_state(self): self.run_command('snapshot-reset-state 1234') expected = {'reset_status': {'status': 'available'}} self.assert_called('POST', '/snapshots/1234/action', body=expected) def test_snapshot_reset_state_with_flag(self): self.run_command('snapshot-reset-state --state error 1234') expected = {'reset_status': {'status': 'error'}} self.assert_called('POST', '/snapshots/1234/action', body=expected) @ddt.data( {}, {'--name': 'fake_name'}, {'--description': 'fake_description'}, {'--neutron_net_id': 'fake_neutron_net_id'}, {'--neutron_subnet_id': 'fake_neutron_subnet_id'}, {'--description': 'fake_description', '--name': 'fake_name', '--neutron_net_id': 'fake_neutron_net_id', '--neutron_subnet_id': 'fake_neutron_subnet_id'}) def test_share_network_create(self, data): cmd = 'share-network-create' for k, v in data.items(): cmd += ' ' + k + ' ' + v self.run_command(cmd) self.assert_called('POST', '/share-networks') @ddt.unpack @ddt.data( {'data': {'--name': 'fake_name'}}, {'data': {'--description': 'fake_description'}}, {'data': {'--neutron_net_id': 'fake_neutron_net_id'}, 'version': '2.49', }, {'data': {'--neutron_subnet_id': 'fake_neutron_subnet_id'}, 'version': '2.49', }, {'data': { '--description': 'fake_description', '--name': 'fake_name', '--neutron_net_id': 'fake_neutron_net_id', '--neutron_subnet_id': 'fake_neutron_subnet_id'}, 'version': '2.49', }, {'data': {'--name': '""'}}, {'data': {'--description': '""'}}, {'data': {'--neutron_net_id': '""'}, 'version': '2.49', }, {'data': {'--neutron_subnet_id': '""'}, 'version': '2.49', }, {'data': { '--description': '""', '--name': '""', '--neutron_net_id': '""', '--neutron_subnet_id': '""'}, 'version': '2.49', }) def test_share_network_update(self, data, version=None): cmd = 'share-network-update 1111' expected = dict() for k, v in data.items(): cmd += ' ' + k + ' ' + v expected[k[2:]] = v expected = dict(share_network=expected) self.run_command(cmd, version=version) self.assert_called('PUT', '/share-networks/1111', body=expected) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_share_network_list(self): self.run_command('share-network-list') self.assert_called( 'GET', '/share-networks/detail', ) cliutils.print_list.assert_called_once_with( mock.ANY, fields=['id', 'name']) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_share_network_list_select_column(self): self.run_command('share-network-list --columns id') self.assert_called( 'GET', '/share-networks/detail', ) cliutils.print_list.assert_called_once_with( mock.ANY, fields=['Id']) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_share_network_list_all_tenants(self): self.run_command('share-network-list --all-tenants') self.assert_called( 'GET', '/share-networks/detail?all_tenants=1', ) cliutils.print_list.assert_called_once_with( mock.ANY, fields=['id', 'name']) @mock.patch.object(cliutils, 'print_list', mock.Mock()) @mock.patch.object(shell_v2, '_find_security_service', mock.Mock()) def test_share_network_list_filter_by_security_service(self): ss = type('FakeSecurityService', (object,), {'id': 'fake-ss-id'}) shell_v2._find_security_service.return_value = ss for command in ['--security_service', '--security-service']: self.run_command('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 expected = self.create_share_body.copy() expected['share']['share_type'] = 'test_type' self.run_command("create nfs 1 --share-type test_type") self.assert_called("POST", "/shares", body=expected) def test_create_public_share(self): expected = self.create_share_body.copy() expected['share']['is_public'] = True expected['share']['share_type'] = 'test_type' self.run_command("create --public nfs 1 --share-type test_type") self.assert_called("POST", "/shares", body=expected) def test_create_with_share_network(self): # Except required fields added share network sn = "fake-share-network" with mock.patch.object(shell_v2, "_find_share_network", mock.Mock(return_value=sn)): self.run_command("create nfs 1 --share-type test_type " "--share-network %s" % sn) expected = self.create_share_body.copy() expected['share']['share_network_id'] = sn expected['share']['share_type'] = 'test_type' self.assert_called("POST", "/shares", body=expected) shell_v2._find_share_network.assert_called_once_with(mock.ANY, sn) def test_create_with_metadata(self): # Except required fields added metadata self.run_command("create nfs 1 --metadata key1=value1 key2=value2 " "--share-type test_type") expected = self.create_share_body.copy() expected['share']['metadata'] = {"key1": "value1", "key2": "value2"} expected['share']['share_type'] = 'test_type' self.assert_called("POST", "/shares", body=expected) def test_create_with_wait(self): self.run_command("create nfs 1 --wait --share-type test_type") expected = self.create_share_body.copy() expected['share']['share_type'] = 'test_type' self.assert_called_anytime( "POST", "/shares", body=expected, clear_callstack=False) self.assert_called("GET", "/shares/1234") def test_create_share_with_no_existing_share_type(self): self.assertRaises( exceptions.CommandError, self.run_command, "create nfs 1") @ddt.data('None', 'NONE', 'none') def test_create_share_with_the_name_none(self, name): self.assertRaises( exceptions.CommandError, self.run_command, "create nfs 1 --name %s --share-type test_type" % name) 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'}) @ddt.data(*set(["2.45", api_versions.MAX_VERSION])) def test_allow_access_wait(self, version): fake_access_rule = {'id': 'fake_id'} fake_access = mock.Mock() fake_access._info = fake_access_rule fake_share = mock.Mock() fake_share.name = 'fake_share' fake_share.allow = mock.Mock(return_value=fake_access_rule) self.mock_object(shell_v2, '_wait_for_resource_status', mock.Mock(return_value=fake_access)) self.mock_object(share_access_rules.ShareAccessRuleManager, 'get', mock.Mock(return_value=fake_access_rule)) with mock.patch.object(apiclient_utils, 'find_resource', mock.Mock(return_value=fake_share)): is_default_in_api = (api_versions.APIVersion(version) >= api_versions.APIVersion('2.45')) if is_default_in_api: self.run_command("access-allow fake_share ip 10.0.0.1 --wait", version=version) shell_v2._wait_for_resource_status.assert_has_calls([ mock.call(self.shell.cs, fake_access_rule, resource_type='share_access_rule', expected_status='active', status_attr='state')]) def test_snapshot_access_deny(self): self.run_command("snapshot-access-deny 1234 fake_id") self.assert_called('POST', '/snapshots/1234/action') @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_snapshot_export_location_list(self): self.run_command('snapshot-export-location-list 1234') self.assert_called( 'GET', '/snapshots/1234/export-locations') @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_snapshot_instance_export_location_list(self): self.run_command('snapshot-instance-export-location-list 1234') self.assert_called( 'GET', '/snapshot-instances/1234/export-locations') @mock.patch.object(cliutils, 'print_dict', mock.Mock()) def test_snapshot_instance_export_location_show(self): self.run_command('snapshot-instance-export-location-show 1234 ' 'fake_el_id') self.assert_called( 'GET', '/snapshot-instances/1234/export-locations/fake_el_id') cliutils.print_dict.assert_called_once_with( {'path': '/fake_path', 'id': 'fake_id'}) @mock.patch.object(cliutils, 'print_dict', mock.Mock()) def test_snapshot_export_location_show(self): self.run_command('snapshot-export-location-show 1234 fake_el_id') self.assert_called('GET', '/snapshots/1234/export-locations/fake_el_id') cliutils.print_dict.assert_called_once_with( {'path': '/fake_path', 'id': 'fake_id'}) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_security_service_list(self): self.run_command('security-service-list') self.assert_called( 'GET', '/security-services', ) cliutils.print_list.assert_called_once_with( mock.ANY, fields=['id', 'name', 'status', 'type']) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_security_service_list_select_column(self): self.run_command('security-service-list --columns name,type') self.assert_called( 'GET', '/security-services', ) cliutils.print_list.assert_called_once_with( mock.ANY, fields=['Name', 'Type']) @mock.patch.object(cliutils, 'print_list', mock.Mock()) @mock.patch.object(shell_v2, '_find_share_network', mock.Mock()) def test_security_service_list_filter_share_network(self): class FakeShareNetwork(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}), ('--per_share_gigabytes 101', {'per_share_gigabytes': 101}), ) @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): fake_manager = mock.Mock() fake_share_group = share_groups.ShareGroup( fake_manager, {'id': '1234'}) self.mock_object( shell_v2, '_find_share_group', mock.Mock(side_effect=[fake_share_group])) self.run_command('share-group-show 1234') shell_v2._find_share_group.assert_has_calls( [mock.call(self.shell.cs, "1234")]) def test_share_group_create_wait(self): fake_manager = mock.Mock() fake_share_type1 = share_types.ShareType( fake_manager, {'name': '1234', 'uuid': '1234'}) fake_share_type2 = share_types.ShareType( fake_manager, {'name': '5678', 'uuid': '5678'}) fake_share_group_type = share_group_types.ShareGroupType( fake_manager, {'name': 'fake_sg', 'uuid': '2345'}) fake_share_network = share_networks.ShareNetwork( fake_manager, {'id': '3456', 'uuid': '3456'}) fake_share_group = share_groups.ShareGroup( fake_manager, {'id': 'fake-sg-id', 'name': 'fake_sg'}) self.mock_object( shell_v2, '_find_share_type', mock.Mock(side_effect=[fake_share_type1, fake_share_type2])) self.mock_object( shell_v2, '_find_share_group_type', mock.Mock(side_effect=[fake_share_group_type])) self.mock_object( shell_v2, '_find_share_network', mock.Mock(side_effect=[fake_share_network])) self.mock_object( shell_v2, '_wait_for_resource_status', mock.Mock(side_effect=[fake_share_group]) ) self.run_command( 'share-group-create --name fake_sg --description my_group ' '--share-types 1234,5678 ' '--share-group-type fake_sg ' '--share-network 3456 ' '--availability-zone fake_az --wait') shell_v2._find_share_type.assert_has_calls([ mock.call(self.shell.cs, '1234'), mock.call(self.shell.cs, '5678') ]) shell_v2._find_share_group_type.assert_has_calls([ mock.call(self.shell.cs, 'fake_sg') ]) shell_v2._find_share_network.assert_has_calls([ mock.call(self.shell.cs, '3456') ]) expected = { 'share_group': { 'name': 'fake_sg', 'description': 'my_group', 'availability_zone': 'fake_az', 'share_group_type_id': '2345', 'share_network_id': '3456', 'share_types': ['1234', '5678'], }, } self.assert_called('POST', '/share-groups', body=expected) shell_v2._wait_for_resource_status.assert_has_calls([ mock.call(self.shell.cs, fake_share_group, resource_type='share_group', expected_status='available') ]) @ddt.data( '--name fake_name --availability-zone fake_az', '--description my_fake_description --name fake_name', '--availability-zone fake_az', ) def test_share_group_create_no_share_types(self, data): cmd = 'share-group-create' + ' ' + data self.run_command(cmd) self.assert_called('POST', '/share-groups') def test_share_group_create_invalid_args(self): fake_share_type_1 = type('FakeShareType1', (object,), {'id': '1234'}) fake_share_type_2 = type('FakeShareType2', (object,), {'id': '5678'}) self.mock_object( shell_v2, '_find_share_type', mock.Mock(side_effect=[fake_share_type_1, fake_share_type_2])) fake_share_group_type = type( 'FakeShareGroupType', (object,), {'id': '2345'}) self.mock_object( shell_v2, '_find_share_group_type', mock.Mock(return_value=fake_share_group_type)) fake_share_group_snapshot = type( 'FakeShareGroupSnapshot', (object,), {'id': '3456'}) self.mock_object( shell_v2, '_find_share_group_snapshot', mock.Mock(return_value=fake_share_group_snapshot)) self.assertRaises( ValueError, self.run_command, 'share-group-create --name fake_sg ' '--description my_group --share-types 1234,5678 ' '--share-group-type fake_sg_type ' '--source-share-group-snapshot fake_share_group_snapshot ' '--availability-zone fake_az') @ddt.data( ('--name new-name', {'name': 'new-name'}), ('--description new-description', {'description': 'new-description'}), ('--name new-name --description new-description', {'name': 'new-name', 'description': 'new-description'}), ) @ddt.unpack def test_share_group_update(self, cmd, expected_body): fake_manager = mock.Mock() fake_share_group = share_groups.ShareGroup( fake_manager, {'uuid': '1234', 'id': '1234'}) self.mock_object( shell_v2, '_find_share_group', mock.Mock(side_effect=[fake_share_group])) self.run_command('share-group-update 1234 %s' % cmd) shell_v2._find_share_group.assert_has_calls( [mock.call(self.shell.cs, '1234')]) expected = {'share_group': expected_body} self.assert_called('PUT', '/share-groups/1234', body=expected) def test_try_update_share_group_without_data(self): self.assertRaises( exceptions.CommandError, self.run_command, 'share-group-update 1234') @ddt.data(('share_group_xyz', ), ('share_group_abc', 'share_group_xyz')) def test_share_group_delete_wait(self, share_group_to_delete): fake_manager = mock.Mock() fake_share_group = [ share_groups.ShareGroup(fake_manager, {'id': share_group}) for share_group in share_group_to_delete ] share_group_not_found_error = ("Delete for share group %s " "failed: No group with a " "name or ID of '%s' exists.") share_group_are_not_found_errors = [ exceptions.CommandError( share_group_not_found_error % (share_group, share_group)) for share_group in share_group_to_delete ] self.mock_object( shell_v2, '_find_share_group', mock.Mock(side_effect=( fake_share_group + share_group_are_not_found_errors))) self.mock_object( shell_v2, '_wait_for_resource_status', mock.Mock() ) self.run_command( 'share-group-delete %s --wait' % ' '.join( share_group_to_delete)) shell_v2._find_share_group.assert_has_calls([ mock.call(self.shell.cs, share_group) for share_group in share_group_to_delete ]) fake_manager.delete.assert_has_calls([ mock.call(share_group, force=False) for share_group in fake_share_group]) shell_v2._wait_for_resource_status.assert_has_calls([ mock.call(self.shell.cs, share_group, resource_type='share_group', expected_status='deleted') for share_group in fake_share_group ]) def test_share_group_delete_force(self): fake_manager = mock.Mock() fake_share_group = share_groups.ShareGroup( fake_manager, {'id': 'fake-group'}) self.mock_object( shell_v2, '_find_share_group', mock.Mock(side_effect=[fake_share_group])) self.run_command('share-group-delete --force fake-group') shell_v2._find_share_group.assert_has_calls( [mock.call(self.shell.cs, "fake-group")]) fake_manager.delete.assert_has_calls( [mock.call(fake_share_group, force=True)]) self.assertEqual(1, fake_manager.delete.call_count) @mock.patch.object(shell_v2, '_find_share_group', mock.Mock()) def test_share_group_delete_all_fail(self): shell_v2._find_share_group.side_effect = Exception self.assertRaises( exceptions.CommandError, self.run_command, 'share-group-delete fake-group') @mock.patch.object(shell_v2, '_find_share_group', mock.Mock()) def test_share_group_reset_state_with_flag(self): fake_group = type('FakeShareGroup', (object,), {'id': '1234'}) shell_v2._find_share_group.return_value = fake_group self.run_command('share-group-reset-state --state error 1234') self.assert_called( 'POST', '/share-groups/1234/action', {'reset_status': {'status': 'error'}}) @ddt.data( 'fake-sg-id', '--name fake_name fake-sg-id', '--description my_fake_description --name fake_name fake-sg-id', ) @mock.patch.object(shell_v2, '_find_share_group', mock.Mock()) 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' % str(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}, {'--shares': 5, '--snapshots': 5, '--gigabytes': 5, '--snapshot-gigabytes': 5, '--share-networks': 5, '--share-groups': 5, '--share-group-snapshots': 5}) def test_quota_class_update(self, data): cmd = 'quota-class-update test' expected = dict() for k, v in data.items(): cmd += ' %(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') @mock.patch.object(shell_v2, '_find_share_replica', mock.Mock()) def test_share_replica_promote_quiesce_wait_time(self): fake_replica = type('FakeShareReplica', (object,), {'id': '1234'}) shell_v2._find_share_replica.return_value = fake_replica cmd = ('share-replica-promote ' + fake_replica.id + ' --quiesce-wait-time 5') self.run_command(cmd) self.assert_called( 'POST', '/share-replicas/1234/action', body={'promote': {'quiesce_wait_time': '5'}}) @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', str(param), '1234')) self.run_command(command) expected = {'reset_task_state': {'task_state': param}} self.assert_called('POST', '/shares/1234/action', body=expected) @ddt.data(('fake_security_service1', ), ('fake_security_service1', 'fake_security_service2')) def test_security_service_delete(self, ss_ids): fake_security_services = [ security_services.SecurityService('fake', {'id': ss_id}, True) for ss_id in ss_ids ] self.mock_object( shell_v2, '_find_security_service', mock.Mock(side_effect=fake_security_services)) self.run_command('security-service-delete %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(('snapshot_xyz', ), ('snapshot_abc', 'snapshot_xyz')) def test_snapshot_force_delete_wait(self, snapshots_to_delete): fake_manager = mock.Mock() fake_snapshots = [ share_snapshots.ShareSnapshot(fake_manager, {'id': '1234'}) for snapshot in snapshots_to_delete ] snapshot_not_found_error = ("Delete for snapshot %s failed: No " "snapshot with a name or " "ID of '%s' exists.") snapshots_are_not_found_errors = [ exceptions.CommandError( snapshot_not_found_error % (snapshot, snapshot)) for snapshot in snapshots_to_delete ] self.mock_object( shell_v2, '_find_share_snapshot', mock.Mock(side_effect=( fake_snapshots + snapshots_are_not_found_errors))) self.run_command( 'snapshot-force-delete %s --wait' % ' '.join( snapshots_to_delete)) shell_v2._find_share_snapshot.assert_has_calls([ mock.call(self.shell.cs, snapshot) for snapshot in snapshots_to_delete ]) fake_manager.force_delete.assert_has_calls([ mock.call(snapshot) for snapshot in fake_snapshots]) self.assertEqual(len(snapshots_to_delete), fake_manager.force_delete.call_count) @ddt.data(('fake_type1', ), ('fake_type1', 'fake_type2')) def test_share_type_delete(self, type_ids): fake_share_types = [ share_types.ShareType('fake', {'id': type_id}, True) for type_id in type_ids ] self.mock_object( shell_v2, '_find_share_type', mock.Mock(side_effect=fake_share_types)) self.run_command('type-delete %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(('share_server_xyz', ), ('share_server_abc', 'share_server_xyz')) def test_share_server_delete_wait(self, share_servers_to_delete): fake_manager = mock.Mock() fake_share_servers = [ share_servers.ShareServer(fake_manager, {'id': '1234'}) for share_server in share_servers_to_delete ] share_server_not_found_error = ("Delete for share server %s " "failed: No server with a " "name or ID of '%s' exists.") share_servers_are_not_found_errors = [ exceptions.CommandError(share_server_not_found_error % (share_server, share_server)) for share_server in share_servers_to_delete ] self.mock_object( shell_v2, '_find_share_server', mock.Mock( side_effect=(fake_share_servers + share_servers_are_not_found_errors))) self.mock_object( shell_v2, '_wait_for_resource_status', mock.Mock() ) self.run_command('share-server-delete %s --wait' % ' ' .join(share_servers_to_delete)) shell_v2._find_share_server.assert_has_calls([ mock.call(self.shell.cs, share_server) for share_server in share_servers_to_delete ]) fake_manager.delete.assert_has_calls([ mock.call(share_server) for share_server in fake_share_servers]) shell_v2._wait_for_resource_status.assert_has_calls([ mock.call(self.shell.cs, share_server, resource_type='share_server', expected_status='deleted') for share_server in fake_share_servers ]) self.assertEqual(len(share_servers_to_delete), fake_manager.delete.call_count) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_message_list(self): self.run_command('message-list') self.assert_called('GET', '/messages') cliutils.print_list.assert_called_once_with( mock.ANY, fields=['ID', 'Resource Type', 'Resource ID', 'Action ID', 'User Message', 'Detail ID', 'Created At'], sortby_index=None) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_share_message_list_created_before_aliases(self): self.run_command('message-list --before 2001-01-01') self.assert_called( 'GET', '/messages?created_before=2001-01-01', ) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_share_message_list_created_since_aliases(self): self.run_command('message-list --since 2001-01-01') self.assert_called( 'GET', '/messages?created_since=2001-01-01', ) @mock.patch.object(cliutils, 'print_list', mock.Mock()) def test_message_list_select_column(self): self.run_command('message-list --columns id,resource_type') self.assert_called('GET', '/messages') cliutils.print_list.assert_called_once_with( mock.ANY, fields=['Id', 'Resource_Type'], sortby_index=None) def test_message_list_with_filters(self): self.run_command('message-list --limit 10 --offset 0') self.assert_called( 'GET', '/messages?limit=10&offset=0') def test_message_show(self): self.run_command('message-show 1234') self.assert_called('GET', '/messages/1234') @ddt.data(('1234', ), ('1234_error', ), ('1234_error', '5678'), ('1234', '5678_error'), ('1234', '5678')) def test_message_delete(self, ids): fake_messages = dict() for mid in ids: if mid.endswith('_error'): continue fake_messages[mid] = messages.Message('fake', {'id': mid}, True) def _find_message_with_errors(cs, mid): if mid.endswith('_error'): raise Exception return fake_messages[mid] self.mock_object( shell_v2, '_find_message', mock.Mock(side_effect=_find_message_with_errors)) cmd = 'message-delete %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) @ddt.data('migration-start', 'migration-check') def test_share_server_migration_start_and_check(self, method): command = ("share-server-%s " "1234 host@backend --new-share-network 1111 " "--writable False --nondisruptive True " "--preserve-snapshots True" % method) self.run_command(command) method = method.replace('-', '_') expected = {method: { 'host': 'host@backend', 'writable': 'False', 'nondisruptive': 'True', 'preserve_snapshots': 'True', 'new_share_network_id': 1111 }} self.assert_called('POST', '/share-servers/1234/action', body=expected) @ddt.data('migration-complete', 'migration-get-progress', 'migration-cancel') def test_share_server_migration_others(self, method): command = 'share-server-' + ' '.join((method, '1234')) self.run_command(command) expected = {method.replace('-', '_'): None} self.assert_called('POST', '/share-servers/1234/action', body=expected) @ddt.data('migration_error', 'migration_success', None) def test_share_server_reset_task_state(self, param): command = ' '.join(('share-server-reset-task-state --state', str(param), '1234')) self.run_command(command) expected = {'reset_task_state': {'task_state': param}} self.assert_called('POST', '/share-servers/1234/action', body=expected) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_type_access.py0000664000175000017500000000701300000000000026551 0ustar00zuulzuul00000000000000# 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. from unittest import mock import ddt 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}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/tests/unit/v2/test_types.py0000664000175000017500000004474600000000000025431 0ustar00zuulzuul00000000000000# 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 itertools from unittest import mock import ddt 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/utils.py0000664000175000017500000000407300000000000021703 0ustar00zuulzuul00000000000000# 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 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): return "%s.%s" % (func.__module__, func.__qualname__) 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", "~") ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5972784 python-manilaclient-4.8.0/manilaclient/v1/0000775000175000017500000000000000000000000020513 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/__init__.py0000664000175000017500000000162500000000000022630 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/client.py0000664000175000017500000002437200000000000022353 0ustar00zuulzuul00000000000000# 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=USERNAME, password=PASSWORD, project_name=PROJECT_NAME, auth_url=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, project_name=PROJECT_ID) >>> sess = session.Session(auth=auth) >>> manila = client.Client(VERSION, session=sess) Then call methods on its managers:: >>> client.shares.list() ... """ def __init__(self, username=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 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 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, cert=cert, 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)) 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5972784 python-manilaclient-4.8.0/manilaclient/v1/contrib/0000775000175000017500000000000000000000000022153 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/contrib/__init__.py0000664000175000017500000000000000000000000024252 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/contrib/list_extensions.py0000664000175000017500000000234500000000000025763 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/limits.py0000664000175000017500000000231100000000000022363 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/quota_classes.py0000664000175000017500000000235400000000000023737 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/quotas.py0000664000175000017500000000231100000000000022376 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/scheduler_stats.py0000664000175000017500000000236500000000000024267 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/security_services.py0000664000175000017500000000240500000000000024640 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/services.py0000664000175000017500000000232300000000000022710 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/share_networks.py0000664000175000017500000000236100000000000024125 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/share_servers.py0000664000175000017500000000235400000000000023744 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/share_snapshots.py0000664000175000017500000000235000000000000024271 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/share_type_access.py0000664000175000017500000000240500000000000024552 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/share_types.py0000664000175000017500000000234400000000000023416 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v1/shares.py0000664000175000017500000000227300000000000022356 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.6052804 python-manilaclient-4.8.0/manilaclient/v2/0000775000175000017500000000000000000000000020514 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/__init__.py0000664000175000017500000000125100000000000022624 0ustar00zuulzuul00000000000000# 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/availability_zones.py0000664000175000017500000000244100000000000024757 0ustar00zuulzuul00000000000000# 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 RESOURCE_PATH_LEGACY = '/os-availability-zone' RESOURCE_PATH = '/availability-zones' RESOURCE_NAME = 'availability_zones' class AvailabilityZone(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): # noqa return self._list(RESOURCE_PATH, RESOURCE_NAME) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/client.py0000664000175000017500000003157200000000000022354 0ustar00zuulzuul00000000000000# 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 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 resource_locks 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_backups 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_transfers 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=USERNAME, password=PASSWORD, auth_url=AUTH_URL, project_name=PROJECT_NAME) 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() ... """ def __init__(self, username=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 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 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, cert=cert, 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.transfers = share_transfers.ShareTransferManager(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.resource_locks = resource_locks.ResourceLockManager(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.share_backups = share_backups.ShareBackupManager(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)) 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.6052804 python-manilaclient-4.8.0/manilaclient/v2/contrib/0000775000175000017500000000000000000000000022154 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/contrib/__init__.py0000664000175000017500000000000000000000000024253 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/contrib/list_extensions.py0000664000175000017500000000265000000000000025763 0ustar00zuulzuul00000000000000# 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 import cliutils class ListExtResource(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/limits.py0000664000175000017500000000547100000000000022376 0ustar00zuulzuul00000000000000# 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 class Limits(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.remaining = self.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") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/messages.py0000664000175000017500000000522600000000000022702 0ustar00zuulzuul00000000000000# 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 import constants RESOURCES_PATH = '/messages' RESOURCE_PATH = '/messages/%s' RESOURCES_NAME = 'messages' RESOURCE_NAME = 'message' class Message(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 % base.getid(message) return self._delete(loc) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/quota_classes.py0000664000175000017500000001366700000000000023751 0ustar00zuulzuul00000000000000# 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 RESOURCE_PATH_LEGACY = '/os-quota-class-sets' RESOURCE_PATH = '/quota-class-sets' REPLICA_QUOTAS_MICROVERSION = "2.53" class QuotaClassSet(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): # noqa 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, per_share_gigabytes=None, share_groups=None, share_group_snapshots=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, 'per_share_gigabytes': per_share_gigabytes, 'share_groups': share_groups, 'share_group_snapshots': share_group_snapshots, } } 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.39") # noqa def update(self, class_name, shares=None, gigabytes=None, # noqa 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("2.40", "2.52") # noqa def update(self, class_name, shares=None, gigabytes=None, # noqa snapshots=None, snapshot_gigabytes=None, share_networks=None, share_groups=None, share_group_snapshots=None): return self._do_update( class_name, shares=shares, gigabytes=gigabytes, snapshots=snapshots, snapshot_gigabytes=snapshot_gigabytes, share_networks=share_networks, share_groups=share_groups, share_group_snapshots=share_group_snapshots, resource_path=RESOURCE_PATH) @api_versions.wraps(REPLICA_QUOTAS_MICROVERSION, "2.61") # noqa def update(self, class_name, shares=None, gigabytes=None, # noqa snapshots=None, snapshot_gigabytes=None, share_networks=None, share_groups=None, share_group_snapshots=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_groups=share_groups, share_group_snapshots=share_group_snapshots, share_replicas=share_replicas, replica_gigabytes=replica_gigabytes, resource_path=RESOURCE_PATH) @api_versions.wraps("2.62") # noqa def update(self, class_name, shares=None, gigabytes=None, # noqa snapshots=None, snapshot_gigabytes=None, share_networks=None, share_groups=None, share_group_snapshots=None, share_replicas=None, replica_gigabytes=None, per_share_gigabytes=None): return self._do_update( class_name, shares=shares, gigabytes=gigabytes, snapshots=snapshots, snapshot_gigabytes=snapshot_gigabytes, share_networks=share_networks, share_groups=share_groups, share_group_snapshots=share_group_snapshots, share_replicas=share_replicas, replica_gigabytes=replica_gigabytes, per_share_gigabytes=per_share_gigabytes, resource_path=RESOURCE_PATH) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/quotas.py0000664000175000017500000002545500000000000022415 0ustar00zuulzuul00000000000000# 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 RESOURCE_PATH_LEGACY = '/os-quota-sets' RESOURCE_PATH = '/quota-sets' REPLICA_QUOTAS_MICROVERSION = "2.53" class QuotaSet(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): # noqa 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): # noqa 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): # noqa 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, per_share_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, 'per_share_gigabytes': per_share_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, # noqa 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, # noqa 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, # noqa 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, "2.61") # noqa def update(self, tenant_id, user_id=None, share_type=None, # noqa 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("2.62") # noqa def update(self, tenant_id, user_id=None, share_type=None, # noqa 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, per_share_gigabytes=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, per_share_gigabytes=per_share_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): # noqa 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): # noqa 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): # noqa return self._do_delete( tenant_id, user_id, share_type, resource_path=RESOURCE_PATH) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/resource_locks.py0000664000175000017500000001115100000000000024107 0ustar00zuulzuul00000000000000# 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 import constants class ResourceLock(base.Resource): """Lock a share resource action from being executed""" def __repr__(self): return "" % self.id def delete(self): """Delete this lock.""" return self.manager.delete(self) def update(self, **kwargs): """Update this lock.""" return self.manager.update(self, **kwargs) class ResourceLockManager(base.ManagerWithFind): """Manage :class:`ResourceLock` resources.""" resource_class = ResourceLock @api_versions.wraps(constants.RESOURCE_LOCK_VERSION) def create(self, resource_id, resource_type, resource_action='delete', lock_reason=None): """Creates a resource lock. :param resource_id: The ID of the resource to lock :param resource_type: The type of the resource (e.g., "share", "access") :param resource_action: The functionality to lock (e.g., "delete", "view/delete") :param lock_reason: Lock description :rtype: :class:`ResourceLock` """ body = { 'resource_lock': { 'resource_id': resource_id, 'resource_type': resource_type, 'resource_action': resource_action, 'lock_reason': lock_reason, } } return self._create('/resource-locks', body, 'resource_lock') @api_versions.wraps(constants.RESOURCE_LOCK_VERSION) def get(self, lock_id): """Show details of a resource lock. :param lock_id: The ID of the resource lock to display. :rtype: :class:`ResourceLock` """ return self._get("/resource-locks/%s" % lock_id, "resource_lock") @api_versions.wraps(constants.RESOURCE_LOCK_VERSION) def list(self, search_opts=None, sort_key=None, sort_dir=None): """Get a list of all resource locks. :param search_opts: Filtering options as a dictionary. :param sort_key: Key to be sorted (i.e. 'created_at'). :param sort_dir: Sort direction, should be 'desc' or 'asc'. :rtype: list of :class:`ResourceLock` """ search_opts = search_opts or {} sort_key = sort_key or 'created_at' if sort_key in constants.RESOURCE_LOCK_SORT_KEY_VALUES: search_opts['sort_key'] = sort_key else: raise ValueError( 'sort_key must be one of the following: %s.' % ', '.join(constants.RESOURCE_LOCK_SORT_KEY_VALUES)) sort_dir = sort_dir or 'desc' 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 = "/resource-locks%s" % (query_string,) return self._list(path, 'resource_locks') @api_versions.wraps(constants.RESOURCE_LOCK_VERSION) def update(self, lock, **kwargs): """Updates a resource lock. :param lock: The :class:`ResourceLock` object, or a lock id to update. :param kwargs: "resource_action" and "lock_reason" are allowed kwargs :rtype: :class:`ResourceLock` """ if not kwargs: return body = {'resource_lock': {}} if 'lock_reason' in kwargs: body['resource_lock']['lock_reason'] = kwargs['lock_reason'] if 'resource_action' in kwargs: body['resource_lock']['resource_action'] = ( kwargs['resource_action'] ) lock_id = base.getid(lock) return self._update("/resource-locks/%s" % lock_id, body) @api_versions.wraps(constants.RESOURCE_LOCK_VERSION) def delete(self, lock): """Delete a resource lock. :param lock: The :class:`ResourceLock` object, or a lock id to delete. """ return self._delete("/resource-locks/%s" % base.getid(lock)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/scheduler_stats.py0000664000175000017500000000277000000000000024270 0ustar00zuulzuul00000000000000# 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 RESOURCES_PATH = '/scheduler-stats/pools' RESOURCES_NAME = 'pools' class Pool(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/security_services.py0000664000175000017500000002300400000000000024637 0ustar00zuulzuul00000000000000# 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 import exceptions RESOURCES_PATH = '/security-services' RESOURCE_PATH = "/security-services/%s" RESOURCE_NAME = 'security_service' RESOURCES_NAME = 'security_services' class SecurityService(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 @api_versions.wraps("1.0", "2.75") 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` """ return self._create_security_service(type, dns_ip=dns_ip, ou=ou, server=server, domain=domain, user=user, password=password, name=name, description=description) @api_versions.wraps("2.76") # noqa def create(self, type, dns_ip=None, ou=None, server=None, # noqa domain=None, user=None, password=None, name=None, description=None, default_ad_site=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 :param default_ad_site: default AD-Site :rtype: :class:`SecurityService` """ return self._create_security_service(type, dns_ip=dns_ip, ou=ou, server=server, domain=domain, user=user, password=password, name=name, description=description, default_ad_site=default_ad_site) def _create_security_service(self, type, dns_ip=None, ou=None, server=None, domain=None, user=None, password=None, name=None, description=None, default_ad_site=None): 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 if default_ad_site: values['default_ad_site'] = default_ad_site 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 % base.getid(security_service), RESOURCE_NAME, ) @api_versions.wraps("1.0", "2.75") 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` """ return self._update_security_service(security_service, dns_ip=dns_ip, ou=ou, server=server, domain=domain, password=password, user=user, name=name, description=description) @api_versions.wraps("2.76") # noqa def update(self, security_service, dns_ip=None, ou=None, # noqa server=None, domain=None, password=None, user=None, name=None, description=None, default_ad_site=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 :param default_ad_site: default AD-Site :rtype: :class:`SecurityService` """ return self._update_security_service(security_service, dns_ip=dns_ip, ou=ou, server=server, domain=domain, password=password, user=user, name=name, description=description, default_ad_site=default_ad_site) def _update_security_service(self, security_service, dns_ip=None, ou=None, server=None, domain=None, password=None, user=None, name=None, description=None, default_ad_site=None): 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 if default_ad_site is not None: values['default_ad_site'] = default_ad_site 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 % 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 % 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/services.py0000664000175000017500000000720500000000000022715 0ustar00zuulzuul00000000000000# 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 RESOURCE_PATH_LEGACY = '/os-services' RESOURCE_PATH = '/services' RESOURCE_NAME = 'services' class Service(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): # noqa 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): # noqa return self._do_enable(host, binary, RESOURCE_PATH) def _do_disable(self, host, binary, resource_path=RESOURCE_PATH, disable_reason=None): """Disable the service specified by hostname and binary.""" body = {"host": host, "binary": binary} if disable_reason: body["disabled_reason"] = disable_reason 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", "2.82") # noqa def disable(self, host, binary): # noqa return self._do_disable(host, binary, RESOURCE_PATH) @api_versions.wraps("2.83") # noqa def disable(self, host, binary, disable_reason=None): # noqa return self._do_disable(host, binary, RESOURCE_PATH, disable_reason=disable_reason) 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 [] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_access_rules.py0000664000175000017500000000566200000000000024734 0ustar00zuulzuul00000000000000# 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 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(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 = 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 = 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 % (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'] = base.getid(share) query_string = self._build_query_string(search_opts) url = RESOURCE_LIST_PATH + query_string return self._list(url, 'access_list') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_backups.py0000664000175000017500000001134000000000000023677 0ustar00zuulzuul00000000000000# 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 import constants RESOURCES_NAME = 'share_backups' RESOURCE_NAME = 'share_backup' RESOURCES_PATH = '/share-backups' RESOURCE_PATH = '/share-backups/%s' RESOURCE_PATH_ACTION = '/share-backups/%s/action' class ShareBackup(base.Resource): def __repr__(self): return "" % self.id class ShareBackupManager(base.ManagerWithFind): """Manage :class:`ShareBackup` resources.""" resource_class = ShareBackup @api_versions.wraps("2.80") @api_versions.experimental_api def get(self, backup): """Get a share backup. :param backup: either backup object or its UUID. :rtype: :class:`ShareBackup` """ backup_id = base.getid(backup) return self._get(RESOURCE_PATH % backup_id, RESOURCE_NAME) @api_versions.wraps("2.80") @api_versions.experimental_api def list(self, detailed=True, search_opts=None, sort_key=None, sort_dir=None): """List all share backups or list backups belonging to a share. :param detailed: list backups with detailed fields. :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:`ShareBackup` """ search_opts = search_opts or {} if sort_key is not None: if sort_key in constants.BACKUP_SORT_KEY_VALUES: search_opts['sort_key'] = sort_key else: raise ValueError( 'sort_key must be one of the following: %s.' % ', '.join(constants.BACKUP_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 = "/share-backups/detail%s" % (query_string,) else: path = "/share-backups%s" % (query_string,) return self._list(path, 'share_backups') @api_versions.wraps("2.80") @api_versions.experimental_api def create(self, share, backup_options=None, description=None, name=None): """Create a backup for a share. :param share: The share to create the backup of. Can be the share object or its UUID. :param backup_options: dict - custom set of key-values :param name: text - name of new share :param description: - description for new share """ share_id = base.getid(share) body = { 'share_id': share_id, 'backup_options': backup_options, 'description': description, 'name': name, } return self._create(RESOURCES_PATH, {RESOURCE_NAME: body}, RESOURCE_NAME) @api_versions.wraps("2.80") @api_versions.experimental_api def delete(self, backup): backup_id = base.getid(backup) url = RESOURCE_PATH % backup_id self._delete(url) @api_versions.wraps("2.80") @api_versions.experimental_api def restore(self, backup): return self._action('restore', backup) @api_versions.wraps("2.80") @api_versions.experimental_api def reset_status(self, backup, state): return self._action('reset_status', backup, {"status": state}) @api_versions.wraps("2.80") @api_versions.experimental_api def update(self, backup, **kwargs): if not kwargs: return backup_id = base.getid(backup) body = {'share_backup': kwargs} return self._update(RESOURCE_PATH % backup_id, body) def _action(self, action, backup, info=None, **kwargs): body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) backup_id = base.getid(backup) url = RESOURCE_PATH_ACTION % backup_id return self.api.client.post(url, body=body) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_export_locations.py0000664000175000017500000000341100000000000025643 0ustar00zuulzuul00000000000000# 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 class ShareExportLocation(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 = 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 = base.getid(share) export_location_id = 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") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_group_snapshots.py0000664000175000017500000002215600000000000025514 0ustar00zuulzuul00000000000000# 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 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(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 = 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): # noqa 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 = 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): # noqa 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, # noqa 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 = 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): # noqa 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 = 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): # noqa 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 = 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): # noqa self._share_group_snapshot_reset_state(share_group_snapshot, state) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_group_type_access.py0000664000175000017500000000715700000000000026000 0ustar00zuulzuul00000000000000# 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 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(base.Resource): def __repr__(self): return "" % self.share_group_type_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 = 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): # noqa 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): # noqa """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): # noqa """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 = base.getid(share_group_type) url = RESOURCE_PATH_ACTION % share_group_type_id return self.api.client.post(url, body=body) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_group_types.py0000664000175000017500000001630700000000000024637 0ustar00zuulzuul00000000000000# 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 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(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 = 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 = 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 = 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': [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): # noqa 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 = 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"): # noqa 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`. """ search_opts = search_opts or {} if show_all: search_opts['is_public'] = 'all' query_string = self._build_query_string(search_opts) 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): # noqa 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 = 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): # noqa self._delete_share_group_type(share_group_type) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_groups.py0000664000175000017500000002571500000000000023601 0ustar00zuulzuul00000000000000# 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 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(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'] = base.getid(share_group_type) if share_network: body['share_network_id'] = base.getid(share_network) if source_share_group_snapshot: body['source_share_group_snapshot_id'] = base.getid( source_share_group_snapshot) elif share_types: body['share_types'] = [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, # noqa 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 = 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): # noqa 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, # noqa 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 = 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): # noqa 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 = 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): # noqa 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 = 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): # noqa self._share_group_reset_state(share_group, state) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_instance_export_locations.py0000664000175000017500000000371200000000000027533 0ustar00zuulzuul00000000000000# 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 class ShareInstanceExportLocation(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 = 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 = base.getid(share_instance) export_location_id = 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") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_instances.py0000664000175000017500000001020400000000000024234 0ustar00zuulzuul00000000000000# 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 class ShareInstance(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 = 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): # noqa """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' % 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, 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): # noqa 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): # noqa return self._do_reset_state(instance, state, "reset_status") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_network_subnets.py0000664000175000017500000001214100000000000025503 0ustar00zuulzuul00000000000000# 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 api_versions from manilaclient import 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(base.MetadataCapableResource): """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.MetadataCapableManager): """Manage :class:`ShareNetworkSubnet` resources.""" resource_class = ShareNetworkSubnet resource_path = '/share-networks' subresource_path = '/subnets' def _do_create(self, neutron_net_id=None, neutron_subnet_id=None, availability_zone=None, share_network_id=None, metadata=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 :param metadata: dict - optional metadata to set on share creation :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 if metadata: values['metadata'] = metadata 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) @api_versions.wraps("2.0", "2.77") def create(self, neutron_net_id=None, neutron_subnet_id=None, availability_zone=None, share_network_id=None): return self._do_create(neutron_net_id, neutron_subnet_id, availability_zone, share_network_id) @api_versions.wraps("2.78") def create(self, neutron_net_id=None, neutron_subnet_id=None, # noqa F811 availability_zone=None, share_network_id=None, metadata=None): return self._do_create(neutron_net_id, neutron_subnet_id, availability_zone, share_network_id, metadata) 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 = base.getid(share_network) share_network_subnet_id = 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': base.getid(share_network), 'share_network_subnet': share_network_subnet } self._delete(url) @api_versions.wraps('2.78') def get_metadata(self, share_network, share_network_subnet): return super(ShareNetworkSubnetManager, self).get_metadata( share_network, subresource=share_network_subnet) @api_versions.wraps('2.78') def set_metadata(self, resource, metadata, subresource=None): return super(ShareNetworkSubnetManager, self).set_metadata( resource, metadata, subresource=subresource) @api_versions.wraps('2.78') def delete_metadata(self, resource, keys, subresource=None): return super(ShareNetworkSubnetManager, self).delete_metadata( resource, keys, subresource=subresource) @api_versions.wraps('2.78') def update_all_metadata(self, resource, metadata, subresource=None): return super(ShareNetworkSubnetManager, self).update_all_metadata( resource, metadata, subresource=subresource) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_networks.py0000664000175000017500000003144600000000000024134 0ustar00zuulzuul00000000000000# 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 import exceptions RESOURCES_PATH = '/share-networks' RESOURCE_PATH = "/share-networks/%s" RESOURCE_NAME = 'share_network' RESOURCES_NAME = 'share_networks' ACTION_PATH = RESOURCE_PATH + '/action' class ShareNetwork(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, # noqa 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, # noqa 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 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': base.getid(security_service), }, } return self._create( RESOURCE_PATH % 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 % 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 % base.getid(share_network), body, RESOURCE_NAME) @api_versions.wraps("2.26") # noqa def update(self, share_network, neutron_net_id=None, # noqa 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 % 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 % 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) def _action(self, action, share_network, info=None): """Perform a share network 'action'. :param action: text with action name. :param share_network: either share_network object or text with its ID. :param info: dict with data for specified 'action'. """ body = {action: info} self.run_hooks('modify_body_for_action', body) url = ACTION_PATH % base.getid(share_network) return self.api.client.post(url, body=body) 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` """ info = { 'security_service_id': base.getid(security_service), } return self._action('add_security_service', share_network, info) @api_versions.wraps("2.63") def add_security_service_check(self, share_network, security_service, reset_operation=False): """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 :param reset_operation: start over the check operation :rtype: :class:`ShareNetwork` """ info = { 'security_service_id': base.getid(security_service), 'reset_operation': reset_operation, } return self._action('add_security_service_check', share_network, info) @api_versions.wraps("2.63") def update_share_network_security_service(self, share_network, current_security_service, new_security_service): """Update current security service to new one of a given share network. :param share_network: share network name, id or ShareNetwork instance :param current_security_service: current name, id or SecurityService instance that will be changed :param new_security_service: new name, id or SecurityService instance that will be updated :rtype: :class:`ShareNetwork` """ info = { 'current_service_id': base.getid(current_security_service), 'new_service_id': base.getid(new_security_service)} return self._action('update_security_service', share_network, info) @api_versions.wraps("2.63") def update_share_network_security_service_check( self, share_network, current_security_service, new_security_service, reset_operation=False): """Validates if the security service update is supported by all hosts. :param share_network: share network name, id or ShareNetwork instance :param current_security_service: current name, id or SecurityService instance that will be changed :param new_security_service: new name, id or :param reset_operation: start over the check operation SecurityService instance that will be updated :rtype: :class:`ShareNetwork` """ info = { 'current_service_id': base.getid(current_security_service), 'new_service_id': base.getid(new_security_service), 'reset_operation': reset_operation } return self._action('update_security_service_check', share_network, info) @api_versions.wraps("2.63") def reset_state(self, share_network, state): """Reset state of a share network. :param share_network: either share_network object or text with its ID or name. :param state: text with new state to set for share network. """ return self._action('reset_status', share_network, {"status": state}) @api_versions.wraps("2.70") def share_network_subnet_create_check(self, share_network_id, neutron_net_id=None, neutron_subnet_id=None, availability_zone=None, reset_operation=False): """Check if share network subnet can be created in given share network. :param share_network_id: ID of the Share network :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 :param reset_operation: Start over the check operation :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 values['reset_operation'] = reset_operation return self._action('share_network_subnet_create_check', share_network_id, values) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_replica_export_locations.py0000664000175000017500000000557700000000000027361 0ustar00zuulzuul00000000000000# 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 import constants class ShareReplicaExportLocation(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", constants.REPLICA_PRE_GRADUATION_VERSION) @api_versions.experimental_api def list(self, share_replica, search_opts=None): """List all share replica export locations.""" share_replica_id = base.getid(share_replica) return self._list( "/share-replicas/%s/export-locations" % share_replica_id, "export_locations") @api_versions.wraps(constants.REPLICA_GRADUATION_VERSION) # noqa def list(self, share_replica, search_opts=None): # noqa F811 """List all share replica export locations.""" share_replica_id = base.getid(share_replica) return self._list( "/share-replicas/%s/export-locations" % share_replica_id, "export_locations") @api_versions.wraps("2.47", constants.REPLICA_PRE_GRADUATION_VERSION) @api_versions.experimental_api def get(self, share_replica, export_location): return self._get_replica_export_location( share_replica, export_location) @api_versions.wraps(constants.REPLICA_GRADUATION_VERSION) # noqa def get(self, share_replica, export_location): # noqa F811 return self._get_replica_export_location( share_replica, export_location) def _get_replica_export_location(self, share_replica, export_location): """Get a share replica export location.""" share_replica_id = base.getid(share_replica) export_location_id = 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") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_replicas.py0000664000175000017500000002574200000000000024064 0ustar00zuulzuul00000000000000# 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 import constants 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(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", constants.REPLICA_PRE_GRADUATION_VERSION) @api_versions.experimental_api def get(self, replica): return self._get_share_replica(replica) @api_versions.wraps(constants.REPLICA_GRADUATION_VERSION) # noqa def get(self, replica): # noqa F811 return self._get_share_replica(replica) def _get_share_replica(self, replica): """Get a share replica. :param replica: either replica object or its UUID. :rtype: :class:`ShareReplica` """ replica_id = base.getid(replica) return self._get(RESOURCE_PATH % replica_id, RESOURCE_NAME) @api_versions.wraps("2.11", constants.REPLICA_PRE_GRADUATION_VERSION) @api_versions.experimental_api def list(self, share=None, search_opts=None): return self._list_share_replicas(share=share, search_opts=search_opts) @api_versions.wraps(constants.REPLICA_GRADUATION_VERSION) # noqa def list(self, share=None, search_opts=None): # noqa F811 return self._list_share_replicas(share=share, search_opts=search_opts) def _list_share_replicas(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=' + 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", constants.REPLICA_PRE_GRADUATION_VERSION) @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(constants.REPLICA_GRADUATION_VERSION, '2.74') # noqa def promote(self, replica): # noqa F811 """Promote the provided replica. :param replica: either replica object or its UUID. """ return self._action('promote', replica) @api_versions.wraps('2.75') # noqa def promote(self, replica, quiesce_wait_time=None): # noqa F811 """Promote the provided replica. :param replica: either replica object or its UUID. :param body: either replica object or its UUID. """ body = None if quiesce_wait_time: body = dict(quiesce_wait_time=quiesce_wait_time) return self._action('promote', replica, body) @api_versions.wraps("2.11", constants.REPLICA_PRE_GRADUATION_VERSION) @api_versions.experimental_api def create(self, share, availability_zone=None): return self._create_share_replica( share, availability_zone=availability_zone) @api_versions.wraps(constants.REPLICA_GRADUATION_VERSION, '2.66') # noqa def create(self, share, availability_zone=None): # noqa F811 return self._create_share_replica( share, availability_zone=availability_zone) @api_versions.wraps("2.67", "2.71") # noqa def create(self, share, availability_zone=None, scheduler_hints=None): # noqa F811 return self._create_share_replica( share, availability_zone=availability_zone, scheduler_hints=scheduler_hints) @api_versions.wraps("2.72") # noqa def create(self, share, # pylint: disable=function-redefined # noqa F811 availability_zone=None, scheduler_hints=None, share_network=None): return self._create_share_replica( share, availability_zone=availability_zone, scheduler_hints=scheduler_hints, share_network=share_network) def _create_share_replica(self, share, availability_zone=None, scheduler_hints=None, share_network=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. :param scheduler_hints: The scheduler_hints as key=value pair. Only supported key is 'only_host'. :param share_network: either share network object or its UUID. """ share_id = base.getid(share) body = {'share_id': share_id} if availability_zone: body['availability_zone'] = base.getid(availability_zone) if scheduler_hints: body['scheduler_hints'] = scheduler_hints if share_network: body['share_network_id'] = base.getid(share_network) return self._create(RESOURCES_PATH, {RESOURCE_NAME: body}, RESOURCE_NAME) @api_versions.wraps("2.11", constants.REPLICA_PRE_GRADUATION_VERSION) @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(constants.REPLICA_GRADUATION_VERSION) # noqa def delete(self, replica, force=False): # noqa F811 """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", constants.REPLICA_PRE_GRADUATION_VERSION) @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(constants.REPLICA_GRADUATION_VERSION) # noqa def reset_state(self, replica, state): # noqa F811 """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", constants.REPLICA_PRE_GRADUATION_VERSION) @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(constants.REPLICA_GRADUATION_VERSION) # noqa def reset_replica_state(self, replica, state): # noqa F811 """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", constants.REPLICA_PRE_GRADUATION_VERSION) @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) @api_versions.wraps(constants.REPLICA_GRADUATION_VERSION) # noqa def resync(self, replica): # noqa F811 """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 = 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 = 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, 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}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_servers.py0000664000175000017500000002501100000000000023740 0ustar00zuulzuul00000000000000# 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 RESOURCES_NAME = 'share_servers' RESOURCES_PATH = '/share-servers' RESOURCE_PATH = RESOURCES_PATH + '/%s' RESOURCE_NAME = 'share_server' ACTION_PATH = RESOURCE_PATH + '/action' class ShareServer(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) def migration_check(self, host, writable, nondisruptive, preserve_snapshots, new_share_network_id=None): """Check if the new host is suitable for migration.""" return self.manager.migration_check( self, host, writable, nondisruptive, preserve_snapshots, new_share_network_id=new_share_network_id) def migration_start(self, host, writable, nondisruptive, preserve_snapshots, new_share_network_id=None): """Migrate the share server to a new host.""" self.manager.migration_start( self, host, writable, nondisruptive, preserve_snapshots, new_share_network_id=new_share_network_id) def migration_complete(self): """Complete migration of a share server.""" return self.manager.migration_complete(self) def migration_cancel(self): """Attempts to cancel migration of a share server.""" self.manager.migration_cancel(self) def migration_get_progress(self): """Obtain progress of migration of a share server.""" return self.manager.migration_get_progress(self) def reset_task_state(self, task_state): """Reset the task state of a given share server.""" self.manager.reset_task_state(self, task_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 = 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 = 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 = 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, # noqa 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'. """ body = {action: info} self.run_hooks('modify_body_for_action', body) url = ACTION_PATH % base.getid(share_server) return self.api.client.post(url, body=body) @api_versions.wraps("2.57") @api_versions.experimental_api def migration_check(self, share_server, host, writable, nondisruptive, preserve_snapshots, new_share_network_id=None): """Check the share server migration to a new host :param share_server: either share_server object or text with its ID. :param host: Destination host where share server will be migrated. :param writable: Enforces migration to keep the shares writable. :param nondisruptive: Enforces migration to be nondisruptive. :param preserve_snapshots: Enforces migration to preserve snapshots. :param new_share_network_id: Specify the new share network id. """ result = self._action( "migration_check", share_server, { "host": host, "preserve_snapshots": preserve_snapshots, "writable": writable, "nondisruptive": nondisruptive, "new_share_network_id": new_share_network_id, }) return result[1] @api_versions.wraps("2.57") @api_versions.experimental_api def migration_start(self, share_server, host, writable, nondisruptive, preserve_snapshots, new_share_network_id=None): """Migrates share server to a new host :param share_server: either share_server object or text with its ID. :param host: Destination host where share server will be migrated. :param writable: Enforces migration to keep the shares writable. :param nondisruptive: Enforces migration to be nondisruptive. :param preserve_snapshots: Enforces migration to preserve snapshots. :param new_share_network_id: Specify the new share network id. """ return self._action( "migration_start", share_server, { "host": host, "writable": writable, "nondisruptive": nondisruptive, "preserve_snapshots": preserve_snapshots, "new_share_network_id": new_share_network_id, }) @api_versions.wraps("2.57") @api_versions.experimental_api def reset_task_state(self, share_server, task_state): """Update the provided share server with the provided task state. :param share_server: either share_server 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_server, {"task_state": task_state}) @api_versions.wraps("2.57") @api_versions.experimental_api def migration_complete(self, share_server): """Completes migration for a given share server. :param share_server: either share_server object or text with its ID. """ result = self._action('migration_complete', share_server) # NOTE(dviroel): result[0] is response code, result[1] is dict body return result[1] @api_versions.wraps("2.57") @api_versions.experimental_api def migration_cancel(self, share_server): """Attempts to cancel migration for a given share server. :param share_server: either share_server object or text with its ID. """ return self._action('migration_cancel', share_server) @api_versions.wraps("2.57") @api_versions.experimental_api def migration_get_progress(self, share_server): """Obtains progress of share migration for a given share server. :param share_server: either share_server object or text with its ID. """ result = self._action('migration_get_progress', share_server) # NOTE(felipefutty): result[0] is response code, result[1] is dict body return result[1] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_snapshot_export_locations.py0000664000175000017500000000344200000000000027566 0ustar00zuulzuul00000000000000# 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 class ShareSnapshotExportLocation(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" % base.getid(snapshot), 'share_snapshot_export_locations') @api_versions.wraps("2.32") def get(self, export_location, snapshot=None): params = { "snapshot_id": base.getid(snapshot), "export_location_id": base.getid(export_location), } return self._get("/snapshots/%(snapshot_id)s/export-locations/" "%(export_location_id)s" % params, "share_snapshot_export_location") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_snapshot_instance_export_locations.py0000664000175000017500000000362400000000000031454 0ustar00zuulzuul00000000000000# 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 class ShareSnapshotInstanceExportLocation(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" % 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": base.getid(snapshot_instance), "export_location_id": base.getid(export_location), } return self._get("/snapshot-instances/%(snapshot_instance_id)s/" "export-locations/%(export_location_id)s" % params, "share_snapshot_export_location") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_snapshot_instances.py0000664000175000017500000000521400000000000026160 0ustar00zuulzuul00000000000000# 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 class ShareSnapshotInstance(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 = 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' % 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' % base.getid(instance)) return self.api.client.post(url, body=body) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_snapshots.py0000664000175000017500000002207400000000000024277 0ustar00zuulzuul00000000000000# 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 import constants class ShareSnapshot(base.MetadataCapableResource): """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.MetadataCapableManager): """Manage :class:`ShareSnapshot` resources.""" resource_class = ShareSnapshot resource_path = '/snapshots' def _do_create(self, share, force=False, name=None, description=None, metadata=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 :param metadata: dict - optional metadata to set on share creation :rtype: :class:`ShareSnapshot` """ metadata = metadata if metadata is not None else dict() body = {'snapshot': {'share_id': base.getid(share), 'force': force, 'name': name, 'description': description, 'metadata': metadata}} return self._create('/snapshots', body, 'snapshot') @api_versions.wraps("2.0", "2.72") def create(self, share, force=False, name=None, description=None): return self._do_create(share, force, name, description) @api_versions.wraps("2.73") def create(self, share, force=False, name=None, description=None,# noqa F811 metadata=None): return self._do_create(share, force, name, description, metadata) @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': 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 = 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" % 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, 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): # noqa 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 = 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): # noqa 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 = 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' % base.getid(snapshot) return self.api.client.post(url, body=body) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_transfers.py0000664000175000017500000001065600000000000024267 0ustar00zuulzuul00000000000000# Copyright (c) 2022 China Telecom Digital Intelligence. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from manilaclient import api_versions from manilaclient import base from manilaclient.common import constants class ShareTransfer(base.Resource): """Transfer a share from one tenant to another""" def __repr__(self): return "" % self.id def delete(self): """Delete this share transfer.""" return self.manager.delete(self) class ShareTransferManager(base.ManagerWithFind): """Manage :class:`ShareTransfer` resources.""" resource_class = ShareTransfer @api_versions.wraps(constants.SHARE_TRANSFER_VERSION) def create(self, share_id, name=None): """Creates a share transfer. :param share_id: The ID of the share to transfer. :param name: The name of the transfer. :rtype: :class:`ShareTransfer` """ body = {'transfer': {'share_id': share_id, 'name': name}} return self._create('/share-transfers', body, 'transfer') @api_versions.wraps(constants.SHARE_TRANSFER_VERSION) def accept(self, transfer, auth_key, clear_access_rules=False): """Accept a share transfer. :param transfer_id: The ID of the transfer to accept. :param auth_key: The auth_key of the transfer. :param clear_access_rules: Transfer share without access rules :rtype: :class:`ShareTransfer` """ transfer_id = base.getid(transfer) body = {'accept': {'auth_key': auth_key, 'clear_access_rules': clear_access_rules}} self._accept('/share-transfers/%s/accept' % transfer_id, body) @api_versions.wraps(constants.SHARE_TRANSFER_VERSION) def get(self, transfer_id): """Show details of a share transfer. :param transfer_id: The ID of the share transfer to display. :rtype: :class:`ShareTransfer` """ return self._get("/share-transfers/%s" % transfer_id, "transfer") @api_versions.wraps(constants.SHARE_TRANSFER_VERSION) def list(self, detailed=True, search_opts=None, sort_key=None, sort_dir=None): """Get a list of all share transfer. :param detailed: Get detailed object information. :param search_opts: Filtering options. :param sort_key: Key to be sorted (i.e. 'created_at'). :param sort_dir: Sort direction, should be 'desc' or 'asc'. :rtype: list of :class:`ShareTransfer` """ if search_opts is None: search_opts = {} if sort_key is not None: if sort_key in constants.SHARE_TRANSFER_SORT_KEY_VALUES: search_opts['sort_key'] = sort_key # NOTE: Replace aliases with appropriate keys if sort_key == 'name': search_opts['sort_key'] = 'display_name' else: raise ValueError( 'sort_key must be one of the following: %s.' % ', '.join(constants.SHARE_TRANSFER_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 = "/share-transfers/detail%s" % (query_string,) else: path = "/share-transfers%s" % (query_string,) return self._list(path, 'transfers') @api_versions.wraps(constants.SHARE_TRANSFER_VERSION) def delete(self, transfer_id): """Delete a share transfer. :param transfer_id: The :class:`ShareTransfer` to delete. """ return self._delete("/share-transfers/%s" % base.getid(transfer_id)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_type_access.py0000664000175000017500000000450500000000000024556 0ustar00zuulzuul00000000000000# 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 class ShareTypeAccess(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": 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): # noqa 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' % base.getid(share_type) return self.api.client.post(url, body=body) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/share_types.py0000664000175000017500000002410300000000000023414 0ustar00zuulzuul00000000000000# 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 import exceptions class ShareType(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" % 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" % 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" % (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 type. :param share type: either share type object or text with its ID. :rtype: :class:`ShareType` """ type_id = 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" % 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" % 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" % 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, # noqa 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, # noqa 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, # noqa 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/shares.py0000664000175000017500000007614200000000000022365 0ustar00zuulzuul00000000000000# 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 string from manilaclient import api_versions from manilaclient import base from manilaclient.common import constants from manilaclient import exceptions from manilaclient.v2 import share_instances class Share(base.MetadataCapableResource): """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, *args, **kwargs): """Allow access to a share.""" return self.manager.allow(self, *args, **kwargs) def deny(self, id, **kwargs): """Deny access from IP to a share.""" return self.manager.deny(self, id, **kwargs) def access_list(self): """Get access list from a share.""" return self.manager.access_list(self) def reset_state(self, state): """Update the share with the provided state.""" self.manager.reset_state(self, state) def extend(self, new_size, **kwargs): """Extend the size of the specified share.""" self.manager.extend(self, new_size, **kwargs) 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) def soft_delete(self): """Soft delete a share to recycle bin""" self.manager.soft_delete(self) def restore(self): """Restore a share from recycle bin""" self.manager.restore(self) class ShareManager(base.MetadataCapableManager): """Manage :class:`Share` resources.""" resource_class = Share resource_path = '/shares' 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, scheduler_hints=None, return_raw=False): """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 :param scheduler_hints: dict - hints for the scheduler to place share on most appropriate host e.g. keys are same_host for affinity and different_host for anti-affinity :rtype: :class:`Share` """ share_metadata = metadata if metadata is not None else dict() scheduler_hints = (scheduler_hints if scheduler_hints 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': base.getid(share_network), 'share_type': base.getid(share_type), 'is_public': is_public, 'availability_zone': availability_zone, 'scheduler_hints': scheduler_hints, } if share_group_id: body['share_group_id'] = share_group_id return self._create('/shares', {'share': body}, 'share', return_raw=return_raw) @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, # noqa 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, # noqa 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, # noqa 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" % base.getid(share)) @api_versions.wraps("2.7") # noqa def unmanage(self, share): # noqa """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 = base.getid(snapshot) info = {'snapshot_id': snapshot_id} return self._action('revert', share, info=info) def get(self, share, return_raw=False): """Get a share. :param share: either share object or text with its ID. :rtype: :class:`Share` """ share_id = base.getid(share) return self._get("/shares/%s" % share_id, "share", return_raw=return_raw) 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 = 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, return_raw=False): """Get a list of all shares.""" search_opts = search_opts or {} search_opts.pop("export_location", None) search_opts.pop("is_soft_deleted", None) return self.do_list(detailed=detailed, search_opts=search_opts, sort_key=sort_key, sort_dir=sort_dir, return_raw=return_raw) @api_versions.wraps("2.35", "2.68") # noqa def list(self, detailed=True, search_opts=None, # noqa sort_key=None, sort_dir=None, return_raw=False): """Get a list of all shares.""" if search_opts is None: search_opts = {} search_opts.pop("is_soft_deleted", None) return self.do_list(detailed=detailed, search_opts=search_opts, sort_key=sort_key, sort_dir=sort_dir, return_raw=return_raw) @api_versions.wraps("2.69") # noqa def list(self, detailed=True, search_opts=None, # noqa sort_key=None, sort_dir=None, return_raw=False): """Get a list of all shares.""" return self.do_list(detailed=detailed, search_opts=search_opts, sort_key=sort_key, sort_dir=sort_dir, return_raw=return_raw) def do_list(self, detailed=True, search_opts=None, sort_key=None, sort_dir=None, return_raw=False): """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) - ('is_soft_deleted', bool) 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' elif sort_key == 'availability_zone': search_opts['sort_key'] = 'availability_zone_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', return_raw=return_raw) 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" % 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): # noqa return self._do_force_delete(share, "force_delete") @api_versions.wraps("2.69") def soft_delete(self, share): """Soft delete a share - share will go to recycle bin. :param share: either share object or text with its ID. """ return self._action("soft_delete", share) @api_versions.wraps("2.69") def restore(self, share): """Restore a share - share will restore from recycle bin. :param share: either share object or text with its ID. """ return self._action("restore", share) @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(str(access)) else: ipaddress.IPv4Network(str(access)) except ValueError as error: raise exceptions.CommandError(str(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, lock_visibility=False, lock_deletion=False, lock_reason=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 if lock_visibility: access_params['lock_visibility'] = lock_visibility if lock_deletion: access_params['lock_deletion'] = lock_deletion if lock_reason: access_params['lock_reason'] = lock_reason 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): # noqa 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): # noqa 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): # noqa 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", "2.81") # noqa def allow(self, share, access_type, access, access_level, metadata=None): # noqa 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=metadata) @api_versions.wraps("2.82") # noqa def allow(self, share, access_type, access, access_level, # pylint: disable=function-redefined # noqa F811 metadata=None, lock_visibility=False, lock_deletion=False, lock_reason=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=metadata, lock_visibility=lock_visibility, lock_deletion=lock_deletion, lock_reason=lock_reason) def _do_deny(self, share, access_id, action_name, unrestrict=False): """Deny access to a share. :param share: either share object or text with its ID. :param access_id: ID of share access rule """ body = { "access_id": access_id, } if unrestrict: body['unrestrict'] = True return self._action(action_name, share, body) @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", "2.81") # noqa def deny(self, share, access_id): # noqa return self._do_deny(share, access_id, "deny_access") @api_versions.wraps("2.82") # noqa def deny(self, share, access_id, unrestrict=False): # noqa return self._do_deny(share, access_id, "deny_access", unrestrict=unrestrict) 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): # noqa return self._do_access_list(share, "access_list") 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' % 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): # noqa return self._do_reset_state(share, state, "reset_status") def _do_extend(self, share, new_size, action_name, force=False): """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. :param force: if set to True, the scheduler's capacity decisions are not accounted for. Setting this parameter to True does not mean that the request will always succeed. """ req_body = {"new_size": new_size} if force: req_body['force'] = "true" return self._action(action_name, share, req_body) @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", "2.63") # noqa def extend(self, share, new_size): # noqa return self._do_extend(share, new_size, "extend") @api_versions.wraps("2.64") # noqa def extend(self, share, new_size, force=False): # noqa return self._do_extend(share, new_size, "extend", force=force) 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): # noqa 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' % base.getid(share), 'share_instances', manager=share_instances.ShareInstanceManager(self) ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/manilaclient/v2/shell.py0000664000175000017500000067051000000000000022206 0ustar00zuulzuul00000000000000# Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from operator import xor import os import re import sys import time from oslo_utils import strutils from manilaclient import api_versions from manilaclient.common.apiclient import utils as apiclient_utils from manilaclient.common import cliutils from manilaclient.common import constants from manilaclient import exceptions import manilaclient.v2.shares def _wait_for_resource_status(cs, resource, expected_status, resource_type='share', status_attr='status', poll_timeout=900, poll_interval=2): """Waiter for resource status changes :param cs: command shell control :param expected_status: a string or a list of strings containing expected states to wait for :param resource_type: 'share', 'snapshot', 'share_replica', 'share_group', or 'share_group_snapshot' :param status_attr: 'status', 'task_state', 'access_rules_status' or any other status field that is expected to have the "expected_status" :param poll_timeout: how long to wait for in seconds :param poll_interval: how often to try in seconds """ find_resource = { 'share': _find_share, 'snapshot': _find_share_snapshot, 'share_replica': _find_share_replica, 'share_group': _find_share_group, 'share_group_snapshot': _find_share_group_snapshot, 'share_instance': _find_share_instance, 'share_server': _find_share_server, 'share_access_rule': _find_share_access_rule, } print_resource = { 'share': _print_share, 'snapshot': _print_share_snapshot, 'share_replica': _print_share_replica, 'share_group': _print_share_group, 'share_group_snapshot': _print_share_group_snapshot, 'share_instance': _print_share_instance, 'share_access_rule': _print_share_access_rule, } expected_status = expected_status or ('available', ) if not isinstance(expected_status, (list, tuple, set)): expected_status = (expected_status, ) time_elapsed = 0 timeout_message = ("%(resource_type)s %(resource)s did not reach " "%(expected_states)s within %(seconds)d seconds.") error_message = ("%(resource_type)s %(resource)s has reached a failed " "state.") deleted_message = ("%(resource_type)s %(resource)s has been successfully " "deleted.") unmanaged_message = ("%(resource_type)s %(resource)s has been " "successfully unmanaged.") message_payload = { 'resource_type': resource_type.capitalize(), 'resource': resource.id, } not_found_regex = "no .* exists" while True: if time_elapsed > poll_timeout: print_resource[resource_type](cs, resource) message_payload.update({'expected_states': expected_status, 'seconds': poll_timeout}) raise exceptions.TimeoutException( message=timeout_message % message_payload) try: resource = find_resource[resource_type](cs, resource.id) except exceptions.CommandError as e: if (re.search(not_found_regex, str(e), flags=re.IGNORECASE)): if 'deleted' in expected_status: print(deleted_message % message_payload) break if 'unmanaged' in expected_status: print(unmanaged_message % message_payload) break else: raise e if getattr(resource, status_attr) in expected_status: break elif 'error' in getattr(resource, status_attr): print_resource[resource_type](cs, resource) raise exceptions.ResourceInErrorState( message=error_message % message_payload) time.sleep(poll_interval) time_elapsed += poll_interval return resource def _find_share(cs, share): """Get a share by ID.""" return apiclient_utils.find_resource(cs.shares, share) def _find_share_transfer(cs, transfer): """Get a share transfer by ID.""" return apiclient_utils.find_resource(cs.transfers, transfer) @api_versions.wraps("1.0", "2.8") def _print_share(cs, share): info = share._info.copy() info.pop('links', None) # NOTE(vponomaryov): remove deprecated single field 'export_location' and # leave only list field 'export_locations'. Also, transform the latter to # text with new line separators to make it pretty in CLI. # It will look like following: # +-------------------+--------------------------------------------+ # | Property | Value | # +-------------------+--------------------------------------------+ # | status | available | # | export_locations | 1.2.3.4:/f/o/o | # | | 5.6.7.8:/b/a/r | # | | 9.10.11.12:/q/u/u/z | # | id | d778d2ee-b6bb-4c5f-9f5d-6f3057d549b1 | # | size | 1 | # | share_proto | NFS | # +-------------------+--------------------------------------------+ if info.get('export_locations'): info.pop('export_location', None) info['export_locations'] = "\n".join(info['export_locations']) # No need to print both volume_type and share_type to CLI if 'volume_type' in info and 'share_type' in info: info.pop('volume_type', None) cliutils.print_dict(info) @api_versions.wraps("2.9") # noqa def _print_share(cs, share): # noqa info = share._info.copy() info.pop('links', None) # NOTE(vponomaryov): remove deprecated single field 'export_location' and # leave only list field 'export_locations'. Also, transform the latter to # text with new line separators to make it pretty in CLI. # It will look like following: # +-------------------+--------------------------------------------+ # | Property | Value | # +-------------------+--------------------------------------------+ # | status | available | # | export_locations | | # | | uuid = FOO-UUID | # | | path = 5.6.7.8:/foo/export/location/path | # | | | # | | uuid = BAR-UUID | # | | path = 5.6.7.8:/bar/export/location/path | # | | | # | id | d778d2ee-b6bb-4c5f-9f5d-6f3057d549b1 | # | size | 1 | # | share_proto | NFS | # +-------------------+--------------------------------------------+ if info.get('export_locations'): info['export_locations'] = ( cliutils.convert_dict_list_to_string( info['export_locations'], ignored_keys=['replica_state', 'availability_zone', 'share_replica_id']) ) # No need to print both volume_type and share_type to CLI if 'volume_type' in info and 'share_type' in info: info.pop('volume_type', None) cliutils.print_dict(info) def _wait_for_share_status(cs, share, expected_status='available'): return _wait_for_resource_status( cs, share, expected_status, resource_type='share') def _find_share_instance(cs, instance): """Get a share instance by ID.""" return apiclient_utils.find_resource(cs.share_instances, instance) def _print_type_show(stype, default_share_type=None): if hasattr(stype, 'is_default'): is_default = 'YES' if stype.is_default else 'NO' elif default_share_type: is_default = 'YES' if stype.id == default_share_type.id else 'NO' else: is_default = 'NO' stype_dict = { 'id': stype.id, 'name': stype.name, 'visibility': _is_share_type_public(stype), 'is_default': is_default, 'description': stype.description, 'required_extra_specs': _print_type_required_extra_specs(stype), 'optional_extra_specs': _print_type_optional_extra_specs(stype), } cliutils.print_dict(stype_dict) @api_versions.wraps("1.0", "2.8") def _print_share_instance(cs, instance): info = instance._info.copy() info.pop('links', None) cliutils.print_dict(info) @api_versions.wraps("2.9") # noqa def _print_share_instance(cs, instance): # noqa info = instance._info.copy() info.pop('links', None) if info.get('export_locations'): info['export_locations'] = ( cliutils.convert_dict_list_to_string( info['export_locations'], ignored_keys=['replica_state', 'availability_zone', 'share_replica_id']) ) cliutils.print_dict(info) def _find_share_access_rule(cs, access_rule): """Get share access rule state""" return apiclient_utils.find_resource(cs.share_access_rules, access_rule) def _print_share_access_rule(cs, access_rule): info = access_rule._info.copy() cliutils.print_dict(info) def _find_share_replica(cs, replica): """Get a replica by ID.""" return apiclient_utils.find_resource(cs.share_replicas, replica) @api_versions.wraps("2.11", "2.46") def _print_share_replica(cs, replica): info = replica._info.copy() info.pop('links', None) cliutils.print_dict(info) @api_versions.wraps("2.47") # noqa def _print_share_replica(cs, replica): # noqa info = replica._info.copy() info.pop('links', None) if info.get('export_locations'): info['export_locations'] = ( cliutils.convert_dict_list_to_string( info['export_locations'], ignored_keys=['replica_state', 'availability_zone', 'share_replica_id'])) cliutils.print_dict(info) @api_versions.wraps("2.31") def _find_share_group(cs, share_group): """Get a share group ID.""" return apiclient_utils.find_resource(cs.share_groups, share_group) def _print_share_group(cs, share_group): info = share_group._info.copy() info.pop('links', None) if info.get('share_types'): info['share_types'] = "\n".join(info['share_types']) cliutils.print_dict(info) @api_versions.wraps("2.31") def _find_share_group_snapshot(cs, share_group_snapshot): """Get a share group snapshot by name or ID.""" return apiclient_utils.find_resource( cs.share_group_snapshots, share_group_snapshot) def _print_share_group_snapshot(cs, share_group_snapshot): info = share_group_snapshot._info.copy() info.pop('links', None) info.pop('members', None) cliutils.print_dict(info) def _print_share_group_snapshot_members(cs, share_group_snapshot): info = share_group_snapshot._info.copy() cliutils.print_dict(info.get('members', {})) def _find_share_snapshot(cs, snapshot): """Get a snapshot by ID.""" return apiclient_utils.find_resource(cs.share_snapshots, snapshot) def _print_share_snapshot(cs, snapshot): info = snapshot._info.copy() info.pop('links', None) if info.get('export_locations'): info['export_locations'] = ( cliutils.convert_dict_list_to_string( info['export_locations'])) cliutils.print_dict(info) def _quota_set_pretty_show(quotas): """Convert quotas object to dict and display.""" new_quotas = {} for quota_k, quota_v in sorted(quotas.to_dict().items()): if isinstance(quota_v, dict): quota_v = '\n'.join( ['%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', 'per_share_gigabytes', 'share_groups', 'share_group_snapshots' ] def _quota_class_update(manager, identifier, args): updates = {} for resource in _quota_resources: val = getattr(args, resource, None) if val is not None: updates[resource] = val if updates: manager.update(identifier, **updates) @cliutils.arg( '--tenant-id', '--tenant', '--project', '--project-id', action='single_alias', dest='project_id', metavar='', default=None, help='ID of project to list the quotas for.') @cliutils.arg( '--user-id', metavar='', default=None, help="ID of user to list the quotas for. Optional. " "Mutually exclusive with '--share-type'.") @cliutils.arg( '--share-type', '--share_type', metavar='', type=str, default=None, action='single_alias', help="UUID or name of a share type to set the quotas for. Optional. " "Mutually exclusive with '--user-id'. " "Available only for microversion >= 2.39") @cliutils.arg( '--detail', action='store_true', help='Optional flag to indicate whether to show quota in detail. ' 'Default false, available only for microversion >= 2.25.') @api_versions.wraps("1.0") def do_quota_show(cs, args): """List the quotas for a project, user or share type.""" project_id = args.project_id or cs.keystone_client.project_id kwargs = { "tenant_id": project_id, "user_id": args.user_id, "detail": args.detail, } if args.share_type is not None: if cs.api_version < api_versions.APIVersion("2.39"): raise exceptions.CommandError( "'share type' quotas are available only starting with " "'2.39' API microversion.") kwargs["share_type"] = args.share_type _quota_set_pretty_show(cs.quotas.get(**kwargs)) @cliutils.arg( '--tenant-id', '--tenant', '--project', '--project-id', action='single_alias', dest='project_id', metavar='', default=None, help='ID of the project to list the default quotas for.') def do_quota_defaults(cs, args): """List the default quotas for a project.""" project = args.project_id or cs.keystone_client.project_id _quota_set_pretty_show(cs.quotas.defaults(project)) @cliutils.arg( 'project_id', metavar='', help='UUID of project to set the quotas for.') @cliutils.arg( '--user-id', metavar='', default=None, help="ID of a user to set the quotas for. Optional. " "Mutually exclusive with '--share-type'.") @cliutils.arg( '--shares', metavar='', type=int, default=None, help='New value for the "shares" quota.') @cliutils.arg( '--snapshots', metavar='', type=int, default=None, help='New value for the "snapshots" quota.') @cliutils.arg( '--gigabytes', metavar='', type=int, default=None, help='New value for the "gigabytes" quota.') @cliutils.arg( '--snapshot-gigabytes', '--snapshot_gigabytes', # alias metavar='', type=int, default=None, action='single_alias', help='New value for the "snapshot_gigabytes" quota.') @cliutils.arg( '--share-networks', '--share_networks', metavar='', type=int, default=None, action='single_alias', help='New value for the "share_networks" quota.') @cliutils.arg( '--share-groups', '--share_groups', '--groups', metavar='', type=int, default=None, action='single_alias', help='New value for the "share_groups" quota.') @cliutils.arg( '--share-group-snapshots', '--share_group_snapshots', '--group-snapshots', '--group_snapshots', metavar='', type=int, default=None, action='single_alias', help='New value for the "share_group_snapshots" quota.') @cliutils.arg( '--share-type', '--share_type', metavar='', type=str, default=None, action='single_alias', help="UUID or name of a share type to set the quotas for. Optional. " "Mutually exclusive with '--user-id'. " "Available only for microversion >= 2.39") @cliutils.arg( '--share-replicas', '--share_replicas', '--replicas', metavar='', type=int, default=None, help='New value for the "share_replicas" quota. Available only for ' 'microversion >= 2.53') @cliutils.arg( '--replica-gigabytes', '--replica_gigabytes', metavar='', type=int, default=None, help='New value for the "replica_gigabytes" quota. Available only for ' 'microversion >= 2.53') @cliutils.arg( '--per-share-gigabytes', '--per_share_gigabytes', metavar='', type=int, default=None, help='New value for the "per_share_gigabytes" quota. Available only for ' 'microversion >= 2.62') @cliutils.arg( '--force', dest='force', action="store_true", default=None, help='Whether force update the quota even if the already used ' 'and reserved exceeds the new quota.') @api_versions.wraps("1.0") def do_quota_update(cs, args): """Update the quotas for a project/user and/or share type (Admin only).""" kwargs = { "tenant_id": args.project_id, "user_id": args.user_id, "shares": args.shares, "gigabytes": args.gigabytes, "snapshots": args.snapshots, "snapshot_gigabytes": args.snapshot_gigabytes, "share_networks": args.share_networks, "force": args.force, } if args.share_type is not None: if cs.api_version < api_versions.APIVersion("2.39"): raise exceptions.CommandError( "'share type' quotas are available only starting with " "'2.39' API microversion.") kwargs["share_type"] = args.share_type if args.share_groups is not None or args.share_group_snapshots is not None: if cs.api_version < api_versions.APIVersion("2.40"): raise exceptions.CommandError( "'share group' quotas are available only starting with " "'2.40' API microversion.") elif args.share_type is not None: raise exceptions.CommandError( "Share type quotas cannot be used to constrain share groups.") kwargs["share_groups"] = args.share_groups kwargs["share_group_snapshots"] = args.share_group_snapshots if args.share_replicas is not None or args.replica_gigabytes is not None: if cs.api_version < api_versions.APIVersion("2.53"): raise exceptions.CommandError( "'share replica' quotas are available only starting with " "'2.53' API microversion.") kwargs["share_replicas"] = args.share_replicas kwargs["replica_gigabytes"] = args.replica_gigabytes if args.per_share_gigabytes is not None: if cs.api_version < api_versions.APIVersion("2.62"): raise exceptions.CommandError( "'per share gigabytes' quotas are available only starting " "with '2.62' API microversion.") kwargs["per_share_gigabytes"] = args.per_share_gigabytes cs.quotas.update(**kwargs) @cliutils.arg( '--tenant-id', '--tenant', '--project', '--project-id', action='single_alias', dest='project_id', metavar='', help='ID of the project to delete quota for.') @cliutils.arg( '--user-id', metavar='', help="ID of user to delete quota for. Optional." "Mutually exclusive with '--share-type'.") @cliutils.arg( '--share-type', '--share_type', metavar='', type=str, default=None, action='single_alias', help="UUID or name of a share type to set the quotas for. Optional. " "Mutually exclusive with '--user-id'. " "Available only for microversion >= 2.39") @api_versions.wraps("1.0") def do_quota_delete(cs, args): """Delete quota for a project, or project/user or project/share-type. The quota will revert back to default (Admin only). """ project_id = args.project_id or cs.keystone_client.project_id kwargs = { "tenant_id": project_id, "user_id": args.user_id, } if args.share_type is not None: if cs.api_version < api_versions.APIVersion("2.39"): raise exceptions.CommandError( "'share type' quotas are available only starting with " "'2.39' API microversion.") kwargs["share_type"] = args.share_type cs.quotas.delete(**kwargs) @cliutils.arg( 'class_name', metavar='', help='Name of quota class to list the quotas for.') def do_quota_class_show(cs, args): """List the quotas for a quota class.""" _quota_set_pretty_show(cs.quota_classes.get(args.class_name)) @cliutils.arg( 'class_name', metavar='', help='Name of quota class to set the quotas for.') @cliutils.arg( '--shares', metavar='', type=int, default=None, help='New value for the "shares" quota.') @cliutils.arg( '--snapshots', metavar='', type=int, default=None, help='New value for the "snapshots" quota.') @cliutils.arg( '--gigabytes', metavar='', type=int, default=None, help='New value for the "gigabytes" quota.') @cliutils.arg( '--snapshot-gigabytes', '--snapshot_gigabytes', # alias metavar='', type=int, default=None, action='single_alias', help='New value for the "snapshot_gigabytes" quota.') @cliutils.arg( '--share-networks', '--share_networks', # alias metavar='', type=int, default=None, action='single_alias', help='New value for the "share_networks" quota.') @cliutils.arg( '--share-groups', '--share_groups', # alias metavar='', type=int, default=None, action='single_alias', help='New value for the "share_groups" quota. Available only for ' 'microversion >= 2.40') @cliutils.arg( '--share-group-snapshots', '--share_group_snapshots', metavar='', type=int, default=None, action='single_alias', help='New value for the "share_group_snapshots" quota. Available only for ' 'microversion >= 2.40') @cliutils.arg( '--share-replicas', '--share_replicas', # alias '--replicas', # alias metavar='', type=int, default=None, action='single_alias', help='New value for the "share_replicas" quota. Available only for ' 'microversion >= 2.53') @cliutils.arg( '--replica-gigabytes', '--replica_gigabytes', # alias metavar='', type=int, default=None, action='single_alias', help='New value for the "replica_gigabytes" quota. Available only for ' 'microversion >= 2.53') @cliutils.arg( '--per-share-gigabytes', '--per_share_gigabytes', # alias metavar='', type=int, default=None, action='single_alias', help='New value for the "per_share_gigabytes" quota. Available only for ' 'microversion >= 2.62') def do_quota_class_update(cs, args): """Update the quotas for a quota class (Admin only).""" if args.share_groups is not None or args.share_group_snapshots is not None: if cs.api_version < api_versions.APIVersion("2.40"): raise exceptions.CommandError( "'share groups' quotas are available only starting with " "'2.40' API microversion.") if args.share_replicas is not None or args.replica_gigabytes is not None: if cs.api_version < api_versions.APIVersion("2.53"): raise exceptions.CommandError( "'share replica' quotas are available only starting with " "'2.53' API microversion.") if args.per_share_gigabytes is not None: if cs.api_version < api_versions.APIVersion("2.62"): raise exceptions.CommandError( "'per_share_gigabytes' quota is available only starting " "with '2.62' API microversion.") _quota_class_update(cs.quota_classes, args.class_name, args) def do_absolute_limits(cs, args): """Print a list of absolute limits for a user.""" limits = cs.limits.get().absolute columns = ['Name', 'Value'] cliutils.print_list(limits, columns) @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "verb,uri,value".') def do_rate_limits(cs, args): """Print a list of rate limits for a user.""" limits = cs.limits.get().rate columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available'] if args.columns is not None: columns = _split_columns(columns=args.columns) cliutils.print_list(limits, columns) @cliutils.arg( 'share_protocol', metavar='', type=str, help='Share protocol (NFS, CIFS, CephFS, GlusterFS, HDFS or MAPRFS).') @cliutils.arg( 'size', metavar='', type=int, help='Share size in GiB.') @cliutils.arg( '--snapshot-id', '--snapshot_id', '--snapshot', metavar='', action='single_alias', help='Optional snapshot ID or name to create the share from.' ' (Default=None)', default=None) @cliutils.arg( '--name', metavar='', help='Optional share name. (Default=None)', default=None) @cliutils.arg( '--metadata', type=str, nargs='*', metavar='', help='Metadata key=value pairs (Optional, Default=None).', default=None) @cliutils.arg( '--share-network', '--share_network', metavar='', action='single_alias', help='Optional network info ID or name.', default=None) @cliutils.arg( '--description', metavar='', help='Optional share description. (Default=None)', default=None) @cliutils.arg( '--share-type', '--share_type', '--volume-type', '--volume_type', metavar='', default=None, action='single_alias', help='Optional share type. Use of optional volume type is deprecated. ' '(Default=None)') @cliutils.arg( '--public', dest='public', action='store_true', default=False, help="Level of visibility for share. Defines whether other projects are " "able to see it or not. (Default=False)") @cliutils.arg( '--availability-zone', '--availability_zone', '--az', metavar='', default=None, action='single_alias', help='Availability zone in which share should be created.') @cliutils.arg( '--share-group', '--share_group', '--group', metavar='', action='single_alias', help='Optional share group name or ID in which to create the share ' '(Default=None).', default=None) @cliutils.arg( '--wait', action='store_true', help='Wait for share creation') @cliutils.arg( '--scheduler-hints', '--scheduler_hints', '--sh', metavar='', nargs='*', help='Scheduler hints for the share as key=value pairs, ' 'possible keys are same_host, different_host, ' 'value must be share_name or share_id.', default=None) @cliutils.service_type('sharev2') def do_create(cs, args): """Creates a new share (NFS, CIFS, CephFS, GlusterFS, HDFS or MAPRFS).""" share_metadata = None if args.metadata is not None: share_metadata = _extract_metadata(args) share_group = None if args.share_group: share_group = _find_share_group(cs, args.share_group).id share_network = None if args.share_network: share_network = _find_share_network(cs, args.share_network) snapshot = None if args.snapshot_id: snapshot = _find_share_snapshot(cs, args.snapshot_id).id if args.name: if args.name.capitalize() == 'None': raise exceptions.CommandError( "Share name cannot be with the value 'None'") if not args.share_type: try: _find_share_type(cs, "default") except exceptions.CommandError: msg = ("There is no default share type available. You must pick " "a valid share type to create a share.") raise exceptions.CommandError(msg) scheduler_hints = {} if args.scheduler_hints: scheduler_hints = _extract_key_value_options(args, 'scheduler_hints') same_host_hint_shares = scheduler_hints.get('same_host') different_host_hint_shares = scheduler_hints.get('different_host') if same_host_hint_shares: same_host_hint_shares = [ _find_share(cs, sh).id for sh in same_host_hint_shares.split(',') ] scheduler_hints['same_host'] = ','.join(same_host_hint_shares) if different_host_hint_shares: different_host_hint_shares = [ _find_share(cs, sh).id for sh in different_host_hint_shares.split(',') ] scheduler_hints['different_host'] = ','.join( different_host_hint_shares) share = cs.shares.create(args.share_protocol, args.size, snapshot, args.name, args.description, metadata=share_metadata, share_network=share_network, share_type=args.share_type, is_public=args.public, availability_zone=args.availability_zone, share_group_id=share_group, scheduler_hints=scheduler_hints) if args.wait: share = _wait_for_share_status(cs, share) _print_share(cs, share) @api_versions.wraps("2.29") @cliutils.arg( 'share', metavar='', help='Name or ID of share to migrate.') @cliutils.arg( 'host', metavar='', help="Destination host where share will be migrated to. Use the " "format 'host@backend#pool'.") @cliutils.arg( '--force_host_assisted_migration', '--force-host-assisted-migration', metavar='', choices=['True', 'False'], action='single_alias', required=False, default=False, help="Enforces the use of the host-assisted migration approach, " "which bypasses driver optimizations. Default=False.") @cliutils.arg( '--preserve-metadata', '--preserve_metadata', action='single_alias', metavar='', choices=['True', 'False'], required=True, help="Enforces migration to preserve all file metadata when moving its " "contents. If set to True, host-assisted migration will not be " "attempted.") @cliutils.arg( '--preserve-snapshots', '--preserve_snapshots', action='single_alias', metavar='', choices=['True', 'False'], required=True, help="Enforces migration of the share snapshots to the destination. If " "set to True, host-assisted migration will not be attempted.") @cliutils.arg( '--writable', metavar='', choices=['True', 'False'], required=True, help="Enforces migration to keep the share writable while contents are " "being moved. If set to True, host-assisted migration will not be " "attempted.") @cliutils.arg( '--nondisruptive', metavar='', choices=['True', 'False'], required=True, help="Enforces migration to be nondisruptive. If set to True, " "host-assisted migration will not be attempted.") @cliutils.arg( '--new_share_network', '--new-share-network', metavar='', action='single_alias', required=False, help='Specify the new share network for the share. Do not specify this ' 'parameter if the migrating share has to be retained within its ' 'current share network.', default=None) @cliutils.arg( '--new_share_type', '--new-share-type', metavar='', required=False, action='single_alias', help='Specify the new share type for the share. Do not specify this ' 'parameter if the migrating share has to be retained with its ' 'current share type.', default=None) def do_migration_start(cs, args): """Migrates share to a new host (Admin only, Experimental).""" share = _find_share(cs, args.share) new_share_net_id = None if args.new_share_network: share_net = _find_share_network(cs, args.new_share_network) new_share_net_id = share_net.id if share_net else None new_share_type_id = None if args.new_share_type: share_type = _find_share_type(cs, args.new_share_type) new_share_type_id = share_type.id if share_type else None share.migration_start(args.host, args.force_host_assisted_migration, args.preserve_metadata, args.writable, args.nondisruptive, args.preserve_snapshots, new_share_net_id, new_share_type_id) @cliutils.arg( 'share', metavar='', help='Name or ID of share to complete migration.') @api_versions.wraps("2.22") def do_migration_complete(cs, args): """Completes migration for a given share (Admin only, Experimental).""" share = _find_share(cs, args.share) share.migration_complete() @cliutils.arg( 'share', metavar='', help='Name or ID of share to cancel migration.') @api_versions.wraps("2.22") def do_migration_cancel(cs, args): """Cancels migration of a given share when copying (Admin only, Experimental). """ share = _find_share(cs, args.share) share.migration_cancel() @cliutils.arg( 'share', metavar='', help='Name or ID of the share to modify.') @cliutils.arg( '--task-state', '--task_state', '--state', metavar='', default='None', action='single_alias', required=False, help=('Indicate which task state to assign the share. Options include ' 'migration_starting, migration_in_progress, migration_completing, ' 'migration_success, migration_error, migration_cancelled, ' 'migration_driver_in_progress, migration_driver_phase1_done, ' 'data_copying_starting, data_copying_in_progress, ' 'data_copying_completing, data_copying_completed, ' 'data_copying_cancelled, data_copying_error. If no value is ' 'provided, None will be used.')) @api_versions.wraps("2.22") def do_reset_task_state(cs, args): """Explicitly update the task state of a share (Admin only, Experimental). """ state = args.task_state if args.task_state == 'None': state = None share = _find_share(cs, args.share) share.reset_task_state(state) @cliutils.arg( 'share', metavar='', help='Name or ID of the share to get share migration progress ' 'information.') @api_versions.wraps("2.22") def do_migration_get_progress(cs, args): """Gets migration progress of a given share when copying (Admin only, Experimental). """ share = _find_share(cs, args.share) result = share.migration_get_progress() # NOTE(ganso): result[0] is response code, result[1] is dict body cliutils.print_dict(result[1]) @cliutils.arg( 'share_server_id', metavar='', help='ID of the share server to check if the migration is possible.') @cliutils.arg( 'host', metavar='', help="Destination to migrate the share server to. Use the format " "'@'.") @cliutils.arg( '--preserve-snapshots', '--preserve_snapshots', action='single_alias', metavar='', choices=['True', 'False'], required=True, help="Set to True if snapshots must be preserved at the migration " "destination.") @cliutils.arg( '--writable', metavar='', choices=['True', 'False'], required=True, help="Set to True if shares associated with the share server must be " "writable through the first phase of the migration.") @cliutils.arg( '--nondisruptive', metavar='', choices=['True', 'False'], required=True, help="Set to True if migration must be non disruptive to clients that are " "using the shares associated with the share server through both " "phases of the migration.") @cliutils.arg( '--new_share_network', '--new-share-network', metavar='', action='single_alias', required=False, help="New share network to migrate to. Optional, default=None.", default=None) @api_versions.wraps("2.57") @api_versions.experimental_api def do_share_server_migration_check(cs, args): """Check migration compatibility for a share server with desired properties (Admin only, Experimental). """ share_server = _find_share_server(cs, args.share_server_id) new_share_net_id = None if args.new_share_network: share_net = _find_share_network(cs, args.new_share_network) new_share_net_id = share_net.id result = share_server.migration_check( args.host, args.writable, args.nondisruptive, args.preserve_snapshots, new_share_net_id) cliutils.print_dict(result) @cliutils.arg( 'share_server_id', metavar='', help='ID of the share server to migrate.') @cliutils.arg( 'host', metavar='', help="Destination to migrate the share server to. Use the format " "'@'.") @cliutils.arg( '--preserve-snapshots', '--preserve_snapshots', action='single_alias', metavar='', choices=['True', 'False'], required=True, help="Set to True if snapshots must be preserved at the migration " "destination.") @cliutils.arg( '--writable', metavar='', choices=['True', 'False'], required=True, help="Enforces migration to keep all its shares writable while contents " "are being moved.") @cliutils.arg( '--nondisruptive', metavar='', choices=['True', 'False'], required=True, help="Enforces migration to be nondisruptive.") @cliutils.arg( '--new_share_network', '--new-share-network', metavar='', action='single_alias', required=False, help='Specify a new share network for the share server. Do not ' 'specify this parameter if the migrating share server has ' 'to be retained within its current share network.', default=None) @api_versions.wraps("2.57") @api_versions.experimental_api def do_share_server_migration_start(cs, args): """Migrates share server to a new host (Admin only, Experimental).""" share_server = _find_share_server(cs, args.share_server_id) new_share_net_id = None if args.new_share_network: share_net = _find_share_network(cs, args.new_share_network) new_share_net_id = share_net.id share_server.migration_start(args.host, args.writable, args.nondisruptive, args.preserve_snapshots, new_share_net_id) @cliutils.arg( 'share_server_id', metavar='', help='ID of share server to complete migration.') @api_versions.wraps("2.57") @api_versions.experimental_api def do_share_server_migration_complete(cs, args): """Completes migration for a given share server (Admin only, Experimental). """ share_server = _find_share_server(cs, args.share_server_id) result = share_server.migration_complete() cliutils.print_dict(result) @cliutils.arg( 'share_server_id', metavar='', help='ID of share server to complete migration.') @api_versions.wraps("2.57") @api_versions.experimental_api def do_share_server_migration_cancel(cs, args): """Cancels migration of a given share server when copying (Admin only, Experimental). """ share_server = _find_share_server(cs, args.share_server_id) share_server.migration_cancel() @cliutils.arg( 'share_server_id', metavar='', help='ID of share server to complete migration.') @cliutils.arg( '--task-state', '--task_state', '--state', metavar='', default='None', action='single_alias', required=False, help=('Indicate which task state to assign the share server. Options: ' 'migration_starting, migration_in_progress, migration_completing, ' 'migration_success, migration_error, migration_cancel_in_progress, ' 'migration_cancelled, migration_driver_in_progress, ' 'migration_driver_phase1_done. If no value is provided, None will ' 'be used.')) @api_versions.wraps("2.57") @api_versions.experimental_api def do_share_server_reset_task_state(cs, args): """Explicitly update the task state of a share (Admin only, Experimental). """ state = args.task_state if args.task_state == 'None': state = None share_server = _find_share_server(cs, args.share_server_id) share_server.reset_task_state(state) @cliutils.arg( 'share_server_id', metavar='', help='ID of share server to complete migration.') @api_versions.wraps("2.57") @api_versions.experimental_api def do_share_server_migration_get_progress(cs, args): """Gets migration progress of a given share server when copying (Admin only, Experimental). """ share_server = _find_share_server(cs, args.share_server_id) result = share_server.migration_get_progress() cliutils.print_dict(result) @cliutils.arg( 'share', metavar='', help='Name or ID of the share to update metadata on.') @cliutils.arg( 'action', metavar='', choices=['set', 'unset'], help="Actions: 'set' or 'unset'.") @cliutils.arg( 'metadata', metavar='', nargs='+', default=[], help='Metadata to set or unset (only key is necessary to unset).') def do_metadata(cs, args): """Set or delete metadata on a share.""" share = _find_share(cs, args.share) metadata = _extract_metadata(args) if args.action == 'set': share.set_metadata(metadata) elif args.action == 'unset': share.delete_metadata(sorted(list(metadata), reverse=True)) @cliutils.arg( 'share', metavar='', help='Name or ID of the share.') def do_metadata_show(cs, args): """Show metadata of given share.""" share = _find_share(cs, args.share) metadata = share.get_metadata()._info cliutils.print_dict(metadata, 'Property') @cliutils.arg( 'share', metavar='', help='Name or ID of the share to update metadata on.') @cliutils.arg( 'metadata', metavar='', nargs='+', default=[], help='Metadata entry or entries to update.') def do_metadata_update_all(cs, args): """Update all metadata of a share.""" share = _find_share(cs, args.share) metadata = _extract_metadata(args) metadata = share.update_all_metadata(metadata)._info['metadata'] cliutils.print_dict(metadata, 'Property') @api_versions.wraps("2.9") @cliutils.arg( 'share', metavar='', help='Name or ID of the share.') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,host,status".') def do_share_export_location_list(cs, args): """List export locations of a given share.""" if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) else: list_of_keys = [ 'ID', 'Path', 'Preferred', ] share = _find_share(cs, args.share) export_locations = cs.share_export_locations.list(share) cliutils.print_list(export_locations, list_of_keys) @api_versions.wraps("2.9") @cliutils.arg( 'share', metavar='', help='Name or ID of the share.') @cliutils.arg( 'export_location', metavar='', help='ID of the share export location.') def do_share_export_location_show(cs, args): """Show export location of the share.""" share = _find_share(cs, args.share) export_location = cs.share_export_locations.get( share, args.export_location) view_data = export_location._info.copy() cliutils.print_dict(view_data) @cliutils.arg( 'service_host', metavar='', type=str, help='manage-share service host: some.host@driver#pool.') @cliutils.arg( 'protocol', metavar='', type=str, help='Protocol of the share to manage, such as NFS or CIFS.') @cliutils.arg( 'export_path', metavar='', type=str, help='Share export path, NFS share such as: 10.0.0.1:/example_path, ' 'CIFS share such as: \\\\10.0.0.1\\example_cifs_share.') @cliutils.arg( '--name', metavar='', help='Optional share name. (Default=None)', default=None) @cliutils.arg( '--description', metavar='', help='Optional share description. (Default=None)', default=None) @cliutils.arg( '--share_type', '--share-type', metavar='', default=None, action='single_alias', help='Optional share type assigned to share. (Default=None)') @cliutils.arg( '--driver_options', '--driver-options', type=str, nargs='*', metavar='', action='single_alias', help='Driver option key=value pairs (Optional, Default=None).', default=None) @cliutils.arg( '--public', dest='public', action='store_true', default=False, help="Level of visibility for share. Defines whether other projects are " "able to see it or not. Available only for microversion >= 2.8. " "(Default=False)") @cliutils.arg( '--share_server_id', '--share-server-id', metavar='', default=None, action='single_alias', help="Share server associated with share when using a share type with " "'driver_handles_share_servers' extra_spec set to True. Available " "only for microversion >= 2.49. (Default=None)") @cliutils.arg( '--wait', action='store_true', help='Wait for share management') def do_manage(cs, args): """Manage share not handled by Manila (Admin only).""" driver_options = _extract_key_value_options(args, 'driver_options') if cs.api_version.matches(api_versions.APIVersion("2.49"), api_versions.APIVersion()): share = cs.shares.manage( args.service_host, args.protocol, args.export_path, driver_options=driver_options, share_type=args.share_type, name=args.name, description=args.description, is_public=args.public, share_server_id=args.share_server_id) else: if args.share_server_id: raise exceptions.CommandError("Invalid parameter " "--share_server_id specified. This" " parameter is only supported on" " microversion 2.49 or newer.") share = cs.shares.manage( args.service_host, args.protocol, args.export_path, driver_options=driver_options, share_type=args.share_type, name=args.name, description=args.description, is_public=args.public) if args.wait: share = _wait_for_resource_status( cs, share, resource_type='share', expected_status='available' ) _print_share(cs, share) @api_versions.wraps("2.49") @cliutils.arg( 'host', metavar='', type=str, help='Backend name as "@".') @cliutils.arg( 'share_network', metavar='', help="Share network where share server has network allocations in.") @cliutils.arg( 'identifier', metavar='', type=str, help='A driver-specific share server identifier required by the driver to ' 'manage the share server.') @cliutils.arg( '--driver_options', '--driver-options', type=str, nargs='*', metavar='', action='single_alias', help='One or more driver-specific key=value pairs that may be necessary to' ' manage the share server (Optional, Default=None).', default=None) @cliutils.arg( '--share-network-subnet', '--share_network_subnet', type=str, metavar='', help="Share network subnet where share server has network allocations in. " "The default subnet will be used if it's not specified. Available " "for microversion >= 2.51 (Optional, Default=None).", default=None) @cliutils.arg( '--wait', action='store_true', default='False', help='Wait for share server to manage') def do_share_server_manage(cs, args): """Manage share server not handled by Manila (Admin only).""" driver_options = _extract_key_value_options(args, 'driver_options') manage_kwargs = { 'driver_options': driver_options, } if cs.api_version < api_versions.APIVersion("2.51"): if getattr(args, 'share_network_subnet'): raise exceptions.CommandError( "Share network subnet option is only available with manila " "API version >= 2.51") else: manage_kwargs['share_network_subnet_id'] = args.share_network_subnet share_server = cs.share_servers.manage( args.host, args.share_network, args.identifier, **manage_kwargs) if args.wait: try: _wait_for_resource_status( cs, share_server, resource_type='share_server', expected_status='active') except exceptions.CommandError as e: print(e, file=sys.stderr) cliutils.print_dict(share_server._info) @cliutils.arg( 'share_server_id', metavar='', help='ID of the share server to modify.') @cliutils.arg( '--state', metavar='', default=constants.STATUS_ACTIVE, help=('Indicate which state to assign the share server. Options include ' 'active, error, creating, deleting, managing, unmanaging, ' 'manage_error and unmanage_error. If no state is provided, active ' 'will be used.')) @api_versions.wraps("2.49") def do_share_server_reset_state(cs, args): """Explicitly update the state of a share server (Admin only).""" cs.share_servers.reset_state(args.share_server_id, args.state) @api_versions.wraps("2.12") @cliutils.arg( 'share', metavar='', type=str, help='Name or ID of the share.') @cliutils.arg( 'provider_location', metavar='', type=str, help='Provider location of the snapshot on the backend.') @cliutils.arg( '--name', metavar='', help='Optional snapshot name (Default=None).', default=None) @cliutils.arg( '--description', metavar='', help='Optional snapshot description (Default=None).', default=None) @cliutils.arg( '--driver_options', '--driver-options', type=str, nargs='*', metavar='', action='single_alias', help='Optional driver options as key=value pairs (Default=None).', default=None) 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).') @cliutils.arg( '--wait', action='store_true', help='Wait for share unmanagement') def do_unmanage(cs, args): """Unmanage share (Admin only).""" share_ref = _find_share(cs, args.share) share_ref.unmanage() if args.wait: _wait_for_share_status(cs, share_ref, expected_status='unmanaged') @api_versions.wraps("2.49") @cliutils.arg( 'share_server', metavar='', nargs='+', help='ID of the share server(s).') @cliutils.arg( '--force', dest='force', action="store_true", required=False, default=False, help="Enforces the unmanage share server operation, even if the back-end " "driver does not support it.") 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.arg( '--wait', action='store_true', help='Wait for share deletion') @cliutils.service_type('sharev2') def do_delete(cs, args): """Remove one or more shares.""" failure_count = 0 shares_to_delete = [] for share in args.share: try: share_ref = _find_share(cs, share) shares_to_delete.append(share_ref) if args.share_group: share_group_id = _find_share_group(cs, args.share_group).id cs.shares.delete(share_ref, share_group_id=share_group_id) else: cs.shares.delete(share_ref) except Exception as e: failure_count += 1 print("Delete for share %s failed: %s" % (share, e), file=sys.stderr) if failure_count == len(args.share): raise exceptions.CommandError("Unable to delete any of the specified " "shares.") if args.wait: for share in shares_to_delete: try: _wait_for_share_status(cs, share, expected_status='deleted') except exceptions.CommandError as e: print(e, file=sys.stderr) @cliutils.arg( 'share', metavar='', nargs='+', help='Name or ID of the share(s) to force delete.') @cliutils.arg( '--wait', action='store_true', help='Wait for share to delete') @cliutils.service_type('sharev2') def do_force_delete(cs, args): """Attempt force-delete of share, regardless of state (Admin only).""" failure_count = 0 shares_to_delete = [] for share in args.share: try: share_ref = _find_share(cs, share) shares_to_delete.append(share_ref) share_ref.force_delete() except Exception as e: failure_count += 1 print("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.") if args.wait: for share in shares_to_delete: try: _wait_for_share_status(cs, share, expected_status='deleted') except exceptions.CommandError as e: print(e, file=sys.stderr) @cliutils.arg( 'share', metavar='', nargs='+', help='Name or ID of the share(s).') @cliutils.service_type('sharev2') @api_versions.wraps("2.69") def do_soft_delete(cs, args): """Soft delete one or more shares.""" failure_count = 0 for share in args.share: try: share_ref = _find_share(cs, share) cs.shares.soft_delete(share_ref) except Exception as e: failure_count += 1 print("Soft deletion of share %s failed: %s" % (share, e), file=sys.stderr) if failure_count == len(args.share): raise exceptions.CommandError("Unable to soft delete any of the " "specified shares.") @cliutils.arg( 'share', metavar='', nargs='+', help='Name or ID of the share(s).') @cliutils.service_type('sharev2') @api_versions.wraps("2.69") def do_restore(cs, args): """Restore one or more shares from recycle bin.""" failure_count = 0 for share in args.share: try: share_ref = _find_share(cs, share) cs.shares.restore(share_ref) except Exception as e: failure_count += 1 print("Restoration of share %s failed: %s" % (share, e), file=sys.stderr) if failure_count == len(args.share): raise exceptions.CommandError("Unable to restore any of the " "specified shares.") @api_versions.wraps("1.0", "2.8") @cliutils.arg( 'share', metavar='', help='Name or ID of the NAS share.') def do_show(cs, args): """Show details about a NAS share.""" share = _find_share(cs, args.share) _print_share(cs, share) @api_versions.wraps("2.9") # noqa @cliutils.arg( 'share', metavar='', help='Name or ID of the NAS share.') def do_show(cs, args): # noqa """Show details about a NAS share.""" share = _find_share(cs, args.share) export_locations = cs.share_export_locations.list(share) share._info['export_locations'] = export_locations _print_share(cs, share) @cliutils.arg( 'share', metavar='', help='Name or ID of the NAS share to modify.') @cliutils.arg( 'access_type', metavar='', help='Access rule type (only "ip", "user"(user or group), "cert" or ' '"cephx" are supported).') @cliutils.arg( 'access_to', metavar='', help='Value that defines access.') @cliutils.arg( '--access-level', '--access_level', # alias metavar='', type=str, default=None, choices=['rw', 'ro'], action='single_alias', help='Share access level ("rw" and "ro" access levels are supported). ' 'Defaults to rw.') @cliutils.arg( '--metadata', type=str, nargs='*', metavar='', help='Space Separated list of key=value pairs of metadata items. ' 'OPTIONAL: Default=None. Available only for microversion >= 2.45.', default=None) @cliutils.arg( '--wait', action='store_true', help='Wait for share access to become active') def do_access_allow(cs, args): """Allow access to a given share.""" access_metadata = None if cs.api_version.matches(api_versions.APIVersion("2.45"), api_versions.APIVersion()): access_metadata = _extract_metadata(args) elif getattr(args, 'metadata'): raise exceptions.CommandError( "Adding metadata to access rules is supported only beyond " "API version 2.45") share = _find_share(cs, args.share) access = share.allow(args.access_type, args.access_to, args.access_level, access_metadata) if args.wait: try: if not cs.api_version.matches(api_versions.APIVersion("2.45"), api_versions.APIVersion()): raise exceptions.CommandError( "Waiting on the allowing access operation is only " "available for API versions equal to or greater than 2.45." ) access_id = access.get('id') share_access_rule = cs.share_access_rules.get(access_id) access = _wait_for_resource_status( cs, share_access_rule, resource_type='share_access_rule', expected_status='active', status_attr='state')._info except exceptions.CommandError as e: print(e, file=sys.stderr) cliutils.print_dict(access) @api_versions.wraps("2.45") @cliutils.arg( 'access_id', metavar='', help='ID of the NAS share access rule.') def do_access_show(cs, args): """Show details about a NAS share access rule.""" access = cs.share_access_rules.get(args.access_id) view_data = access._info.copy() cliutils.print_dict(view_data) @api_versions.wraps("2.45") @cliutils.arg( 'access_id', metavar='', help='ID of the NAS share access rule.') @cliutils.arg( 'action', metavar='', choices=['set', 'unset'], help="Actions: 'set' or 'unset'.") @cliutils.arg( 'metadata', metavar='', nargs='+', default=[], help='Space separated key=value pairs of metadata items to set. ' 'To unset only keys are required. ') def do_access_metadata(cs, args): """Set or delete metadata on a share access rule.""" share_access = cs.share_access_rules.get(args.access_id) metadata = _extract_metadata(args) if args.action == 'set': cs.share_access_rules.set_metadata(share_access, metadata) elif args.action == 'unset': cs.share_access_rules.unset_metadata( share_access, sorted(list(metadata), reverse=True)) @api_versions.wraps("2.32") @cliutils.arg( 'snapshot', metavar='', help='Name or ID of the share snapshot to allow access to.') @cliutils.arg( 'access_type', metavar='', help='Access rule type (only "ip", "user"(user or group), "cert" or ' '"cephx" are supported).') @cliutils.arg( 'access_to', metavar='', help='Value that defines access.') def do_snapshot_access_allow(cs, args): """Allow read only access to a snapshot.""" share_snapshot = _find_share_snapshot(cs, args.snapshot) access = share_snapshot.allow(args.access_type, args.access_to) cliutils.print_dict(access) @cliutils.arg( 'share', metavar='', help='Name or ID of the NAS share to modify.') @cliutils.arg( 'id', metavar='', help='ID of the access rule to be deleted.') def do_access_deny(cs, args): """Deny access to a share.""" share = _find_share(cs, args.share) share.deny(args.id) @api_versions.wraps("2.32") @cliutils.arg( 'snapshot', metavar='', help='Name or ID of the share snapshot to deny access to.') @cliutils.arg( 'id', metavar='', nargs='+', help='ID(s) of the access rule(s) to be deleted.') def do_snapshot_access_deny(cs, args): """Deny access to a snapshot.""" failure_count = 0 snapshot = _find_share_snapshot(cs, args.snapshot) for access_id in args.id: try: snapshot.deny(access_id) except Exception as e: failure_count += 1 print("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): # noqa """Show access list for share.""" list_of_keys = [ 'id', 'access_type', 'access_to', 'access_level', 'state', 'access_key' ] if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) share = _find_share(cs, args.share) access_list = share.access_list() cliutils.print_list(access_list, list_of_keys) @api_versions.wraps("2.33") # noqa @cliutils.arg( 'share', metavar='', help='Name or ID of the share.') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "access_type,access_to".') @cliutils.arg( '--metadata', type=str, nargs='*', metavar='', help='Filters results by a metadata key and value. OPTIONAL: ' 'Default=None. Available only for microversion >= 2.45', default=None) def do_access_list(cs, args): # noqa """Show access list for share.""" list_of_keys = [ 'id', 'access_type', 'access_to', 'access_level', 'state', 'access_key', 'created_at', 'updated_at', ] share = _find_share(cs, args.share) if cs.api_version < api_versions.APIVersion("2.45"): if getattr(args, 'metadata'): raise exceptions.CommandError( "Filtering access rules by metadata is supported only beyond " "API version 2.45") access_list = share.access_list() else: access_list = cs.share_access_rules.access_list( share, {'metadata': _extract_metadata(args)}) if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) cliutils.print_list(access_list, list_of_keys) @api_versions.wraps("2.32") @cliutils.arg( 'snapshot', metavar='', help='Name or ID of the share snapshot to list access of.') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "access_type,access_to".') def do_snapshot_access_list(cs, args): """Show access list for a snapshot.""" if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) else: list_of_keys = ['id', 'access_type', 'access_to', 'state'] snapshot = _find_share_snapshot(cs, args.snapshot) access_list = snapshot.access_list() cliutils.print_list(access_list, list_of_keys) @cliutils.arg( '--all-tenants', '--all-projects', action='single_alias', dest='all_projects', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all projects (Admin only).') @cliutils.arg( '--name', metavar='', type=str, default=None, help='Filter results by name.') @cliutils.arg( '--description', metavar='', type=str, default=None, help='Filter results by description. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--name~', metavar='', type=str, default=None, help='Filter results matching a share name pattern. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--description~', metavar='', type=str, default=None, help='Filter results matching a share description pattern. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--status', metavar='', type=str, default=None, help='Filter results by status.') @cliutils.arg( '--share-server-id', '--share-server_id', '--share_server-id', '--share_server_id', # aliases metavar='', type=str, default=None, action='single_alias', help='Filter results by share server ID (Admin only).') @cliutils.arg( '--metadata', type=str, nargs='*', metavar='', help='Filters results by a metadata key and value. OPTIONAL: ' 'Default=None.', default=None) @cliutils.arg( '--extra-specs', '--extra_specs', # alias type=str, nargs='*', metavar='', action='single_alias', help='Filters results by a extra specs key and value of share type that ' 'was used for share creation. OPTIONAL: Default=None.', default=None) @cliutils.arg( '--share-type', '--volume-type', '--share_type', '--share-type-id', '--volume-type-id', # aliases '--share-type_id', '--share_type-id', '--share_type_id', # aliases '--volume_type', '--volume_type_id', metavar='', type=str, default=None, action='single_alias', help='Filter results by a share type id or name that was used for share ' 'creation.') @cliutils.arg( '--limit', metavar='', type=int, default=None, help='Maximum number of shares to return. OPTIONAL: Default=None.') @cliutils.arg( '--offset', metavar='', type=int, default=None, help='Set offset to define start point of share listing. ' 'OPTIONAL: Default=None.') @cliutils.arg( '--sort-key', '--sort_key', # alias metavar='', type=str, default=None, action='single_alias', help='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-projects'.") @cliutils.arg( '--public', dest='public', action='store_true', default=False, help="Add public shares from all projects to result. (Default=False)") @cliutils.arg( '--share-group', '--share_group', '--group', metavar='', type=str, default=None, action='single_alias', help='Filter results by share group name or ID (Default=None).') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "export_location,is public".') @cliutils.arg( '--export-location', '--export_location', metavar='', type=str, default=None, action='single_alias', help='ID or path of the share export location. ' 'Available only for microversion >= 2.35.') @cliutils.arg( '--count', dest='count', metavar='', choices=['True', 'False'], default=False, help='Display total number of shares to return. ' 'Available only for microversion >= 2.42.') @cliutils.arg( '--soft-deleted', '--soft_deleted', action='store_true', help='Get shares in recycle bin. If this parameter is set to ' 'True(Default=False), will only show shares in recycle bin. ' 'Available only for microversion >= 2.69.') @cliutils.service_type('sharev2') def do_list(cs, args): """List NAS shares with filters.""" columns = args.columns all_projects = int( os.environ.get("ALL_TENANTS", os.environ.get("ALL_PROJECTS", args.all_projects)) ) if columns is not None: list_of_keys = _split_columns(columns=columns) else: list_of_keys = [ 'ID', 'Name', 'Size', 'Share Proto', 'Status', 'Is Public', 'Share Type Name', 'Host', 'Availability Zone' ] if all_projects or args.public: list_of_keys.append('Project ID') empty_obj = type('Empty', (object,), {'id': None}) share_type = (_find_share_type(cs, args.share_type) if args.share_type else empty_obj) snapshot = (_find_share_snapshot(cs, args.snapshot) if args.snapshot else empty_obj) share_network = (_find_share_network(cs, args.share_network) if args.share_network else empty_obj) share_group = None if args.share_group: share_group = _find_share_group(cs, args.share_group) search_opts = { 'offset': args.offset, 'limit': args.limit, 'all_tenants': all_projects, 'name': args.name, 'status': args.status, 'host': args.host, 'share_network_id': share_network.id, 'snapshot_id': snapshot.id, 'share_type_id': share_type.id, 'metadata': _extract_metadata(args), 'extra_specs': _extract_extra_specs(args), 'share_server_id': args.share_server_id, 'project_id': args.project_id, 'is_public': args.public, } if cs.api_version.matches(api_versions.APIVersion("2.36"), api_versions.APIVersion()): search_opts['name~'] = getattr(args, 'name~') search_opts['description~'] = getattr(args, 'description~') search_opts['description'] = getattr(args, 'description') elif (getattr(args, 'name~') or getattr(args, 'description~') or getattr(args, 'description')): raise exceptions.CommandError( "Pattern based filtering (name~, description~ and description)" " is only available with manila API version >= 2.36") if cs.api_version.matches(api_versions.APIVersion("2.35"), api_versions.APIVersion()): search_opts['export_location'] = args.export_location elif args.export_location: raise exceptions.CommandError( "Filtering by export location is only " "available with manila API version >= 2.35") if (args.count and cs.api_version.matches( api_versions.APIVersion(), api_versions.APIVersion("2.41"))): raise exceptions.CommandError( "Display total number of shares is only " "available with manila API version >= 2.42") if cs.api_version.matches(api_versions.APIVersion("2.69"), api_versions.APIVersion()): if args.soft_deleted: search_opts['is_soft_deleted'] = args.soft_deleted elif args.soft_deleted: raise exceptions.CommandError( "Filtering by is_soft_deleted is only " "available with manila API version >= 2.69") if share_group: search_opts['share_group_id'] = share_group.id total_count = 0 if strutils.bool_from_string(args.count, strict=True): search_opts['with_count'] = args.count shares, total_count = cs.shares.list( search_opts=search_opts, sort_key=args.sort_key, sort_dir=args.sort_dir ) else: shares = cs.shares.list( search_opts=search_opts, sort_key=args.sort_key, sort_dir=args.sort_dir ) # 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) with cs.shares.completion_cache('uuid', manilaclient.v2.shares.Share, mode="w"): for share in shares: cs.shares.write_to_completion_cache('uuid', share.id) with cs.shares.completion_cache('name', manilaclient.v2.shares.Share, mode="w"): for share in shares: if share.name is not None: cs.shares.write_to_completion_cache('name', share.name) @cliutils.arg( '--share-id', '--share_id', # alias metavar='', default=None, action='single_alias', help='Filter results by share ID.') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,host,status".') @cliutils.arg( '--export-location', '--export_location', metavar='', type=str, default=None, action='single_alias', help='ID or path of the share instance export location. ' 'Available only for microversion >= 2.35.') @api_versions.wraps("2.3") def do_share_instance_list(cs, args): """List share instances (Admin only).""" share = _find_share(cs, args.share_id) if args.share_id else None list_of_keys = [ 'ID', 'Share ID', 'Host', 'Status', 'Availability Zone', 'Share Network ID', 'Share Server ID', 'Share Type ID', ] if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) if share: instances = cs.shares.list_instances(share) else: if cs.api_version.matches( api_versions.APIVersion("2.35"), api_versions.APIVersion()): instances = cs.share_instances.list(args.export_location) else: if args.export_location: raise exceptions.CommandError( "Filtering by export location is only " "available with manila API version >= 2.35") instances = cs.share_instances.list() cliutils.print_list(instances, list_of_keys) @api_versions.wraps("2.3", "2.8") @cliutils.arg( 'instance', metavar='', help='Name or ID of the share instance.') def do_share_instance_show(cs, args): """Show details about a share instance.""" instance = _find_share_instance(cs, args.instance) _print_share_instance(cs, instance) @api_versions.wraps("2.9") # noqa @cliutils.arg( 'instance', metavar='', help='Name or ID of the share instance.') def do_share_instance_show(cs, args): # noqa """Show details about a share instance (Admin only).""" instance = _find_share_instance(cs, args.instance) export_locations = cs.share_instance_export_locations.list(instance) instance._info['export_locations'] = export_locations _print_share_instance(cs, instance) @cliutils.arg( 'instance', metavar='', nargs='+', help='Name or ID of the instance(s) to force delete.') @api_versions.wraps("2.3") @cliutils.arg( '--wait', action='store_true', help='Wait for share instance deletion') @cliutils.service_type('sharev2') def do_share_instance_force_delete(cs, args): """Force-delete the share instance, regardless of state (Admin only).""" failure_count = 0 instances_to_delete = [] for instance in args.instance: try: instance_ref = _find_share_instance(cs, instance) instances_to_delete.append(instance_ref) instance_ref.force_delete() except Exception as e: failure_count += 1 print("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.") if args.wait: for instance in instances_to_delete: try: _wait_for_resource_status( cs, instance, resource_type='share_instance', expected_status='deleted') except exceptions.CommandError as e: print(e, file=sys.stderr) @cliutils.arg( 'instance', metavar='', help='Name or ID of the share instance to modify.') @cliutils.arg( '--state', metavar='', default='available', help=('Indicate which state to assign the instance. Options include ' 'available, error, creating, deleting, error_deleting, migrating,' 'migrating_to. If no state is provided, available will be used.')) @api_versions.wraps("2.3") def do_share_instance_reset_state(cs, args): """Explicitly update the state of a share instance (Admin only).""" instance = _find_share_instance(cs, args.instance) instance.reset_state(args.state) @api_versions.wraps("2.9") @cliutils.arg( 'instance', metavar='', help='Name or ID of the share instance.') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,host,status".') def do_share_instance_export_location_list(cs, args): """List export locations of a given share instance.""" if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) else: list_of_keys = [ 'ID', 'Path', 'Is Admin only', 'Preferred', ] instance = _find_share_instance(cs, args.instance) export_locations = cs.share_instance_export_locations.list(instance) cliutils.print_list(export_locations, list_of_keys) @api_versions.wraps("2.9") @cliutils.arg( 'instance', metavar='', help='Name or ID of the share instance.') @cliutils.arg( 'export_location', metavar='', help='ID of the share instance export location.') def do_share_instance_export_location_show(cs, args): """Show export location for the share instance.""" instance = _find_share_instance(cs, args.instance) export_location = cs.share_instance_export_locations.get( instance, args.export_location) view_data = export_location._info.copy() cliutils.print_dict(view_data) @cliutils.arg( '--all-tenants', '--all-projects', action='single_alias', dest='all_projects', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all projects (Admin only).') @cliutils.arg( '--name', metavar='', type=str, default=None, help='Filter results by name.') @cliutils.arg( '--description', metavar='', type=str, default=None, help='Filter results by description. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--status', metavar='', default=None, help='Filter results by status.') @cliutils.arg( '--share-id', '--share_id', # alias metavar='', default=None, action='single_alias', help='Filter results by source share ID.') @cliutils.arg( '--usage', dest='usage', metavar='any|used|unused', nargs='?', type=str, const='any', default=None, choices=['any', 'used', 'unused', ], help='Either filter or not snapshots by its usage. OPTIONAL: Default=any.') @cliutils.arg( '--limit', metavar='', type=int, default=None, help='Maximum number of share snapshots to return. ' 'OPTIONAL: Default=None.') @cliutils.arg( '--offset', metavar='', type=int, default=None, help='Set offset to define start point of share snapshots listing. ' 'OPTIONAL: Default=None.') @cliutils.arg( '--sort-key', '--sort_key', # alias metavar='', type=str, default=None, action='single_alias', help='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=str, default=None, help='Filter results matching a share snapshot name pattern. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--description~', metavar='', type=str, default=None, help='Filter results matching a share snapshot description pattern. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--metadata', metavar='', type=str, default=None, nargs='*', help='Filters results by a metadata key and value. OPTIONAL: ' 'Default=None, Available only for microversion >= 2.73. ') @cliutils.arg( '--count', dest='count', metavar='', choices=['True', 'False'], default=False, help='Display total number of share snapshots to return. ' 'Available only for microversion >= 2.79.') def do_snapshot_list(cs, args): """List all the snapshots.""" all_projects = int( os.environ.get("ALL_TENANTS", os.environ.get("ALL_PROJECTS", args.all_projects)) ) if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) else: list_of_keys = [ 'ID', 'Share ID', 'Status', 'Name', 'Share Size', ] if all_projects: list_of_keys.append('Project ID') empty_obj = type('Empty', (object,), {'id': None}) share = _find_share(cs, args.share_id) if args.share_id else empty_obj search_opts = { 'offset': args.offset, 'limit': args.limit, 'all_tenants': all_projects, 'name': args.name, 'status': args.status, 'share_id': share.id, 'usage': args.usage, 'metadata': _extract_metadata(args), } if cs.api_version.matches(api_versions.APIVersion("2.36"), api_versions.APIVersion()): search_opts['name~'] = getattr(args, 'name~') search_opts['description~'] = getattr(args, 'description~') search_opts['description'] = getattr(args, 'description') elif (getattr(args, 'name~') or getattr(args, 'description~') or getattr(args, 'description')): raise exceptions.CommandError( "Pattern based filtering (name~, description~ and description)" " is only available with manila API version >= 2.36") if (args.count and cs.api_version.matches( api_versions.APIVersion(), api_versions.APIVersion("2.78"))): raise exceptions.CommandError( "Display total number of share snapshots is only " "available with manila API version >= 2.79") total_count = 0 if strutils.bool_from_string(args.count, strict=True): search_opts['with_count'] = args.count snapshots, total_count = cs.share_snapshots.list( search_opts=search_opts, sort_key=args.sort_key, sort_dir=args.sort_dir) else: snapshots = cs.share_snapshots.list( search_opts=search_opts, sort_key=args.sort_key, sort_dir=args.sort_dir) cliutils.print_list(snapshots, list_of_keys, sortby_index=None) if args.count: print("Share snapshots in total: %s" % total_count) @cliutils.arg( 'snapshot', metavar='', help='Name or ID of the snapshot.') def do_snapshot_show(cs, args): """Show details about a snapshot.""" snapshot = _find_share_snapshot(cs, args.snapshot) export_locations = cs.share_snapshot_export_locations.list( snapshot=snapshot) snapshot._info['export_locations'] = export_locations _print_share_snapshot(cs, snapshot) @api_versions.wraps("2.32") @cliutils.arg( 'snapshot', metavar='', help='Name or ID of the snapshot.') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,path".') def do_snapshot_export_location_list(cs, args): """List export locations of a given snapshot.""" if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) else: list_of_keys = [ 'ID', 'Path', ] snapshot = _find_share_snapshot(cs, args.snapshot) export_locations = cs.share_snapshot_export_locations.list( snapshot) cliutils.print_list(export_locations, list_of_keys) @api_versions.wraps("2.32") @cliutils.arg( 'instance', metavar='', help='Name or ID of the snapshot instance.') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,path,is_admin_only".') def do_snapshot_instance_export_location_list(cs, args): """List export locations of a given snapshot instance.""" if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) else: list_of_keys = [ 'ID', 'Path', 'Is Admin only', ] instance = _find_share_snapshot_instance(cs, args.instance) export_locations = cs.share_snapshot_instance_export_locations.list( instance) cliutils.print_list(export_locations, list_of_keys) @api_versions.wraps("2.32") @cliutils.arg( 'snapshot', metavar='', help='Name or ID of the snapshot.') @cliutils.arg( 'export_location', metavar='', help='ID of the share snapshot export location.') def do_snapshot_export_location_show(cs, args): """Show export location of the share snapshot.""" snapshot = _find_share_snapshot(cs, args.snapshot) export_location = cs.share_snapshot_export_locations.get( args.export_location, snapshot) view_data = export_location._info.copy() cliutils.print_dict(view_data) @api_versions.wraps("2.32") @cliutils.arg( 'snapshot_instance', metavar='', help='ID of the share snapshot instance.') @cliutils.arg( 'export_location', metavar='', help='ID of the share snapshot instance export location.') def do_snapshot_instance_export_location_show(cs, args): """Show export location of the share instance snapshot.""" snapshot_instance = _find_share_snapshot_instance(cs, args.snapshot_instance) export_location = cs.share_snapshot_instance_export_locations.get( args.export_location, snapshot_instance) view_data = export_location._info.copy() cliutils.print_dict(view_data) @cliutils.arg( 'share', metavar='', help='Name or ID of the share to snapshot.') @cliutils.arg( '--force', metavar='', help='Optional flag to indicate whether ' 'to snapshot a share even if it\'s busy. ' '(Default=False)', default=False) @cliutils.arg( '--name', metavar='', default=None, help='Optional snapshot name. (Default=None)') @cliutils.arg( '--description', metavar='', default=None, help='Optional snapshot description. (Default=None)') 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 projects.') def do_update(cs, args): """Rename a share.""" kwargs = {} if args.name is not None: kwargs['display_name'] = args.name if args.description is not None: kwargs['display_description'] = args.description if args.is_public is not None: kwargs['is_public'] = strutils.bool_from_string(args.is_public, strict=True) if not kwargs: msg = "Must supply name, description or is_public value." raise exceptions.CommandError(msg) _find_share(cs, args.share).update(**kwargs) @cliutils.arg( 'snapshot', metavar='', help='Name or ID of the snapshot to rename.') @cliutils.arg( 'name', nargs='?', metavar='', help='New name for the snapshot.') @cliutils.arg( '--description', metavar='', help='Optional snapshot description. (Default=None)', default=None) def do_snapshot_rename(cs, args): """Rename a snapshot.""" kwargs = {} if args.name is not None: kwargs['display_name'] = args.name if args.description is not None: kwargs['display_description'] = args.description if not kwargs: msg = "Must supply either name or description." raise exceptions.CommandError(msg) _find_share_snapshot(cs, args.snapshot).update(**kwargs) @cliutils.arg( 'snapshot', metavar='', nargs='+', help='Name or ID of the snapshot(s) to delete.') 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.') @cliutils.arg( '--wait', action='store_true', help='Wait for snapshot to delete') @cliutils.service_type('sharev2') def do_snapshot_force_delete(cs, args): """Attempt force-deletion of one or more snapshots. Regardless of the state (Admin only). """ failure_count = 0 snapshots_to_delete = [] for snapshot in args.snapshot: try: snapshot_ref = _find_share_snapshot( cs, snapshot) snapshots_to_delete.append(snapshot_ref) snapshot_ref.force_delete() except Exception as e: failure_count += 1 print("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.") if args.wait: for snapshot in snapshots_to_delete: try: _wait_for_resource_status( cs, snapshot, resource_type='snapshot', expected_status='deleted') except exceptions.CommandError as e: print(e, file=sys.stderr) @cliutils.arg( 'snapshot', metavar='', help='Name or ID of the snapshot to modify.') @cliutils.arg( '--state', metavar='', default='available', help=('Indicate which state to assign the snapshot. ' 'Options include available, error, creating, deleting, ' 'error_deleting. If no state is provided, ' 'available will be used.')) def do_snapshot_reset_state(cs, args): """Explicitly update the state of a snapshot (Admin only).""" snapshot = _find_share_snapshot(cs, args.snapshot) snapshot.reset_state(args.state) @api_versions.wraps("2.19") @cliutils.arg( '--snapshot', metavar='', default=None, help='Filter results by share snapshot ID.') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id".') @cliutils.arg( '--detailed', metavar='', default=False, help='Show detailed information about snapshot instances.' ' (Default=False)') def do_snapshot_instance_list(cs, args): """List share snapshot instances.""" snapshot = (_find_share_snapshot(cs, args.snapshot) if args.snapshot else None) if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) elif args.detailed: list_of_keys = ['ID', 'Snapshot ID', 'Status', 'Created_at', 'Updated_at', 'Share_id', 'Share_instance_id', 'Progress', 'Provider_location'] else: list_of_keys = ['ID', 'Snapshot ID', 'Status'] instances = cs.share_snapshot_instances.list( detailed=args.detailed, snapshot=snapshot) cliutils.print_list(instances, list_of_keys) @api_versions.wraps("2.19") @cliutils.arg( 'snapshot_instance', metavar='', help='ID of the share snapshot instance.') def do_snapshot_instance_show(cs, args): """Show details about a share snapshot instance.""" snapshot_instance = _find_share_snapshot_instance( cs, args.snapshot_instance) export_locations = ( cs.share_snapshot_instance_export_locations.list(snapshot_instance)) snapshot_instance._info['export_locations'] = export_locations _print_share_snapshot(cs, snapshot_instance) @cliutils.arg( 'snapshot_instance', metavar='', help='ID of the snapshot instance to modify.') @cliutils.arg( '--state', metavar='', default='available', help=('Indicate which state to assign the snapshot instance. ' 'Options include available, error, creating, deleting, ' 'error_deleting. If no state is provided, available ' 'will be used.')) @api_versions.wraps("2.19") def do_snapshot_instance_reset_state(cs, args): """Explicitly update the state of a share snapshot instance.""" snapshot_instance = _find_share_snapshot_instance( cs, args.snapshot_instance) snapshot_instance.reset_state(args.state) @cliutils.arg( 'share', metavar='', help='Name or ID of the share to modify.') @cliutils.arg( '--state', metavar='', default='available', help=('Indicate which state to assign the share. Options include ' 'available, error, creating, deleting, error_deleting. If no ' 'state is provided, available will be used.')) def do_reset_state(cs, args): """Explicitly update the state of a share (Admin only).""" share = _find_share(cs, args.share) share.reset_state(args.state) @api_versions.wraps("1.0", "2.25") @cliutils.arg( '--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 a share network to export shares to.""" 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): # noqa """Create a share network to export shares to.""" values = { 'neutron_net_id': args.neutron_net_id, 'neutron_subnet_id': args.neutron_subnet_id, 'name': args.name, 'description': args.description, } if cs.api_version >= api_versions.APIVersion("2.51"): values['availability_zone'] = args.availability_zone elif args.availability_zone: raise exceptions.CommandError( "Creating share networks with a given az is only " "available with manila API version >= 2.51") share_network = cs.share_networks.create(**values) info = share_network._info.copy() cliutils.print_dict(info) @api_versions.wraps("1.0", "2.25") @cliutils.arg( 'share_network', metavar='', help='Name or ID of share network to update.') @cliutils.arg( '--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): # noqa """Update share network data.""" values = { 'neutron_net_id': args.neutron_net_id, 'neutron_subnet_id': args.neutron_subnet_id, 'name': args.name, 'description': args.description, } share_network = _find_share_network( cs, args.share_network).update(**values) info = share_network._info.copy() cliutils.print_dict(info) @cliutils.arg( 'share_network', metavar='', help='Name or ID of the share network to show.') def do_share_network_show(cs, args): """Retrieve details for a share network.""" share_network = _find_share_network(cs, args.share_network) info = share_network._info.copy() cliutils.print_dict(info) @api_versions.wraps("1.0", "2.25") @cliutils.arg( '--all-tenants', '--all-projects', action='single_alias', dest='all_projects', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all projects (Admin only).') @cliutils.arg( '--project-id', '--project_id', # alias metavar='', action='single_alias', default=None, help='Filter results by project ID.') @cliutils.arg( '--name', metavar='', default=None, help='Filter results by name.') @cliutils.arg( '--created-since', '--created_since', # alias metavar='', action='single_alias', default=None, help='''Return only share networks created since given date. ''' '''The date is in the format 'yyyy-mm-dd'.''') @cliutils.arg( '--created-before', '--created_before', # alias metavar='', action='single_alias', default=None, help='''Return only share networks created until given date. ''' '''The date is in the format 'yyyy-mm-dd'.''') @cliutils.arg( '--security-service', '--security_service', # alias metavar='', action='single_alias', default=None, help='Filter results by attached security service.') @cliutils.arg( '--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_projects = int( os.environ.get("ALL_TENANTS", os.environ.get("ALL_PROJECTS", args.all_projects)) ) search_opts = { 'all_tenants': all_projects, 'project_id': args.project_id, 'name': args.name, 'created_since': args.created_since, 'created_before': args.created_before, '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', '--all-projects', action='single_alias', dest='all_projects', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all projects (Admin only).') @cliutils.arg( '--project-id', '--project_id', # alias metavar='', action='single_alias', default=None, help='Filter results by project ID.') @cliutils.arg( '--name', metavar='', type=str, default=None, help='Filter results by name.') @cliutils.arg( '--description', metavar='', type=str, default=None, help='Filter results by description. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--created-since', '--created_since', # alias metavar='', action='single_alias', default=None, help='''Return only share networks created since given date. ''' '''The date is in the format 'yyyy-mm-dd'.''') @cliutils.arg( '--created-before', '--created_before', # alias metavar='', action='single_alias', default=None, help='''Return only share networks created until given date. ''' '''The date is in the format 'yyyy-mm-dd'.''') @cliutils.arg( '--security-service', '--security_service', # alias metavar='', action='single_alias', default=None, help='Filter results by attached security service.') @cliutils.arg( '--neutron-net-id', '--neutron_net_id', '--neutron_net-id', '--neutron-net_id', # aliases metavar='', action='single_alias', default=None, help='Filter results by neutron net ID.') @cliutils.arg( '--neutron-subnet-id', '--neutron_subnet_id', '--neutron-subnet_id', # aliases '--neutron_subnet-id', # alias metavar='', action='single_alias', default=None, help='Filter results by neutron subnet ID.') @cliutils.arg( '--network-type', '--network_type', # alias metavar='', action='single_alias', default=None, help='Filter results by network type.') @cliutils.arg( '--segmentation-id', '--segmentation_id', # alias metavar='', type=int, action='single_alias', default=None, help='Filter results by segmentation ID.') @cliutils.arg( '--cidr', metavar='', default=None, help='Filter results by CIDR.') @cliutils.arg( '--ip-version', '--ip_version', # alias metavar='', type=int, action='single_alias', default=None, help='Filter results by IP version.') @cliutils.arg( '--offset', metavar='', type=int, default=None, help='Start position of share networks listing.') @cliutils.arg( '--limit', metavar='', type=int, default=None, help='Number of share networks to return per request.') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id".') @cliutils.arg( '--name~', metavar='', type=str, default=None, help='Filter results matching a share network name pattern. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--description~', metavar='', type=str, default=None, help='Filter results matching a share network description pattern. ' 'Available only for microversion >= 2.36.') def do_share_network_list(cs, args): # noqa """Get a list of share networks""" all_projects = int( os.environ.get("ALL_TENANTS", os.environ.get("ALL_PROJECTS", args.all_projects)) ) search_opts = { 'all_tenants': all_projects, 'project_id': args.project_id, 'name': args.name, 'created_since': args.created_since, 'created_before': args.created_before, 'neutron_net_id': args.neutron_net_id, 'neutron_subnet_id': args.neutron_subnet_id, 'network_type': args.network_type, 'segmentation_id': args.segmentation_id, 'cidr': args.cidr, 'ip_version': args.ip_version, 'offset': args.offset, 'limit': args.limit, } if cs.api_version.matches(api_versions.APIVersion("2.36"), api_versions.APIVersion()): search_opts['name~'] = getattr(args, 'name~') search_opts['description~'] = getattr(args, 'description~') search_opts['description'] = getattr(args, 'description') elif (getattr(args, 'name~') or getattr(args, 'description~') or getattr(args, 'description')): raise exceptions.CommandError( "Pattern based filtering (name~, description~ and description)" " is only available with manila API version >= 2.36") if args.security_service: search_opts['security_service_id'] = _find_security_service( cs, args.security_service).id share_networks = cs.share_networks.list(search_opts=search_opts) fields = ['id', 'name'] if args.columns is not None: fields = _split_columns(columns=args.columns) cliutils.print_list(share_networks, fields=fields) @cliutils.arg( 'share_network', metavar='', help='Share network name or ID.') @cliutils.arg( 'security_service', metavar='', help='Security service name or ID to associate with.') def do_share_network_security_service_add(cs, args): """Associate security service with share network.""" share_network = _find_share_network(cs, args.share_network) security_service = _find_security_service(cs, args.security_service) cs.share_networks.add_security_service(share_network, security_service) @cliutils.arg( 'share_network', metavar='', help='Share network name or ID.') @cliutils.arg( 'security_service', metavar='', help='Security service name or ID to associate with.') @cliutils.arg( '--reset', metavar='', choices=['True', 'False'], required=False, default=False, help='Reset and restart the check operation.' '(Optional, Default=False)') @api_versions.wraps("2.63") def do_share_network_security_service_add_check(cs, args): """Associate security service with share network.""" share_network = _find_share_network(cs, args.share_network) security_service = _find_security_service(cs, args.security_service) add_sec_service_result = cs.share_networks.add_security_service_check( share_network, security_service, reset_operation=args.reset) # result[0] is response code, result[1] is dict body cliutils.print_dict(add_sec_service_result[1]) @cliutils.arg( 'share_network', metavar='', help='Share network name or ID.') @cliutils.arg( 'security_service', metavar='', help='Security service name or ID to dissociate.') def do_share_network_security_service_remove(cs, args): """Dissociate security service from share network.""" share_network = _find_share_network(cs, args.share_network) security_service = _find_security_service(cs, args.security_service) cs.share_networks.remove_security_service(share_network, security_service) @cliutils.arg( 'share_network', metavar='', help='Share network name or ID.') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,name".') def do_share_network_security_service_list(cs, args): """Get list of security services associated with a given share network.""" share_network = _find_share_network(cs, args.share_network) search_opts = { 'share_network_id': share_network.id, } security_services = cs.security_services.list(search_opts=search_opts) fields = ['id', 'name', 'status', 'type', ] if args.columns is not None: fields = _split_columns(columns=args.columns) cliutils.print_list(security_services, fields=fields) @cliutils.arg( 'share_network', metavar='', help='Share network name or ID.') @cliutils.arg( 'current_security_service', metavar='', help='Current security service name or ID.') @cliutils.arg( 'new_security_service', metavar='', help='New security service name or ID.') @api_versions.wraps("2.63") def do_share_network_security_service_update(cs, args): """Update a current security service to a new security service.""" share_network = _find_share_network(cs, args.share_network) current_security_service = _find_security_service( cs, args.current_security_service) new_security_service = _find_security_service( cs, args.new_security_service) cs.share_networks.update_share_network_security_service( share_network, current_security_service, new_security_service) @cliutils.arg( 'share_network', metavar='', help='Share network name or ID.') @cliutils.arg( 'current_security_service', metavar='', help='Current security service name or ID.') @cliutils.arg( 'new_security_service', metavar='', help='New security service name or ID.') @cliutils.arg( '--reset', metavar='', choices=['True', 'False'], required=False, default=False, help='Reset and start again the check operation.' '(Optional, Default=False)') @api_versions.wraps("2.63") def do_share_network_security_service_update_check(cs, args): """Check if a security service update on the share network is supported. This call can be repeated until a successful result is obtained. """ share_network = _find_share_network(cs, args.share_network) current_security_service = _find_security_service( cs, args.current_security_service) new_security_service = _find_security_service( cs, args.new_security_service) share_network_update_check = ( cs.share_networks.update_share_network_security_service_check( share_network, current_security_service, new_security_service, reset_operation=args.reset)) # result[0] is response code, result[1] is dict body cliutils.print_dict(share_network_update_check[1]) @cliutils.arg( 'share_network', metavar='', help='Share network name or ID.') @cliutils.arg( '--state', metavar='', default=constants.STATUS_ACTIVE, help=('Indicate which state to assign the share network. Options include ' 'active, error, network change. If no state is provided, active ' 'will be used.')) @api_versions.wraps("2.63") def do_share_network_reset_state(cs, args): """Explicitly update the state of a share network (Admin only).""" share_network = _find_share_network(cs, args.share_network) cs.share_networks.reset_state(share_network, args.state) @cliutils.arg( 'share_network', metavar='', help='Share network name or ID.') @cliutils.arg( '--neutron-net-id', '--neutron-net_id', '--neutron_net_id', '--neutron_net-id', metavar='', default=None, action='single_alias', help="Neutron network ID. Used to set up network for share servers. " "Optional, Default = None.") @cliutils.arg( '--neutron-subnet-id', '--neutron-subnet_id', '--neutron_subnet_id', '--neutron_subnet-id', metavar='', default=None, action='single_alias', help="Neutron subnet ID. Used to set up network for share servers. " "This subnet should belong to specified neutron network. " "Optional, Default = None.") @cliutils.arg( '--availability-zone', '--availability_zone', '--az', default=None, action='single_alias', metavar='', help='Optional availability zone that the subnet is available within ' '(Default=None). If None, the subnet will be considered as being ' 'available across all availability zones.') def do_share_network_subnet_create(cs, args): """Add a new subnet into a share network.""" if xor(bool(args.neutron_net_id), bool(args.neutron_subnet_id)): raise exceptions.CommandError( "Both neutron_net_id and neutron_subnet_id should be specified. " "Alternatively, neither of them should be specified.") share_network = _find_share_network(cs, args.share_network) values = { 'share_network_id': share_network.id, 'neutron_net_id': args.neutron_net_id, 'neutron_subnet_id': args.neutron_subnet_id, 'availability_zone': args.availability_zone, } share_network_subnet = cs.share_network_subnets.create(**values) info = share_network_subnet._info.copy() cliutils.print_dict(info) @cliutils.arg( 'share_network', metavar='', help='Share network name or ID.') @cliutils.arg( '--neutron-net-id', '--neutron-net_id', '--neutron_net_id', '--neutron_net-id', metavar='', default=None, action='single_alias', help="Neutron network ID. Used to set up network for share servers. " "Optional, Default = None.") @cliutils.arg( '--neutron-subnet-id', '--neutron-subnet_id', '--neutron_subnet_id', '--neutron_subnet-id', metavar='', default=None, action='single_alias', help="Neutron subnet ID. Used to set up network for share servers. " "This subnet should belong to specified neutron network. " "Optional, Default = None.") @cliutils.arg( '--availability-zone', '--availability_zone', '--az', default=None, action='single_alias', metavar='', help='Optional availability zone that the subnet is available within ' '(Default=None). If None, the subnet will be considered as being ' 'available across all availability zones.') @cliutils.arg( '--reset', metavar='', choices=['True', 'False'], required=False, default=False, help='Reset and start again the check operation.' '(Optional, Default=False)') @api_versions.wraps("2.70") def do_share_network_subnet_create_check(cs, args): """Check if a new subnet can be added to a share network.""" if xor(bool(args.neutron_net_id), bool(args.neutron_subnet_id)): raise exceptions.CommandError( "Both neutron_net_id and neutron_subnet_id should be specified. " "Alternatively, neither of them should be specified.") share_network = _find_share_network(cs, args.share_network) values = { 'neutron_net_id': args.neutron_net_id, 'neutron_subnet_id': args.neutron_subnet_id, 'availability_zone': args.availability_zone, 'reset_operation': args.reset, } subnet_create_check = ( cs.share_networks.share_network_subnet_create_check( share_network.id, **values)) # result[0] is response code, result[1] is dict body cliutils.print_dict(subnet_create_check[1]) @cliutils.arg( 'share_network', metavar='', help='Share network name or ID.') @cliutils.arg( 'share_network_subnet', metavar='', nargs='+', help='Name or ID of share network subnet(s) to be deleted.') def do_share_network_subnet_delete(cs, args): """Delete one or more share network subnets.""" failure_count = 0 share_network_ref = _find_share_network(cs, args.share_network) for subnet in args.share_network_subnet: try: cs.share_network_subnets.delete(share_network_ref, subnet) except Exception as e: failure_count += 1 print("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 project's network.") @cliutils.arg( '--ou', metavar='', default=None, help="Security service OU (Organizational Unit). Available only for " "microversion >= 2.44.") @cliutils.arg( '--server', metavar='', default=None, help="Security service IP address or hostname.") @cliutils.arg( '--domain', metavar='', default=None, help="Security service domain.") @cliutils.arg( '--user', metavar='', default=None, help="Security service user or group used by project.") @cliutils.arg( '--password', metavar='', default=None, help="Password used by user.") @cliutils.arg( '--name', metavar='', default=None, help="Security service name.") @cliutils.arg( '--default-ad-site', metavar='', dest='default_ad_site', default=None, help="Default AD site. Available only for microversion >= 2.76. Can " "be provided in the place of '--server' but not along with it.") @cliutils.arg( '--description', metavar='', default=None, help="Security service description.") def do_security_service_create(cs, args): """Create security service used by project.""" values = { 'dns_ip': args.dns_ip, 'server': args.server, 'domain': args.domain, 'user': args.user, 'password': args.password, 'name': args.name, 'description': args.description, } if cs.api_version.matches(api_versions.APIVersion("2.44"), api_versions.APIVersion()): values['ou'] = args.ou elif args.ou: raise exceptions.CommandError( "Security service Organizational Unit (ou) option " "is only available with manila API version >= 2.44") if cs.api_version.matches(api_versions.APIVersion("2.76"), api_versions.APIVersion()): values['default_ad_site'] = args.default_ad_site elif args.default_ad_site: raise exceptions.CommandError( "Default AD site option is only available with " "manila API version >= 2.76") if args.type == 'active_directory': if args.server and args.default_ad_site: raise exceptions.CommandError( "Cannot create security service because both " "server and 'default_ad_site' were provided. " "Specify either server or 'default_ad_site'.") security_service = cs.security_services.create(args.type, **values) info = security_service._info.copy() cliutils.print_dict(info) @cliutils.arg( 'security_service', metavar='', help='Security service name or ID to update.') @cliutils.arg( '--dns-ip', metavar='', default=None, help="DNS IP address used inside project's network.") @cliutils.arg( '--ou', metavar='', default=None, help="Security service OU (Organizational Unit). Available only for " "microversion >= 2.44.") @cliutils.arg( '--server', metavar='', default=None, help="Security service IP address or hostname.") @cliutils.arg( '--domain', metavar='', default=None, help="Security service domain.") @cliutils.arg( '--user', metavar='', default=None, help="Security service user or group used by project.") @cliutils.arg( '--password', metavar='', default=None, help="Password used by user.") @cliutils.arg( '--name', metavar='', default=None, help="Security service name.") @cliutils.arg( '--default-ad-site', metavar='', dest='default_ad_site', default=None, help="Default AD site. Available only for microversion >= 2.76.") @cliutils.arg( '--description', metavar='', default=None, help="Security service description.") def do_security_service_update(cs, args): """Update security service.""" values = { 'dns_ip': args.dns_ip, 'server': args.server, 'domain': args.domain, 'user': args.user, 'password': args.password, 'name': args.name, 'description': args.description, } if cs.api_version.matches(api_versions.APIVersion("2.44"), api_versions.APIVersion()): values['ou'] = args.ou elif args.ou: raise exceptions.CommandError( "Security service Organizational Unit (ou) option " "is only available with manila API version >= 2.44") if cs.api_version.matches(api_versions.APIVersion("2.76"), api_versions.APIVersion()): values['default_ad_site'] = args.default_ad_site elif args.default_ad_site: raise exceptions.CommandError( "Default AD site option is only available with " "manila API version >= 2.76") security_service = _find_security_service( cs, args.security_service).update(**values) cliutils.print_dict(security_service._info) @cliutils.arg( 'security_service', metavar='', help='Security service name or ID to show.') def do_security_service_show(cs, args): """Show security service.""" security_service = _find_security_service(cs, args.security_service) info = security_service._info.copy() cliutils.print_dict(info) @cliutils.arg( '--all-tenants', '--all-projects', action='single_alias', dest='all_projects', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all projects (Admin only).') @cliutils.arg( '--share-network', '--share_network', # alias metavar='', action='single_alias', default=None, help='Filter results by share network id or name.') @cliutils.arg( '--status', metavar='', default=None, help='Filter results by status.') @cliutils.arg( '--name', metavar='', default=None, help='Filter results by name.') @cliutils.arg( '--type', metavar='', default=None, help='Filter results by type.') @cliutils.arg( '--user', metavar='', default=None, help='Filter results by user or group used by projects.') @cliutils.arg( '--dns-ip', '--dns_ip', # alias metavar='', action='single_alias', default=None, help="Filter results by DNS IP address used inside project's network.") @cliutils.arg( '--ou', metavar='', default=None, help="Filter results by security service OU (Organizational Unit)." " Available only for microversion >= 2.44.") @cliutils.arg( '--server', metavar='', default=None, help="Filter results by security service IP address or hostname.") @cliutils.arg( '--domain', metavar='', default=None, help="Filter results by domain.") @cliutils.arg( '--detailed', dest='detailed', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help="Show detailed information about filtered security services.") @cliutils.arg( '--offset', metavar="", default=None, help='Start position of security services listing.') @cliutils.arg( '--limit', metavar="", default=None, help='Number of security services to return per request.') @cliutils.arg( '--default-ad-site', metavar='', dest='default_ad_site', default=None, help="Default AD site. Available only for microversion >= 2.76.") @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "name,type".') def do_security_service_list(cs, args): """Get a list of security services.""" all_projects = int( os.environ.get("ALL_TENANTS", os.environ.get("ALL_PROJECTS", args.all_projects)) ) search_opts = { 'all_tenants': all_projects, 'status': args.status, 'name': args.name, 'type': args.type, 'user': args.user, 'dns_ip': args.dns_ip, 'server': args.server, 'domain': args.domain, 'offset': args.offset, 'limit': args.limit, } if cs.api_version.matches(api_versions.APIVersion("2.44"), api_versions.APIVersion()): search_opts['ou'] = args.ou elif args.ou: raise exceptions.CommandError( "Security service Organizational Unit (ou) option " "is only available with manila API version >= 2.44") if cs.api_version.matches(api_versions.APIVersion("2.76"), api_versions.APIVersion()): search_opts['default_ad_site'] = args.default_ad_site elif args.ou: raise exceptions.CommandError( "Security service Default AD site option " "is only available with manila API version >= 2.76") if args.share_network: search_opts['share_network_id'] = _find_share_network( cs, args.share_network).id security_services = cs.security_services.list(search_opts=search_opts, detailed=args.detailed) fields = ['id', 'name', 'status', 'type', ] if args.columns is not None: fields = _split_columns(columns=args.columns) if args.detailed: fields.append('share_networks') cliutils.print_list(security_services, fields=fields) @cliutils.arg( 'security_service', metavar='', nargs='+', help='Name or ID of the security service(s) to delete.') def do_security_service_delete(cs, args): """Delete one or more security services.""" failure_count = 0 for security_service in args.security_service: try: security_ref = _find_security_service( cs, security_service) cs.security_services.delete(security_ref) except Exception as e: failure_count += 1 print("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") elif cs.api_version < api_versions.APIVersion("2.70"): search_opts.update({ 'share_network_subnet_id': args.share_network_subnet}) fields.append("Share Network Subnet Id") else: search_opts.update({ 'share_network_subnet_id': args.share_network_subnet}) fields.append("Share Network Subnet IDs") if args.columns is not None: fields = _split_columns(columns=args.columns) share_servers = cs.share_servers.list(search_opts=search_opts) cliutils.print_list(share_servers, fields=fields) @cliutils.arg( 'id', metavar='', type=str, help='ID of share server.') def do_share_server_show(cs, args): """Show share server info (Admin only).""" share_server = cs.share_servers.get(args.id) # All 'backend_details' data already present as separated strings, # so remove big dict from view. if "backend_details" in share_server._info: del share_server._info["backend_details"] cliutils.print_dict(share_server._info) @cliutils.arg( 'id', metavar='', type=str, help='ID of share server.') def do_share_server_details(cs, args): """Show share server details (Admin only).""" details = cs.share_servers.details(args.id) cliutils.print_dict(details._info) @cliutils.arg( 'id', metavar='', nargs='+', type=str, help='ID of the share server(s) to delete.') @cliutils.arg( '--wait', action='store_true', help='Wait for share server to delete') @cliutils.service_type('sharev2') def do_share_server_delete(cs, args): """Delete one or more share servers (Admin only).""" failure_count = 0 share_servers_to_delete = [] for server_id in args.id: try: id_ref = _find_share_server(cs, server_id) share_servers_to_delete.append(id_ref) id_ref.delete() except Exception as e: failure_count += 1 print("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.") if args.wait: for share_server in share_servers_to_delete: try: _wait_for_resource_status( cs, share_server, resource_type='share_server', expected_status='deleted') except exceptions.CommandError as e: print(e, file=sys.stderr) @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,name".') def do_availability_zone_list(cs, args): """List all availability zones.""" if args.columns is not None: fields = _split_columns(columns=args.columns) else: fields = ("Id", "Name", "Created_At", "Updated_At") availability_zones = cs.availability_zones.list() cliutils.print_list(availability_zones, fields=fields) @cliutils.arg( '--host', metavar='', default=None, help='Name of host.') @cliutils.arg( '--binary', metavar='', default=None, help='Service binary.') @cliutils.arg( '--status', metavar='', default=None, help='Filter results by status.') @cliutils.arg( '--state', metavar='', default=None, help='Filter results by state.') @cliutils.arg( '--zone', metavar='', default=None, help='Availability zone.') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,host".') def do_service_list(cs, args): """List all services (Admin only).""" search_opts = { 'status': args.status, 'host': args.host, 'binary': args.binary, 'zone': args.zone, 'state': args.state, } fields = ["Id", "Binary", "Host", "Zone", "Status", "State", "Updated_at"] if args.columns is not None: fields = _split_columns(columns=args.columns) services = cs.services.list(search_opts=search_opts) cliutils.print_list(services, fields=fields) @cliutils.arg( 'host', metavar='', help="Host name as 'example_host@example_backend'.") @cliutils.arg( 'binary', metavar='', help="Service binary, could be 'manila-share' or 'manila-scheduler'.") def do_service_enable(cs, args): """Enables 'manila-share' or 'manila-scheduler' services (Admin only).""" columns = ("Host", "Binary", "Enabled") result = cs.services.enable(args.host, args.binary) result.enabled = not result.disabled cliutils.print_list([result], columns) @cliutils.arg( 'host', metavar='', help="Host name as 'example_host@example_backend'.") @cliutils.arg( 'binary', metavar='', help="Service binary, could be 'manila-share' or 'manila-scheduler'.") def do_service_disable(cs, args): """Disables 'manila-share' or 'manila-scheduler' services (Admin only).""" columns = ("Host", "Binary", "Enabled") result = cs.services.disable(args.host, args.binary) result.enabled = not result.disabled cliutils.print_list([result], columns) def _print_dict(data_dict): formatted_data = [] for date in data_dict: formatted_data.append("%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.') @cliutils.arg( '--wait', action='store_true', help='Wait for share extension') @cliutils.arg( '--force', action='store_true', help='Force attempt the extension of the share, only available with ' 'microversion 2.64 and higher. (admin only)') @cliutils.service_type('sharev2') def do_extend(cs, args): """Increases the size of an existing share.""" share = _find_share(cs, args.share) force = False if args.force: if cs.api_version < api_versions.APIVersion("2.64"): raise exceptions.CommandError( "args 'force' is available only starting with " "'2.64' API microversion.") force = True if force: cs.shares.extend(share, args.new_size, force=force) else: cs.shares.extend(share, args.new_size) if args.wait: share = _wait_for_share_status(cs, share) else: share = _find_share(cs, args.share) _print_share(cs, share) @cliutils.arg('share', metavar='', help='Name or ID of share to shrink.') @cliutils.arg('new_size', metavar='', type=int, help='New size of share, in GiBs.') @cliutils.arg( '--wait', action='store_true', help='Wait for share shrinkage') @cliutils.service_type('sharev2') def do_shrink(cs, args): """Decreases the size of an existing share.""" share = _find_share(cs, args.share) cs.shares.shrink(share, args.new_size) if args.wait: share = _wait_for_share_status(cs, share) else: share = _find_share(cs, args.share) _print_share(cs, share) ############################################################################## # # Share types # ############################################################################## def _print_type_extra_specs(share_type): """Prints share type extra specs or share group type specs.""" try: return _print_dict(share_type.get_keys()) except exceptions.NotFound: return None def _print_type_required_extra_specs(share_type): try: return _print_dict(share_type.get_required_keys()) except exceptions.NotFound: return "N/A" def _print_type_optional_extra_specs(share_type): try: return _print_dict(share_type.get_optional_keys()) except exceptions.NotFound: return "N/A" def _is_share_type_public(share_type): return 'public' if share_type.is_public else 'private' def _print_share_type_list(stypes, default_share_type=None, columns=None, description=False): def _is_default(share_type): if hasattr(share_type, 'is_default'): return 'YES' if share_type.is_default else '-' elif default_share_type: default = default_share_type.id return 'YES' if share_type.id == default else '-' else: return '-' formatters = { 'visibility': _is_share_type_public, 'is_default': _is_default, 'required_extra_specs': _print_type_required_extra_specs, 'optional_extra_specs': _print_type_optional_extra_specs, } for stype in stypes: stype = stype.to_dict() stype['visibility'] = stype.pop('is_public', 'unknown') fields = [ 'ID', 'Name', 'visibility', 'is_default', 'required_extra_specs', 'optional_extra_specs', ] if description: fields.append('Description') if columns is not None: fields = _split_columns(columns=columns, title=False) cliutils.print_list(stypes, fields, formatters) def _print_share_type(stype, default_share_type=None, show_des=False): def _is_default(share_type): if hasattr(share_type, 'is_default'): return 'YES' if share_type.is_default else '-' return '-' stype_dict = { 'ID': stype.id, 'Name': stype.name, 'Visibility': _is_share_type_public(stype), 'is_default': _is_default(stype), 'required_extra_specs': _print_type_required_extra_specs(stype), 'optional_extra_specs': _print_type_optional_extra_specs(stype), } if show_des: stype_dict['Description'] = stype.description cliutils.print_dict(stype_dict) def _print_type_and_extra_specs_list(stypes, columns=None): """Prints extra specs for a list of share types or share group types.""" formatters = { 'all_extra_specs': _print_type_extra_specs, } fields = ['ID', 'Name', 'all_extra_specs'] if columns is not None: fields = _split_columns(columns=columns, title=False) cliutils.print_list(stypes, fields, formatters) def _find_share_type(cs, stype): """Get a share type by name or ID.""" return apiclient_utils.find_resource(cs.share_types, stype) @cliutils.arg( '--all', dest='all', action='store_true', default=False, help='Display all share types whatever public or private ' 'OPTIONAL: Default=False. (Admin only).') @cliutils.arg( '--extra-specs', '--extra_specs', type=str, nargs='*', metavar='', action='single_alias', default=None, help='Filters results by a extra specs key and value of share type that ' 'was used for share creation. Available only for microversion >= ' '2.43. OPTIONAL: Default=None.') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,name".') def do_type_list(cs, args): """Print a list of available 'share types'.""" search_opts = None show_all = args.all extra_specs = _extract_extra_specs(args) if extra_specs: if cs.api_version < api_versions.APIVersion("2.43"): raise exceptions.CommandError( "Filter by 'extra_specs' is available only starting with " "'2.43' API microversion.") search_opts = { 'extra_specs': extra_specs } share_types = cs.share_types.list(show_all=show_all, search_opts=search_opts) default = None if share_types and not hasattr(share_types[0], 'is_default'): if ((args.columns and 'is_default' in args.columns) or args.columns is None): default = cs.share_types.get() show_des = cs.api_version.matches( api_versions.APIVersion("2.41"), api_versions.APIVersion()) _print_share_type_list(share_types, default_share_type=default, columns=args.columns, description=show_des) @cliutils.arg( 'share_type', metavar='', help='Name or ID of the share type.') def do_type_show(cs, args): """Show share type details.""" share_type = _find_share_type(cs, args.share_type) default = None if (share_type and not hasattr(share_type, 'is_default')): default = cs.share_types.get() _print_type_show(share_type, default_share_type=default) @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,name".') def do_extra_specs_list(cs, args): """Print a list of current 'share types and extra specs' (Admin Only).""" stypes = cs.share_types.list() _print_type_and_extra_specs_list(stypes, columns=args.columns) @cliutils.arg( 'name', metavar='', help="Name of the new share type.") @cliutils.arg( 'spec_driver_handles_share_servers', metavar='', type=str, help="Required extra specification. " "Valid values are 'true'/'1' and 'false'/'0'.") @cliutils.arg( '--description', metavar='', type=str, default=None, help='Filter results by description. ' 'Available only for microversion >= 2.41.') @cliutils.arg( '--snapshot_support', '--snapshot-support', metavar='', action='single_alias', help="Boolean extra spec used for filtering of back ends by their " "capability to create share snapshots.") @cliutils.arg( '--create_share_from_snapshot_support', '--create-share-from-snapshot-support', metavar='', action='single_alias', help="Boolean extra spec used for filtering of back ends by their " "capability to create shares from snapshots.") @cliutils.arg( '--revert_to_snapshot_support', '--revert-to-snapshot-support', metavar='', action='single_alias', help="Boolean extra spec used for filtering of back ends by their " "capability to revert shares to snapshots. (Default is False).") @cliutils.arg( '--mount_snapshot_support', '--mount-snapshot-support', metavar='', action='single_alias', help="Boolean extra spec used for filtering of back ends by their " "capability to mount share snapshots. (Default is False).") @cliutils.arg( '--extra-specs', '--extra_specs', # alias type=str, nargs='*', metavar='', action='single_alias', help="Extra specs key and value of share type that will be" " used for share type creation. OPTIONAL: Default=None." " example --extra-specs thin_provisioning=' True', " "replication_type=readable.", default=None) @cliutils.arg( '--is_public', '--is-public', metavar='', action='single_alias', help="Make type accessible to the public (default true).") def do_type_create(cs, args): """Create a new share type (Admin only).""" kwargs = { "name": args.name, "is_public": strutils.bool_from_string(args.is_public, default=True), } try: kwargs['spec_driver_handles_share_servers'] = ( strutils.bool_from_string( args.spec_driver_handles_share_servers, strict=True)) except ValueError as e: msg = ("Argument spec_driver_handles_share_servers " "argument is not valid: %s" % str(e)) raise exceptions.CommandError(msg) kwargs['extra_specs'] = _extract_extra_specs(args) if 'driver_handles_share_servers' in kwargs['extra_specs']: msg = ("Argument 'driver_handles_share_servers' is already " "set via positional argument.") raise exceptions.CommandError(msg) show_des = False if cs.api_version.matches(api_versions.APIVersion("2.41"), api_versions.APIVersion()): show_des = True kwargs['description'] = getattr(args, 'description') elif getattr(args, 'description'): raise exceptions.CommandError( "Pattern based option (description)" " is only available with manila API version >= 2.41") boolean_keys = ( 'snapshot_support', 'create_share_from_snapshot_support', 'revert_to_snapshot_support', 'mount_snapshot_support' ) for key in boolean_keys: value = getattr(args, key) if value is not None and key in kwargs['extra_specs']: msg = ("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, str(e))) raise exceptions.CommandError(msg) stype = cs.share_types.create(**kwargs) _print_share_type(stype, show_des=show_des) @cliutils.arg( 'id', metavar='', help="Name or ID of the share type to update.") @cliutils.arg( '--name', metavar='', type=str, help="New name of share type.") @cliutils.arg( '--description', metavar='', type=str, default=None, help="New description of share type.") @cliutils.arg( '--is-public', '--is_public', metavar='', action='single_alias', help="New visibility of the share type. If set to True, share type will " "be available to all projects in the cloud.") @api_versions.wraps("2.50") def do_type_update(cs, args): """Update share type name, description, and/or visibility. (Admin only).""" name = getattr(args, 'name') description = getattr(args, 'description') is_public = getattr(args, 'is_public') if not name and description is None and is_public is None: msg = ("A description and/or non-empty name and/or boolean is_public " "must be supplied to update the respective attributes of the " "share type.") raise exceptions.CommandError(msg) kwargs = {} kwargs['name'] = name if is_public: try: kwargs['is_public'] = strutils.bool_from_string(is_public, strict=True) except ValueError as e: raise exceptions.CommandError("The value of 'is_public' is" " invalid: %s", str(e)) kwargs['description'] = description stype = _find_share_type(cs, args.id) stype = stype.update(**kwargs) _print_share_type(stype, show_des=True) @cliutils.arg( 'id', metavar='', nargs='+', help="Name or ID of the share type(s) to delete.") def do_type_delete(cs, args): """Delete one or more specific share types (Admin only).""" failure_count = 0 for name_or_id in args.id: try: id_ref = _find_share_type(cs, name_or_id) cs.share_types.delete(id_ref) except Exception as e: failure_count += 1 print("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 (only key is necessary to unset).') def do_type_key(cs, args): """Set or unset extra_spec for a share type (Admin only).""" stype = _find_share_type(cs, args.stype) if args.metadata is not None: keypair = _extract_metadata(args) if args.action == 'set': stype.set_keys(keypair) elif args.action == 'unset': stype.unset_keys(list(keypair)) @cliutils.arg( 'share_type', metavar='', help="Filter results by share type name or ID.") def do_type_access_list(cs, args): """Print access information about the given share type (Admin only).""" share_type = _find_share_type(cs, args.share_type) if share_type.is_public: raise exceptions.CommandError("Forbidden to get access list " "for public share type.") access_list = cs.share_type_access.list(share_type) columns = ['Project_ID'] cliutils.print_list(access_list, columns) @cliutils.arg( 'share_type', metavar='', help="Share type name or ID to add access" " for the given project.") @cliutils.arg( 'project_id', metavar='', help='Project ID to add share type access for.') def do_type_access_add(cs, args): """Adds share type access for the given project (Admin only).""" vtype = _find_share_type(cs, args.share_type) cs.share_type_access.add_project_access(vtype, args.project_id) @cliutils.arg( 'share_type', metavar='', help=('Share type name or ID to remove access ' 'for the given project.')) @cliutils.arg( 'project_id', metavar='', help='Project ID to remove share type access for.') def do_type_access_remove(cs, args): """Removes share type access for the given project (Admin only).""" vtype = _find_share_type(cs, args.share_type) cs.share_type_access.remove_project_access( vtype, args.project_id) ############################################################################## # # Share group types # ############################################################################## def _print_share_group_type_list(share_group_types, default_share_group_type=None, columns=None): def _is_default(share_group_type): if hasattr(share_group_type, 'is_default'): return 'YES' if share_group_type.is_default else '-' elif default_share_group_type: default = default_share_group_type.id return 'YES' if share_group_type.id == default else '-' else: return '-' formatters = { 'visibility': _is_share_type_public, 'is_default': _is_default, } for sg_type in share_group_types: sg_type = sg_type.to_dict() sg_type['visibility'] = sg_type.pop('is_public', 'unknown') fields = [ 'ID', 'Name', 'visibility', 'is_default', ] if columns is not None: fields = _split_columns(columns=columns, title=False) cliutils.print_list(share_group_types, fields, formatters, sortby_index=None) def _print_share_group_type(share_group_type, default_share_type=None): def _is_default(share_group_type): if hasattr(share_group_type, 'is_default'): return 'YES' if share_group_type.is_default else '-' return '-' share_group_type_dict = { 'ID': share_group_type.id, 'Name': share_group_type.name, 'Visibility': _is_share_type_public(share_group_type), 'is_default': _is_default(share_group_type) } cliutils.print_dict(share_group_type_dict) def _find_share_group_type(cs, sg_type): """Get a share group type by name or ID.""" return apiclient_utils.find_resource(cs.share_group_types, sg_type) @cliutils.arg( '--all', dest='all', action='store_true', default=False, help='Display all share group types (Admin only).') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,name".') @cliutils.service_type('sharev2') def do_share_group_type_list(cs, args): """Print a list of available 'share group types'.""" sg_types = cs.share_group_types.list(show_all=args.all) default = None if sg_types and not hasattr(sg_types[0], 'is_default'): if ((args.columns and 'is_default' in args.columns) or args.columns is None): default = cs.share_group_types.get() _print_share_group_type_list( sg_types, default_share_group_type=default, columns=args.columns) @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,name".') @cliutils.service_type('sharev2') def do_share_group_type_specs_list(cs, args): """Print a list of 'share group types specs' (Admin Only).""" sg_types = cs.share_group_types.list() _print_type_and_extra_specs_list(sg_types, columns=args.columns) @cliutils.arg( 'name', metavar='', help='Name of the new share group type.') @cliutils.arg( 'share_types', metavar='', type=str, help='Comma-separated list of share type names or IDs.') @cliutils.arg( '--is_public', '--is-public', metavar='', action='single_alias', help='Make type accessible to the public (default true).') @cliutils.arg( '--group-specs', '--group_specs', metavar='', type=str, nargs='*', action='single_alias', default=None, help='Share Group type extra specs by key and value. ' 'OPTIONAL: Default=None. ' 'Example: "--group-specs consistent_snapshot_support=host".',) @cliutils.service_type('sharev2') def do_share_group_type_create(cs, args): """Create a new share group type (Admin only).""" share_types = [_find_share_type(cs, share_type) for share_type in args.share_types.split(',')] kwargs = { 'share_types': share_types, 'name': args.name, 'is_public': strutils.bool_from_string(args.is_public, default=True), } if args.group_specs is not None: kwargs['group_specs'] = _extract_group_specs(args) sg_type = cs.share_group_types.create(**kwargs) _print_share_group_type(sg_type) @cliutils.arg( 'id', metavar='', help="Name or ID of the share group type to delete.") @cliutils.service_type('sharev2') def do_share_group_type_delete(cs, args): """Delete a specific share group type (Admin only).""" share_group_type = _find_share_group_type(cs, args.id) cs.share_group_types.delete(share_group_type) @cliutils.arg( 'share_group_type', metavar='', help="Name or ID of the share group type.") @cliutils.arg( 'action', metavar='', choices=['set', 'unset'], help="Actions: 'set' or 'unset'.") @cliutils.arg( 'group_specs', metavar='', nargs='*', default=None, help='Group specs to set or unset (only key is necessary to unset).') @cliutils.service_type('sharev2') def do_share_group_type_key(cs, args): """Set or unset group_spec for a share group type (Admin only).""" sg_type = _find_share_group_type(cs, args.share_group_type) if args.group_specs is not None: keypair = _extract_group_specs(args) if args.action == 'set': sg_type.set_keys(keypair) elif args.action == 'unset': sg_type.unset_keys(list(keypair)) @cliutils.arg( 'share_group_type', metavar='', help="Filter results by share group type name or ID.") def do_share_group_type_access_list(cs, args): """Print access information about a share group type (Admin only).""" share_group_type = _find_share_group_type(cs, args.share_group_type) if share_group_type.is_public: raise exceptions.CommandError( "Forbidden to get access list for public share group type.") access_list = cs.share_group_type_access.list(share_group_type) columns = ['Project_ID'] cliutils.print_list(access_list, columns) @cliutils.arg( 'share_group_type', metavar='', help='Share group type name or ID to add access for the given project.') @cliutils.arg( 'project_id', metavar='', help='Project ID to add share group type access for.') def do_share_group_type_access_add(cs, args): """Adds share group type access for the given project (Admin only).""" share_group_type = _find_share_group_type(cs, args.share_group_type) cs.share_group_type_access.add_project_access( share_group_type, args.project_id) @cliutils.arg( 'share_group_type', metavar='', help='Share group type name or ID to remove access for the given project.') @cliutils.arg( 'project_id', metavar='', help='Project ID to remove share group type access for.') def do_share_group_type_access_remove(cs, args): """Removes share group type access for the given project (Admin only).""" share_group_type = _find_share_group_type(cs, args.share_group_type) cs.share_group_type_access.remove_project_access( share_group_type, args.project_id) ############################################################################## # # Share groups # ############################################################################## @cliutils.arg( '--name', metavar='', help='Optional share group name. (Default=None)', default=None) @cliutils.arg( '--description', metavar='', help='Optional share group description. (Default=None)', default=None) @cliutils.arg( '--share-types', '--share_types', metavar='', type=str, default=None, action='single_alias', help='Comma-separated list of share types. (Default=None)') @cliutils.arg( '--share-group-type', '--share_group_type', '--type', metavar='', type=str, default=None, action='single_alias', help="Share group type name or ID of the share group to be created. " "(Default=None)") @cliutils.arg( '--share-network', '--share_network', metavar='', type=str, default=None, action='single_alias', help='Specify share network name or id.') @cliutils.arg( '--source-share-group-snapshot', '--source_share_group_snapshot', metavar='', type=str, action='single_alias', help='Optional share group snapshot name or ID to create the share group ' 'from. (Default=None)', default=None) @cliutils.arg( '--availability-zone', '--availability_zone', '--az', default=None, action='single_alias', metavar='', help='Optional availability zone in which group should be created. ' '(Default=None)') @cliutils.arg( '--wait', action='store_true', default=False, help='Wait for share group to create') @cliutils.service_type('sharev2') def do_share_group_create(cs, args): """Creates a new share group.""" share_types = [] if args.share_types: s_types = args.share_types.split(',') for s_type in s_types: share_type = _find_share_type(cs, s_type) share_types.append(share_type) share_group_type = None if args.share_group_type: share_group_type = _find_share_group_type(cs, args.share_group_type) share_network = None if args.share_network: share_network = _find_share_network(cs, args.share_network) share_group_snapshot = None if args.source_share_group_snapshot: share_group_snapshot = _find_share_group_snapshot( cs, args.source_share_group_snapshot) kwargs = { 'share_group_type': share_group_type, 'share_types': share_types or None, 'name': args.name, 'description': args.description, 'availability_zone': args.availability_zone, 'source_share_group_snapshot': share_group_snapshot, 'share_network': share_network, } share_group = cs.share_groups.create(**kwargs) if args.wait: try: share_group = _wait_for_resource_status( cs, share_group, resource_type='share_group', expected_status='available') except exceptions.CommandError as e: print(e, file=sys.stderr) _print_share_group(cs, share_group) @cliutils.arg( '--all-tenants', '--all-projects', action='single_alias', dest='all_projects', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all projects (Admin only).') @cliutils.arg( '--name', metavar='', type=str, default=None, help='Filter results by name.') @cliutils.arg( '--description', metavar='', type=str, default=None, help='Filter results by description. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--status', metavar='', type=str, default=None, help='Filter results by status.') @cliutils.arg( '--share-server-id', '--share-server_id', '--share_server-id', '--share_server_id', metavar='', type=str, default=None, action='single_alias', help='Filter results by share server ID (Admin only).') @cliutils.arg( '--share-group-type', '--share-group-type-id', '--share_group_type', '--share_group_type_id', metavar='', type=str, default=None, action='single_alias', help='Filter results by a share group type ID or name that was used for ' 'share group creation.') @cliutils.arg( '--snapshot', metavar='', type=str, default=None, help='Filter results by share group snapshot name or ID that was used to ' 'create the share group.') @cliutils.arg( '--host', metavar='', default=None, help='Filter results by host.') @cliutils.arg( '--share-network', '--share_network', metavar='', type=str, default=None, action='single_alias', help='Filter results by share-network name or ID.') @cliutils.arg( '--project-id', '--project_id', metavar='', type=str, default=None, action='single_alias', help="Filter results by project ID. Useful with set key '--all-projects'.") @cliutils.arg( '--limit', metavar='', type=int, default=None, help='Maximum number of share groups to return. (Default=None)') @cliutils.arg( '--offset', metavar="", default=None, help='Start position of share group listing.') @cliutils.arg( '--sort-key', '--sort_key', metavar='', type=str, default=None, action='single_alias', help='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=str, default=None, help='Filter results matching a share group name pattern. ' 'Available only for microversion >= 2.36.') @cliutils.arg( '--description~', metavar='', type=str, default=None, help='Filter results matching a share group description pattern. ' 'Available only for microversion >= 2.36.') @cliutils.service_type('sharev2') def do_share_group_list(cs, args): """List share groups with filters.""" if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) else: list_of_keys = ('ID', 'Name', 'Status', 'Description') all_projects = int( os.environ.get("ALL_TENANTS", os.environ.get("ALL_PROJECTS", args.all_projects)) ) empty_obj = type('Empty', (object,), {'id': None}) sg_type = (_find_share_group_type(cs, args.share_group_type) if args.share_group_type else empty_obj) snapshot = (_find_share_snapshot(cs, args.snapshot) if args.snapshot else empty_obj) share_network = (_find_share_network(cs, args.share_network) if args.share_network else empty_obj) search_opts = { 'offset': args.offset, 'limit': args.limit, 'all_tenants': all_projects, 'name': args.name, 'status': args.status, 'share_server_id': args.share_server_id, 'share_group_type_id': sg_type.id, 'source_share_group_snapshot_id': snapshot.id, 'host': args.host, 'share_network_id': share_network.id, 'project_id': args.project_id, } if cs.api_version.matches(api_versions.APIVersion("2.36"), api_versions.APIVersion()): search_opts['name~'] = getattr(args, 'name~') search_opts['description~'] = getattr(args, 'description~') search_opts['description'] = getattr(args, 'description') elif (getattr(args, 'name~') or getattr(args, 'description~') or getattr(args, 'description')): raise exceptions.CommandError( "Pattern based filtering (name~, description~ and description)" " is only available with manila API version >= 2.36") share_groups = cs.share_groups.list( search_opts=search_opts, sort_key=args.sort_key, sort_dir=args.sort_dir) cliutils.print_list(share_groups, fields=list_of_keys, sortby_index=None) @cliutils.arg( 'share_group', metavar='', help='Name or ID of the share group.') @cliutils.service_type('sharev2') def do_share_group_show(cs, args): """Show details about a share group.""" share_group = _find_share_group(cs, args.share_group) _print_share_group(cs, share_group) @cliutils.arg( 'share_group', metavar='', help='Name or ID of the share group to update.') @cliutils.arg( '--name', metavar='', default=None, help='Optional new name for the share group. (Default=None)') @cliutils.arg( '--description', metavar='', help='Optional share group description. (Default=None)', default=None) @cliutils.service_type('sharev2') def do_share_group_update(cs, args): """Update a share group.""" kwargs = {} if args.name is not None: kwargs['name'] = args.name if args.description is not None: kwargs['description'] = args.description if not kwargs: msg = "Must supply name and/or description" raise exceptions.CommandError(msg) share_group = _find_share_group(cs, args.share_group) share_group = cs.share_groups.update(share_group, **kwargs) _print_share_group(cs, share_group) @cliutils.arg( 'share_group', metavar='', nargs='+', help='Name or ID of the share group(s).') @cliutils.arg( '--force', action='store_true', default=False, help='Attempt to force delete the share group (Default=False)' ' (Admin only).') @cliutils.arg( '--wait', action='store_true', default=False, help='Wait for share group to delete') @cliutils.service_type('sharev2') def do_share_group_delete(cs, args): """Delete one or more share groups.""" failure_count = 0 share_group_to_delete = [] for share_group in args.share_group: try: share_group_ref = _find_share_group(cs, share_group) share_group_to_delete.append(share_group_ref) share_group_ref.delete(args.force) except Exception as e: failure_count += 1 print("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.") if args.wait: for share_group in share_group_to_delete: try: _wait_for_resource_status( cs, share_group, resource_type='share_group', expected_status='deleted') except exceptions.CommandError as e: print(e, file=sys.stderr) @cliutils.arg( 'share_group', metavar='', help='Name or ID of the share group to modify.') @cliutils.arg( '--state', metavar='', default='available', help=('Indicate which state to assign the share group. ' 'Options include available, error, creating, deleting, ' 'error_deleting. If no state is provided, ' 'available will be used.')) @cliutils.service_type('sharev2') def do_share_group_reset_state(cs, args): """Explicitly update the state of a share group (Admin only). """ share_group = _find_share_group(cs, args.share_group) cs.share_groups.reset_state(share_group, args.state) ############################################################################## # # Share group snapshots # ############################################################################## @cliutils.arg( 'share_group', metavar='', help='Name or ID of the share group.') @cliutils.arg( '--name', metavar='', help='Optional share group snapshot name. (Default=None)', default=None) @cliutils.arg( '--description', metavar='', help='Optional share group snapshot description. (Default=None)', default=None) @cliutils.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', '--all-projects', action='single_alias', dest='all_projects', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Display information from all projects (Admin only).') @cliutils.arg( '--name', metavar='', default=None, help='Filter results by name.') @cliutils.arg( '--status', metavar='', default=None, help='Filter results by status.') @cliutils.arg( '--share-group-id', '--share_group_id', metavar='', default=None, action='single_alias', help='Filter results by share group ID.') @cliutils.arg( '--limit', metavar='', type=int, default=None, help='Maximum number of share group snapshots to return. ' '(Default=None)') @cliutils.arg( '--offset', metavar="", default=None, help='Start position of share group snapshot listing.') @cliutils.arg( '--sort-key', '--sort_key', metavar='', type=str, default=None, action='single_alias', help='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_projects = int( os.environ.get("ALL_TENANTS", os.environ.get("ALL_PROJECTS", args.all_projects)) ) search_opts = { 'offset': args.offset, 'limit': args.limit, 'all_tenants': all_projects, 'name': args.name, 'status': args.status, 'share_group_id': args.share_group_id, } share_group_snapshots = cs.share_group_snapshots.list( detailed=args.detailed, search_opts=search_opts, sort_key=args.sort_key, sort_dir=args.sort_dir) cliutils.print_list(share_group_snapshots, fields=list_of_keys, sortby_index=None) @cliutils.arg( 'share_group_snapshot', metavar='', help='Name or ID of the share group snapshot.') @cliutils.service_type('sharev2') def do_share_group_snapshot_show(cs, args): """Show details about a share group snapshot.""" sg_snapshot = _find_share_group_snapshot(cs, args.share_group_snapshot) _print_share_group_snapshot(cs, sg_snapshot) @cliutils.arg( 'share_group_snapshot', metavar='', help='Name or ID of the share group snapshot.') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,name".') @cliutils.service_type('sharev2') def do_share_group_snapshot_list_members(cs, args): """List members of a share group snapshot.""" if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) else: list_of_keys = ('Share ID', 'Size') sg_snapshot = _find_share_group_snapshot(cs, args.share_group_snapshot) members = [type('ShareGroupSnapshotMember', (object,), member) for member in sg_snapshot._info.get('members', [])] cliutils.print_list(members, fields=list_of_keys) @cliutils.arg( '--state', metavar='', default='available', help=('Indicate which state to assign the share group snapshot. ' 'Options include available, error, creating, deleting, ' 'error_deleting. If no state is provided, ' 'available will be used.')) @cliutils.arg( 'share_group_snapshot', metavar='', help='Name or ID of the share group snapshot.') @cliutils.service_type('sharev2') def do_share_group_snapshot_reset_state(cs, args): """Explicitly update the state of a share group snapshot (Admin only). """ sg_snapshot = _find_share_group_snapshot(cs, args.share_group_snapshot) cs.share_group_snapshots.reset_state(sg_snapshot, args.state) @cliutils.arg( 'share_group_snapshot', metavar='', help='Name or ID of the share group snapshot to update.') @cliutils.arg( '--name', metavar='', default=None, help='Optional new name for the share group snapshot. (Default=None)') @cliutils.arg( '--description', metavar='', help='Optional share group snapshot description. (Default=None)', default=None) @cliutils.service_type('sharev2') def do_share_group_snapshot_update(cs, args): """Update a share group snapshot.""" kwargs = {} if args.name is not None: kwargs['name'] = args.name if args.description is not None: kwargs['description'] = args.description if not kwargs: msg = "Must supply name and/or description" raise exceptions.CommandError(msg) sg_snapshot = _find_share_group_snapshot(cs, args.share_group_snapshot) cs.share_group_snapshots.update(sg_snapshot, **kwargs) @cliutils.arg( 'share_group_snapshot', metavar='', nargs='+', help='Name or ID of the share group snapshot(s) to delete.') @cliutils.arg( '--force', action='store_true', default=False, help='Attempt to force delete the share group snapshot(s) (Default=False)' ' (Admin only).') @cliutils.service_type('sharev2') def do_share_group_snapshot_delete(cs, args): """Remove one or more share group snapshots.""" failure_count = 0 kwargs = {} kwargs['force'] = args.force for sg_snapshot in args.share_group_snapshot: try: sg_snapshot_ref = _find_share_group_snapshot(cs, sg_snapshot) cs.share_group_snapshots.delete(sg_snapshot_ref, **kwargs) 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.""" share = _find_share(cs, args.share_id) if args.share_id else None if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) else: list_of_keys = [ 'ID', 'Status', 'Replica State', 'Share ID', 'Host', 'Availability Zone', 'Updated At', ] if share: replicas = cs.share_replicas.list(share) else: replicas = cs.share_replicas.list() cliutils.print_list(replicas, list_of_keys) @api_versions.wraps("2.11", "2.66") @cliutils.arg( 'share', metavar='', help='Name or ID of the share to replicate.') @cliutils.arg( '--availability-zone', '--availability_zone', '--az', default=None, action='single_alias', metavar='', help='Optional Availability zone in which replica should be created.') def do_share_replica_create(cs, args): """Create a share replica.""" share = _find_share(cs, args.share) body = { 'share': share, 'availability_zone': args.availability_zone, } replica = cs.share_replicas.create(**body) _print_share_replica(cs, replica) @api_versions.wraps("2.67", "2.71") @cliutils.arg( 'share', metavar='', help='Name or ID of the share to replicate.') @cliutils.arg( '--availability-zone', '--availability_zone', '--az', default=None, action='single_alias', metavar='', help='Optional Availability zone in which replica should be created.') @cliutils.arg( '--scheduler-hints', '--scheduler_hints', '--sh', metavar='', nargs='*', help='Scheduler hints for the share replica as key=value pairs, ' 'Supported key is only_host. Available for microversion >= 2.67. ', default=None) def do_share_replica_create(cs, args): # noqa """Create a share replica.""" share = _find_share(cs, args.share) scheduler_hints = {} if args.scheduler_hints: hints = _extract_key_value_options(args, 'scheduler_hints') if 'only_host' not in hints.keys() or len(hints) > 1: raise exceptions.CommandError( "The only valid key supported with the --scheduler-hints " "argument is 'only_host'.") scheduler_hints['only_host'] = hints.get('only_host') body = { 'share': share, 'availability_zone': args.availability_zone, } if scheduler_hints: body['scheduler_hints'] = scheduler_hints replica = cs.share_replicas.create(**body) _print_share_replica(cs, replica) @api_versions.wraps("2.72") @cliutils.arg( 'share', metavar='', help='Name or ID of the share to replicate.') @cliutils.arg( '--availability-zone', '--availability_zone', '--az', default=None, action='single_alias', metavar='', help='Optional Availability zone in which replica should be created.') @cliutils.arg( '--scheduler-hints', '--scheduler_hints', '--sh', metavar='', nargs='*', help='Scheduler hints for the share replica as key=value pairs, ' 'Supported key is only_host. Available for microversion >= 2.67. ', default=None) @cliutils.arg( '--share-network', '--share_network', metavar='', default=None, action='single_alias', help='Optional network info ID or name. ' 'Available only for microversion >= 2.72') def do_share_replica_create(cs, args): # noqa """Create a share replica.""" share = _find_share(cs, args.share) scheduler_hints = {} if args.scheduler_hints: hints = _extract_key_value_options(args, 'scheduler_hints') if 'only_host' not in hints.keys() or len(hints) > 1: raise exceptions.CommandError( "The only valid key supported with the --scheduler-hints " "argument is 'only_host'.") scheduler_hints['only_host'] = hints.get('only_host') share_network = None if args.share_network: share_network = _find_share_network(cs, args.share_network) body = { 'share': share, 'availability_zone': args.availability_zone, } if scheduler_hints: body['scheduler_hints'] = scheduler_hints if share_network: body['share_network'] = share_network replica = cs.share_replicas.create(**body) _print_share_replica(cs, replica) @cliutils.arg( 'replica', metavar='', help='ID of the share replica.') @api_versions.wraps("2.11", "2.46") def do_share_replica_show(cs, args): """Show details about a replica.""" replica = cs.share_replicas.get(args.replica) _print_share_replica(cs, replica) @api_versions.wraps("2.47") # noqa @cliutils.arg( 'replica', metavar='', help='ID of the share replica.') def do_share_replica_show(cs, args): # noqa """Show details about a replica.""" replica = cs.share_replicas.get(args.replica) export_locations = cs.share_replica_export_locations.list(replica) replica._info['export_locations'] = export_locations _print_share_replica(cs, replica) @cliutils.arg( 'replica', metavar='', nargs='+', help='ID of the share replica.') @cliutils.arg( '--force', action='store_true', default=False, help='Attempt to force deletion of a replica on its backend. Using ' 'this option will purge the replica from Manila even if it ' 'is not cleaned up on the backend. Defaults to False.') @api_versions.wraps("2.11") def do_share_replica_delete(cs, args): """Remove one or more share replicas.""" failure_count = 0 kwargs = { "force": args.force } for replica in args.replica: try: replica_ref = _find_share_replica(cs, replica) cs.share_replicas.delete(replica_ref, **kwargs) 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", "2.74") def do_share_replica_promote(cs, args): """Promote specified replica to 'active' replica_state.""" replica = _find_share_replica(cs, args.replica) cs.share_replicas.promote(replica) @cliutils.arg( 'replica', metavar='', help='ID of the share replica.') @cliutils.arg( '--quiesce-wait-time', metavar='', default=None, help='Quiesce wait time in seconds. Available for ' 'microversion >= 2.75') @api_versions.wraps("2.75") # noqa def do_share_replica_promote(cs, args): # noqa """Promote specified replica to 'active' replica_state.""" replica = _find_share_replica(cs, args.replica) quiesce_wait_time = None if args.quiesce_wait_time: quiesce_wait_time = args.quiesce_wait_time cs.share_replicas.promote(replica, quiesce_wait_time) @api_versions.wraps("2.47") @cliutils.arg( 'replica', metavar='', help='ID of the share replica.') @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,path,replica_state".') def do_share_replica_export_location_list(cs, args): """List export locations of a share replica.""" if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) else: list_of_keys = [ 'ID', 'Availability Zone', 'Replica State', 'Preferred', 'Path', ] replica = _find_share_replica(cs, args.replica) export_locations = cs.share_replica_export_locations.list(replica) cliutils.print_list(export_locations, list_of_keys) @api_versions.wraps("2.47") @cliutils.arg( 'replica', metavar='', help='Name or ID of the share replica.') @cliutils.arg( 'export_location', metavar='', help='ID of the share replica export location.') def do_share_replica_export_location_show(cs, args): """Show details of a share replica's export location.""" replica = _find_share_replica(cs, args.replica) export_location = cs.share_replica_export_locations.get( replica, args.export_location) view_data = export_location._info.copy() cliutils.print_dict(view_data) @cliutils.arg( 'replica', metavar='', help='ID of the share replica to modify.') @cliutils.arg( '--state', metavar='', default='available', help=('Indicate which state to assign the replica. Options include ' 'available, error, creating, deleting, error_deleting. If no ' 'state is provided, available will be used.')) @api_versions.wraps("2.11") def do_share_replica_reset_state(cs, args): """Explicitly update the 'status' of a share replica.""" replica = _find_share_replica(cs, args.replica) cs.share_replicas.reset_state(replica, args.state) @cliutils.arg( 'replica', metavar='', help='ID of the share replica to modify.') @cliutils.arg( '--replica-state', '--replica_state', '--state', # alias for user sanity metavar='', default='out_of_sync', action='single_alias', help=('Indicate which replica_state to assign the replica. Options ' 'include in_sync, out_of_sync, active, error. If no ' 'state is provided, out_of_sync will be used.')) @api_versions.wraps("2.11") def do_share_replica_reset_replica_state(cs, args): """Explicitly update the 'replica_state' of a share replica.""" replica = _find_share_replica(cs, args.replica) cs.share_replicas.reset_replica_state(replica, args.replica_state) @cliutils.arg( 'replica', metavar='', help='ID of the share replica to resync.') @api_versions.wraps("2.11") def do_share_replica_resync(cs, args): """Attempt to update the share replica with its 'active' mirror.""" replica = _find_share_replica(cs, args.replica) cs.share_replicas.resync(replica) ############################################################################## # # Share Transfer # ############################################################################## def _print_share_transfer(transfer): info = transfer._info.copy() info.pop('links', None) cliutils.print_dict(info) @api_versions.wraps("2.77") @cliutils.arg( 'share', metavar='', help='Name or ID of share to transfer.') @cliutils.arg( '--name', metavar='', default=None, help='Transfer name. Default=None.') def do_share_transfer_create(cs, args): """Creates a share transfer.""" share = _find_share(cs, args.share) transfer = cs.transfers.create(share.id, args.name) _print_share_transfer(transfer) @api_versions.wraps("2.77") @cliutils.arg( 'transfer', metavar='', nargs='+', help='ID or name of the transfer(s).') def do_share_transfer_delete(cs, args): """Remove one or more transfers.""" failure_count = 0 for transfer in args.transfer: try: transfer_ref = _find_share_transfer(cs, transfer) transfer_ref.delete() except Exception as e: failure_count += 1 print("Delete for share transfer %s failed: %s" % (transfer, e), file=sys.stderr) if failure_count == len(args.transfer): raise exceptions.CommandError("Unable to delete any of the specified " "transfers.") @api_versions.wraps("2.77") @cliutils.arg( 'transfer', metavar='', help='ID of transfer to accept.') @cliutils.arg( 'auth_key', metavar='', help='Authentication key of transfer to accept.') @cliutils.arg( '--clear-rules', '--clear_rules', dest='clear_rules', action='store_true', default=False, help="Whether manila should clean up the access rules after the " "transfer is complete. (Default=False)") def do_share_transfer_accept(cs, args): """Accepts a share transfer.""" cs.transfers.accept(args.transfer, args.auth_key, clear_access_rules=args.clear_rules) @api_versions.wraps("2.77") @cliutils.arg( '--all-tenants', '--all-projects', action='single_alias', dest='all_projects', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help='Shows details for all tenants. (Admin only).') @cliutils.arg( '--name', metavar='', default=None, action='single_alias', help='Transfer name. Default=None.') @cliutils.arg( '--id', metavar='', default=None, action='single_alias', help='Transfer ID. Default=None.') @cliutils.arg( '--resource-type', '--resource_type', metavar='', default=None, action='single_alias', help='Transfer type, which can be share or network. Default=None.') @cliutils.arg( '--resource-id', '--resource_id', metavar='', default=None, action='single_alias', help='Transfer resource id. Default=None.') @cliutils.arg( '--source-project-id', '--source_project_id', metavar='', default=None, action='single_alias', help='Transfer source project id. Default=None.') @cliutils.arg( '--limit', metavar='', type=int, default=None, help='Maximum number of messages to return. (Default=None)') @cliutils.arg( '--offset', metavar="", default=None, help='Start position of message listing.') @cliutils.arg( '--sort-key', '--sort_key', metavar='', type=str, default=None, action='single_alias', help='Key to be sorted, available keys are %(keys)s. Default=None.' % {'keys': constants.SHARE_TRANSFER_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', metavar='<0|1>', nargs='?', type=int, const=1, default=0, help="Show detailed information about filtered share transfers.") @cliutils.arg( '--columns', metavar='', type=str, default=None, help='Comma separated list of columns to be displayed ' 'example --columns "id,resource_id".') def do_share_transfer_list(cs, args): """Lists all transfers.""" if args.columns is not None: list_of_keys = _split_columns(columns=args.columns) else: list_of_keys = ['ID', 'Name', 'Resource Type', 'Resource Id'] if args.detailed: list_of_keys.extend(['Created At', 'Expires At', 'Source Project Id', 'Destination Project Id', 'Accepted']) all_projects = int( os.environ.get("ALL_TENANTS", os.environ.get("ALL_PROJECTS", args.all_projects)) ) search_opts = { 'offset': args.offset, 'limit': args.limit, 'all_tenants': all_projects, 'id': args.id, 'name': args.name, 'resource_type': args.resource_type, 'resource_id': args.resource_id, 'source_project_id': args.source_project_id, } share_transfers = cs.transfers.list( detailed=args.detailed, search_opts=search_opts, sort_key=args.sort_key, sort_dir=args.sort_dir) cliutils.print_list(share_transfers, fields=list_of_keys, sortby_index=None) @api_versions.wraps("2.77") @cliutils.arg( 'transfer', metavar='', help='Name or ID of transfer to show.') def do_share_transfer_show(cs, args): """Delete a transfer.""" transfer = _find_share_transfer(cs, args.transfer) _print_share_transfer(transfer) ############################################################################## # # User Messages # ############################################################################## @api_versions.wraps("2.37") @cliutils.arg( '--resource_id', '--resource-id', '--resource', metavar='', default=None, action='single_alias', help='Filters results by a resource uuid. Default=None.') @cliutils.arg( '--resource_type', '--resource-type', metavar='', default=None, action='single_alias', help='Filters results by a resource type. Default=None. ' 'Example: "manila message-list --resource_type share"') @cliutils.arg( '--action_id', '--action-id', '--action', metavar='', default=None, action='single_alias', help='Filters results by action id. Default=None.') @cliutils.arg( '--detail_id', '--detail-id', '--detail', metavar='', default=None, action='single_alias', help='Filters results by detail id. Default=None.') @cliutils.arg( '--request_id', '--request-id', '--request', metavar='', default=None, action='single_alias', help='Filters results by request id. Default=None.') @cliutils.arg( '--level', '--message_level', '--message-level', metavar='', default=None, action='single_alias', help='Filters results by the message level. Default=None. ' 'Example: "manila message-list --level ERROR".') @cliutils.arg( '--limit', metavar='', type=int, default=None, help='Maximum number of messages to return. (Default=None)') @cliutils.arg( '--offset', metavar="", default=None, help='Start position of message listing.') @cliutils.arg( '--sort-key', '--sort_key', metavar='', type=str, default=None, action='single_alias', help='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) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.6052804 python-manilaclient-4.8.0/playbooks/0000775000175000017500000000000000000000000017530 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/playbooks/enable-fips.yaml0000664000175000017500000000005000000000000022574 0ustar00zuulzuul00000000000000- hosts: all roles: - enable-fips ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.6052804 python-manilaclient-4.8.0/playbooks/python-manilaclient-functional/0000775000175000017500000000000000000000000025647 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/playbooks/python-manilaclient-functional/post.yaml0000664000175000017500000000015400000000000027520 0ustar00zuulzuul00000000000000- hosts: all vars: tox_envlist: functional roles: - fetch-tox-output - fetch-subunit-output ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/playbooks/python-manilaclient-functional/run.yaml0000664000175000017500000000045600000000000027344 0ustar00zuulzuul00000000000000- hosts: all roles: - ensure-python - run-devstack # Run bindep and test-setup after devstack so that they won't interfere - role: bindep bindep_profile: test bindep_dir: "{{ zuul_work_dir }}" - test-setup - populate-manilaclient-config - ensure-tox - tox ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.6052804 python-manilaclient-4.8.0/python_manilaclient.egg-info/0000775000175000017500000000000000000000000023260 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286955.0 python-manilaclient-4.8.0/python_manilaclient.egg-info/PKG-INFO0000664000175000017500000001351100000000000024356 0ustar00zuulzuul00000000000000Metadata-Version: 1.2 Name: python-manilaclient Version: 4.8.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 :: Only Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Requires-Python: >=3.8 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286955.0 python-manilaclient-4.8.0/python_manilaclient.egg-info/SOURCES.txt0000664000175000017500000005314100000000000025150 0ustar00zuulzuul00000000000000.stestr.conf AUTHORS CONTRIBUTING.rst ChangeLog HACKING LICENSE README.rst bindep.txt requirements.txt run_tests.sh setup.cfg setup.py test-requirements.txt tox.ini doc/.gitignore doc/Makefile doc/requirements.txt doc/source/conf.py doc/source/index.rst doc/source/cli/decoder.rst doc/source/cli/osc_plugin_cli.rst doc/source/cli/osc/manila.csv doc/source/cli/osc/v2/index.rst doc/source/contributor/contributing.rst 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/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/availability_zones.py manilaclient/osc/v2/messages.py manilaclient/osc/v2/quotas.py manilaclient/osc/v2/resource_locks.py manilaclient/osc/v2/security_services.py manilaclient/osc/v2/services.py manilaclient/osc/v2/share.py manilaclient/osc/v2/share_access_rules.py manilaclient/osc/v2/share_backups.py manilaclient/osc/v2/share_group_snapshots.py manilaclient/osc/v2/share_group_type_access.py manilaclient/osc/v2/share_group_types.py manilaclient/osc/v2/share_groups.py manilaclient/osc/v2/share_instance_export_locations.py manilaclient/osc/v2/share_instances.py manilaclient/osc/v2/share_limits.py manilaclient/osc/v2/share_network_subnets.py manilaclient/osc/v2/share_networks.py manilaclient/osc/v2/share_pools.py manilaclient/osc/v2/share_replica_export_locations.py manilaclient/osc/v2/share_replicas.py manilaclient/osc/v2/share_servers.py manilaclient/osc/v2/share_snapshot_instance_export_locations.py manilaclient/osc/v2/share_snapshot_instances.py manilaclient/osc/v2/share_snapshots.py manilaclient/osc/v2/share_transfers.py manilaclient/osc/v2/share_type_access.py manilaclient/osc/v2/share_types.py manilaclient/osc/v2/data/manila.csv 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_replicas.py manilaclient/tests/functional/test_share_servers.py manilaclient/tests/functional/test_share_transfers.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_availability_zones.py manilaclient/tests/functional/osc/test_messages.py manilaclient/tests/functional/osc/test_resource_locks.py manilaclient/tests/functional/osc/test_share_access_rules.py manilaclient/tests/functional/osc/test_share_backups.py manilaclient/tests/functional/osc/test_share_group_type_access.py manilaclient/tests/functional/osc/test_share_limits.py manilaclient/tests/functional/osc/test_share_network_subnets.py manilaclient/tests/functional/osc/test_share_networks.py manilaclient/tests/functional/osc/test_share_pools.py manilaclient/tests/functional/osc/test_share_replica_export_locations.py manilaclient/tests/functional/osc/test_share_replicas.py manilaclient/tests/functional/osc/test_share_services.py manilaclient/tests/functional/osc/test_share_snapshot_instances.py manilaclient/tests/functional/osc/test_share_snapshots.py manilaclient/tests/functional/osc/test_share_transfers.py manilaclient/tests/functional/osc/test_share_types.py manilaclient/tests/functional/osc/test_shares.py manilaclient/tests/functional/osc/test_shares_group_type.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/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_availability_zones.py manilaclient/tests/unit/osc/v2/test_messages.py manilaclient/tests/unit/osc/v2/test_quotas.py manilaclient/tests/unit/osc/v2/test_resource_locks.py manilaclient/tests/unit/osc/v2/test_security_services.py manilaclient/tests/unit/osc/v2/test_services.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_backups.py manilaclient/tests/unit/osc/v2/test_share_group_snapshots.py manilaclient/tests/unit/osc/v2/test_share_group_type.py manilaclient/tests/unit/osc/v2/test_share_group_type_access.py manilaclient/tests/unit/osc/v2/test_share_groups.py manilaclient/tests/unit/osc/v2/test_share_instance_export_locations.py manilaclient/tests/unit/osc/v2/test_share_instances.py manilaclient/tests/unit/osc/v2/test_share_limits.py manilaclient/tests/unit/osc/v2/test_share_network_subnets.py manilaclient/tests/unit/osc/v2/test_share_networks.py manilaclient/tests/unit/osc/v2/test_share_pools.py manilaclient/tests/unit/osc/v2/test_share_replica_export_locations.py manilaclient/tests/unit/osc/v2/test_share_replicas.py manilaclient/tests/unit/osc/v2/test_share_servers.py manilaclient/tests/unit/osc/v2/test_share_snapshot_instance_export_locations.py manilaclient/tests/unit/osc/v2/test_share_snapshot_instances.py manilaclient/tests/unit/osc/v2/test_share_snapshots.py manilaclient/tests/unit/osc/v2/test_share_transfers.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_backups.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_share_transfers.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/resource_locks.py manilaclient/v2/scheduler_stats.py manilaclient/v2/security_services.py manilaclient/v2/services.py manilaclient/v2/share_access_rules.py manilaclient/v2/share_backups.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_transfers.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/enable-fips.yaml playbooks/python-manilaclient-functional/post.yaml playbooks/python-manilaclient-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/Bug-1990013-fix-share-grou-from-snapshot-create-fa3629cf1417ba20.yaml releasenotes/notes/add-access-visibility-and-deletion-locks-69978f052e25334c.yaml releasenotes/notes/add-count-info-in-share-21a6b36c0f4c87b2.yaml releasenotes/notes/add-count-info-in-snapshot-c9600adad648a486.yaml releasenotes/notes/add-dash-dash-help-subcomand-ad4226454aa07bc6.yaml releasenotes/notes/add-defaultadsite-to-security-service-33fd0a5d1b865b11.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-per-share-gigabytes-quotas-40bc404bd3cbdd89.yaml releasenotes/notes/add-query-params-pools-list-api-12cf1s14ddf40kdd.yaml releasenotes/notes/add-quiesce-wait-time-for-replica-promote-30d9fa66afc854f2.yaml releasenotes/notes/add-quota-per-share-type-support-3b2708ea232e69bc.yaml releasenotes/notes/add-scheduler-hints-to-share-create-70d429cb0aaf8f11.yaml releasenotes/notes/add-scheduler-hints-to-share-replica-create-d152a3934e5ad3a7.yaml releasenotes/notes/add-share-group-quotas-support-b6563cec58209a1d.yaml releasenotes/notes/add-share-group-support-a3166f6ca4d06a81.yaml releasenotes/notes/add-share-network-sec-service-add-update-to-in-use-networks-ec7a60d07ebceaf4.yaml releasenotes/notes/add-share-replicas-and-replica-gigabytes-quotas-909436c2b2420f2c.yaml releasenotes/notes/add-snapshot-instances-admin-api-3cf3114doe40k598.yaml releasenotes/notes/add-snapshot-metadata-49b0288bf2f12bf0.yaml releasenotes/notes/add-subnet-metadata-82426986431b0179.yaml releasenotes/notes/add-support-filter-search-for-share-type-fdbaaa9510cc59dd.yaml releasenotes/notes/add-support-to-check-quota-usage-scfdg14dod40k71a.yaml releasenotes/notes/add_support_multiple_subnet_per_az-46145c3e90e097be.yaml releasenotes/notes/bp-allow-locking-shares-against-deletion-89e51e27368cda46.yaml releasenotes/notes/bp-export-locations-replica-az-commands-03aa32c08f59c42d.yaml releasenotes/notes/bp-integrate-os-profiler-4818066dc2fbf7b7.yaml releasenotes/notes/bp-ocata-migration-improvements-f63c5d233856fbee.yaml releasenotes/notes/bp-support-group-spec-search-share-group-type-api-d5d9a6096f084b91.yaml releasenotes/notes/bp-support-query-user-messages-by-timestamp-34b70bba2d1b4d13.yaml releasenotes/notes/bp-support-share-transfer-between-project-faefead551380eca.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-1798229-fix-create-share-from-snap-using-name-44100b907ea6a040.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-1855391-support-force-extend-share-6b5ebcfe1de0ca7b.yaml releasenotes/notes/bug-1871252-update-default-quotas-via-quota-class-share-groups-share-group-snapshots-20ec1dfcc0a7e81c.yaml releasenotes/notes/bug-1898304-add-wait-to-share-create-delete-f121073f2f4402ff.yaml releasenotes/notes/bug-1898308-add-wait-to-share-extend-shrink-c9cc413c50d9832a.yaml releasenotes/notes/bug-1898309-add-wait-to-share-manage-unmanage-d2060c61cc295bfd.yaml releasenotes/notes/bug-1898315-add-wait-flag-to-manage-share-server-operation-be6488c2a57536e1.yaml releasenotes/notes/bug-1898316-add-wait-option-for-deleting-a-share-server-e2228018585de5cb.yaml releasenotes/notes/bug-1898317-add-wait-option-for-force-deleting-share-snapshot-share-instance-fb2531b6033f0ae5.yaml releasenotes/notes/bug-1898318-add-wait-flag-for-deleting-a-share-group-operation-c602ba9faad411be.yaml releasenotes/notes/bug-1898318-add-wait-flag-to-create-sharee-group-operation-cd8310b241d377b0.yaml releasenotes/notes/bug-1899325-implement-usage-of-c-or-column-without-additional-logic-2970ee294f32bd31.yaml releasenotes/notes/bug-1902873-fix-py-raw-error-msg-a839fee2ac7b9d3d.yaml releasenotes/notes/bug-1909477-fix-forbid-users-to-create-shares-with-the-name-none-cfb0a59baa597803.yaml releasenotes/notes/bug-1920888-fix-error-in-cli-manila-list-with-sorting-key-availabilityzone-cea5a1f5d8a38fc3.yaml releasenotes/notes/bug-1925486-add-share-network-option-to-replica-create-api-7d2ff3628e93fc77.yaml releasenotes/notes/bug-1953670-fix-id-attr-for-share-group-type-access-repr-008338a53d7a6a50.yaml releasenotes/notes/bug-1959329-fix-server-listing-by-subnets-fa6447fd43093cae.yaml releasenotes/notes/bug-1960422-fix-no-default-share-type-d6191ea0aa1e57fa.yaml releasenotes/notes/bug-1960490-use-suitable-version-for-osc-b375a32273b56522.yaml releasenotes/notes/bug-1962288-fixed-share-network-create-command-879dc3deca131ef9.yaml releasenotes/notes/bug-1975488-skip-force-kwarg-if-unspecified-f98c717df1d6e364.yaml releasenotes/notes/bug-1980985-dont-use-share-type-with-snapshot-ref-de0331c640afbbd3.yaml releasenotes/notes/bug-1999775-add-os-key-0cfc95c7b480df05.yaml releasenotes/notes/bug-2030686-fix-default-share-lookup-cc7e592a0dc855e1.yaml releasenotes/notes/bug-2047249-fix-osc-quota-set-per-share-gigabytes-fcff7f8ce2cc3c75.yaml releasenotes/notes/bug-2051737-fix-share-force-delete-request-6d2578fb7da61e3f.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-manila-shell-0061cbcab5d3d75b.yaml releasenotes/notes/deprecate-v1-a0cfa6fd723c2f46.yaml releasenotes/notes/drop-py36-and-py37-85cf389b2842f045.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/graduate-share-replication-feature-49770e921b4338fb.yaml releasenotes/notes/handle-missing-api-minor-version-5a6d242f28883442.yaml releasenotes/notes/manage-unmanage-share-servers-8c7b27a1fe80e5fa.yaml releasenotes/notes/manila-client-support-recycle-bin-4ecb5de770bd525f.yaml releasenotes/notes/manila-openstackclient-bf61ceb270d3afb7.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-backup-98e11c6a28897e94.yaml releasenotes/notes/share-network-multiple-subnets-732309abfbf5987c.yaml releasenotes/notes/share-revert-to-snapshot-e899a4b7e1126749.yaml releasenotes/notes/share_type-skip-format-119595e62900e571.yaml releasenotes/notes/start-using-reno-b744cd0259c7a88c.yaml releasenotes/notes/support-create-share-from-snapshot-extra-spec-cdba92f179c1c5c6.yaml releasenotes/notes/support-share-server-migration-9804752270c6b153.yaml releasenotes/notes/support-show-type-6380b7c539c95ba8.yaml releasenotes/notes/support_add_disabled_reason_to_services-08cb52e4711745c7.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/notes/v2-0-0-deprecated-opts-removal-863565618535733d.yaml releasenotes/source/2023.1.rst releasenotes/source/2023.2.rst 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/ussuri.rst releasenotes/source/victoria.rst releasenotes/source/wallaby.rst releasenotes/source/xena.rst releasenotes/source/yoga.rst releasenotes/source/zed.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder roles/populate-manilaclient-config/README.rst roles/populate-manilaclient-config/defaults/main.yaml roles/populate-manilaclient-config/tasks/main.yaml tools/manila.bash_completion zuul.d/project.yaml zuul.d/python-manilaclient-jobs.yaml././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286955.0 python-manilaclient-4.8.0/python_manilaclient.egg-info/dependency_links.txt0000664000175000017500000000000100000000000027326 0ustar00zuulzuul00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286955.0 python-manilaclient-4.8.0/python_manilaclient.egg-info/entry_points.txt0000664000175000017500000002655200000000000026570 0ustar00zuulzuul00000000000000[console_scripts] manila = manilaclient.shell:main [openstack.cli.extension] share = manilaclient.osc.plugin [openstack.share.v2] share_abandon = manilaclient.osc.v2.share:AbandonShare 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_adopt = manilaclient.osc.v2.share:AdoptShare share_availability_zone_list = manilaclient.osc.v2.availability_zones:ShareAvailabilityZoneList share_backup_create = manilaclient.osc.v2.share_backups:CreateShareBackup share_backup_delete = manilaclient.osc.v2.share_backups:DeleteShareBackup share_backup_list = manilaclient.osc.v2.share_backups:ListShareBackup share_backup_restore = manilaclient.osc.v2.share_backups:RestoreShareBackup share_backup_set = manilaclient.osc.v2.share_backups:SetShareBackup share_backup_show = manilaclient.osc.v2.share_backups:ShowShareBackup share_backup_unset = manilaclient.osc.v2.share_backups:UnsetShareBackup share_create = manilaclient.osc.v2.share:CreateShare share_delete = manilaclient.osc.v2.share:DeleteShare share_export_location_list = manilaclient.osc.v2.share:ShareExportLocationList share_export_location_show = manilaclient.osc.v2.share:ShareExportLocationShow share_group_create = manilaclient.osc.v2.share_groups:CreateShareGroup share_group_delete = manilaclient.osc.v2.share_groups:DeleteShareGroup share_group_list = manilaclient.osc.v2.share_groups:ListShareGroup share_group_set = manilaclient.osc.v2.share_groups:SetShareGroup share_group_show = manilaclient.osc.v2.share_groups:ShowShareGroup share_group_snapshot_create = manilaclient.osc.v2.share_group_snapshots:CreateShareGroupSnapshot share_group_snapshot_delete = manilaclient.osc.v2.share_group_snapshots:DeleteShareGroupSnapshot share_group_snapshot_list = manilaclient.osc.v2.share_group_snapshots:ListShareGroupSnapshot share_group_snapshot_members_list = manilaclient.osc.v2.share_group_snapshots:ListShareGroupSnapshotMembers share_group_snapshot_set = manilaclient.osc.v2.share_group_snapshots:SetShareGroupSnapshot share_group_snapshot_show = manilaclient.osc.v2.share_group_snapshots:ShowShareGroupSnapshot share_group_snapshot_unset = manilaclient.osc.v2.share_group_snapshots:UnsetShareGroupSnapshot share_group_type_access_create = manilaclient.osc.v2.share_group_type_access:ShareGroupTypeAccessAllow share_group_type_access_delete = manilaclient.osc.v2.share_group_type_access:ShareGroupTypeAccessDeny share_group_type_access_list = manilaclient.osc.v2.share_group_type_access:ListShareGroupTypeAccess share_group_type_create = manilaclient.osc.v2.share_group_types:CreateShareGroupType share_group_type_delete = manilaclient.osc.v2.share_group_types:DeleteShareGroupType share_group_type_list = manilaclient.osc.v2.share_group_types:ListShareGroupType share_group_type_set = manilaclient.osc.v2.share_group_types:SetShareGroupType share_group_type_show = manilaclient.osc.v2.share_group_types:ShowShareGroupType share_group_type_unset = manilaclient.osc.v2.share_group_types:UnsetShareGroupType share_group_unset = manilaclient.osc.v2.share_groups:UnsetShareGroup share_instance_delete = manilaclient.osc.v2.share_instances:ShareInstanceDelete share_instance_export_location_list = manilaclient.osc.v2.share_instance_export_locations:ShareInstanceListExportLocation share_instance_export_location_show = manilaclient.osc.v2.share_instance_export_locations:ShareInstanceShowExportLocation share_instance_list = manilaclient.osc.v2.share_instances:ShareInstanceList share_instance_set = manilaclient.osc.v2.share_instances:ShareInstanceSet share_instance_show = manilaclient.osc.v2.share_instances:ShareInstanceShow share_limits_show = manilaclient.osc.v2.share_limits:ShareLimitsShow share_list = manilaclient.osc.v2.share:ListShare share_lock_create = manilaclient.osc.v2.resource_locks:CreateResourceLock share_lock_delete = manilaclient.osc.v2.resource_locks:DeleteResourceLock share_lock_list = manilaclient.osc.v2.resource_locks:ListResourceLock share_lock_set = manilaclient.osc.v2.resource_locks:SetResourceLock share_lock_show = manilaclient.osc.v2.resource_locks:ShowResourceLock share_lock_unset = manilaclient.osc.v2.resource_locks:UnsetResourceLock share_message_delete = manilaclient.osc.v2.messages:DeleteMessage share_message_list = manilaclient.osc.v2.messages:ListMessage share_message_show = manilaclient.osc.v2.messages:ShowMessage share_migration_cancel = manilaclient.osc.v2.share:ShareMigrationCancel share_migration_complete = manilaclient.osc.v2.share:ShareMigrationComplete share_migration_show = manilaclient.osc.v2.share:ShareMigrationShow share_migration_start = manilaclient.osc.v2.share:ShareMigrationStart share_network_create = manilaclient.osc.v2.share_networks:CreateShareNetwork share_network_delete = manilaclient.osc.v2.share_networks:DeleteShareNetwork share_network_list = manilaclient.osc.v2.share_networks:ListShareNetwork share_network_set = manilaclient.osc.v2.share_networks:SetShareNetwork share_network_show = manilaclient.osc.v2.share_networks:ShowShareNetwork share_network_subnet_create = manilaclient.osc.v2.share_network_subnets:CreateShareNetworkSubnet share_network_subnet_delete = manilaclient.osc.v2.share_network_subnets:DeleteShareNetworkSubnet share_network_subnet_set = manilaclient.osc.v2.share_network_subnets:SetShareNetworkSubnet share_network_subnet_show = manilaclient.osc.v2.share_network_subnets:ShowShareNetworkSubnet share_network_subnet_unset = manilaclient.osc.v2.share_network_subnets:UnsetShareNetworkSubnet share_network_unset = manilaclient.osc.v2.share_networks:UnsetShareNetwork share_pool_list = manilaclient.osc.v2.share_pools:ListSharePools share_properties_show = manilaclient.osc.v2.share:ShowShareProperties share_quota_delete = manilaclient.osc.v2.quotas:QuotaDelete share_quota_set = manilaclient.osc.v2.quotas:QuotaSet share_quota_show = manilaclient.osc.v2.quotas:QuotaShow share_replica_create = manilaclient.osc.v2.share_replicas:CreateShareReplica share_replica_delete = manilaclient.osc.v2.share_replicas:DeleteShareReplica share_replica_export_location_list = manilaclient.osc.v2.share_replica_export_locations:ShareReplicaListExportLocation share_replica_export_location_show = manilaclient.osc.v2.share_replica_export_locations:ShareReplicaShowExportLocation share_replica_list = manilaclient.osc.v2.share_replicas:ListShareReplica share_replica_promote = manilaclient.osc.v2.share_replicas:PromoteShareReplica share_replica_resync = manilaclient.osc.v2.share_replicas:ResyncShareReplica share_replica_set = manilaclient.osc.v2.share_replicas:SetShareReplica share_replica_show = manilaclient.osc.v2.share_replicas:ShowShareReplica share_resize = manilaclient.osc.v2.share:ResizeShare share_restore = manilaclient.osc.v2.share:RestoreShare share_revert = manilaclient.osc.v2.share:RevertShare share_security_service_create = manilaclient.osc.v2.security_services:CreateShareSecurityService share_security_service_delete = manilaclient.osc.v2.security_services:DeleteShareSecurityService share_security_service_list = manilaclient.osc.v2.security_services:ListShareSecurityService share_security_service_set = manilaclient.osc.v2.security_services:SetShareSecurityService share_security_service_show = manilaclient.osc.v2.security_services:ShowShareSecurityService share_security_service_unset = manilaclient.osc.v2.security_services:UnsetShareSecurityService share_server_abandon = manilaclient.osc.v2.share_servers:AbandonShareServer share_server_adopt = manilaclient.osc.v2.share_servers:AdoptShareServer share_server_delete = manilaclient.osc.v2.share_servers:DeleteShareServer share_server_list = manilaclient.osc.v2.share_servers:ListShareServer share_server_migration_cancel = manilaclient.osc.v2.share_servers:ShareServerMigrationCancel share_server_migration_complete = manilaclient.osc.v2.share_servers:ShareServerMigrationComplete share_server_migration_show = manilaclient.osc.v2.share_servers:ShareServerMigrationShow share_server_migration_start = manilaclient.osc.v2.share_servers:ShareServerMigrationStart share_server_set = manilaclient.osc.v2.share_servers:SetShareServer share_server_show = manilaclient.osc.v2.share_servers:ShowShareServer share_service_list = manilaclient.osc.v2.services:ListShareService share_service_set = manilaclient.osc.v2.services:SetShareService share_set = manilaclient.osc.v2.share:SetShare share_show = manilaclient.osc.v2.share:ShowShare share_snapshot_abandon = manilaclient.osc.v2.share_snapshots:AbandonShareSnapshot share_snapshot_access_create = manilaclient.osc.v2.share_snapshots:ShareSnapshotAccessAllow share_snapshot_access_delete = manilaclient.osc.v2.share_snapshots:ShareSnapshotAccessDeny share_snapshot_access_list = manilaclient.osc.v2.share_snapshots:ShareSnapshotAccessList share_snapshot_adopt = manilaclient.osc.v2.share_snapshots:AdoptShareSnapshot share_snapshot_create = manilaclient.osc.v2.share_snapshots:CreateShareSnapshot share_snapshot_delete = manilaclient.osc.v2.share_snapshots:DeleteShareSnapshot share_snapshot_export_location_list = manilaclient.osc.v2.share_snapshots:ShareSnapshotListExportLocation share_snapshot_export_location_show = manilaclient.osc.v2.share_snapshots:ShareSnapshotShowExportLocation share_snapshot_instance_export_location_list = manilaclient.osc.v2.share_snapshot_instance_export_locations:ShareSnapshotInstanceExportLocationList share_snapshot_instance_export_location_show = manilaclient.osc.v2.share_snapshot_instance_export_locations:ShareSnapshotInstanceExportLocationShow share_snapshot_instance_list = manilaclient.osc.v2.share_snapshot_instances:ListShareSnapshotInstance share_snapshot_instance_set = manilaclient.osc.v2.share_snapshot_instances:SetShareSnapshotInstance share_snapshot_instance_show = manilaclient.osc.v2.share_snapshot_instances:ShowShareSnapshotInstance share_snapshot_list = manilaclient.osc.v2.share_snapshots:ListShareSnapshot share_snapshot_set = manilaclient.osc.v2.share_snapshots:SetShareSnapshot share_snapshot_show = manilaclient.osc.v2.share_snapshots:ShowShareSnapshot share_snapshot_unset = manilaclient.osc.v2.share_snapshots:UnsetShareSnapshot share_transfer_accept = manilaclient.osc.v2.share_transfers:AcceptShareTransfer share_transfer_create = manilaclient.osc.v2.share_transfers:CreateShareTransfer share_transfer_delete = manilaclient.osc.v2.share_transfers:DeleteShareTransfer share_transfer_list = manilaclient.osc.v2.share_transfers:ListShareTransfer share_transfer_show = manilaclient.osc.v2.share_transfers:ShowShareTransfer 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286955.0 python-manilaclient-4.8.0/python_manilaclient.egg-info/not-zip-safe0000664000175000017500000000000100000000000025506 0ustar00zuulzuul00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286955.0 python-manilaclient-4.8.0/python_manilaclient.egg-info/pbr.json0000664000175000017500000000005600000000000024737 0ustar00zuulzuul00000000000000{"git_version": "06e96b7", "is_release": true}././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286955.0 python-manilaclient-4.8.0/python_manilaclient.egg-info/requires.txt0000664000175000017500000000037300000000000025663 0ustar00zuulzuul00000000000000Babel!=2.4.0,>=2.3.4 PrettyTable>=0.7.1 debtcollector>=1.2.0 osc-lib>=1.10.0 oslo.config>=5.2.0 oslo.log>=3.36.0 oslo.serialization!=2.19.1,>=2.18.0 oslo.utils>=3.33.0 pbr!=2.1.0,>=2.0.0 python-keystoneclient>=3.8.0 requests>=2.14.2 simplejson>=3.5.1 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286955.0 python-manilaclient-4.8.0/python_manilaclient.egg-info/top_level.txt0000664000175000017500000000001500000000000026006 0ustar00zuulzuul00000000000000manilaclient ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.6052804 python-manilaclient-4.8.0/rally-jobs/0000775000175000017500000000000000000000000017603 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/rally-jobs/rally-manila-no-ss.yaml0000664000175000017500000000354200000000000024112 0ustar00zuulzuul00000000000000--- 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/rally-jobs/rally-manila.yaml0000664000175000017500000001022700000000000023053 0ustar00zuulzuul00000000000000--- 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5492654 python-manilaclient-4.8.0/releasenotes/0000775000175000017500000000000000000000000020216 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.629287 python-manilaclient-4.8.0/releasenotes/notes/0000775000175000017500000000000000000000000021346 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/.placeholder0000664000175000017500000000000000000000000023617 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000022200000000000011451 xustar0000000000000000124 path=python-manilaclient-4.8.0/releasenotes/notes/Bug-1990013-fix-share-grou-from-snapshot-create-fa3629cf1417ba20.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/Bug-1990013-fix-share-grou-from-snapshot-create-fa3629c0000664000175000017500000000020600000000000032600 0ustar00zuulzuul00000000000000--- fixes: - | Create Share group from snapshot failed due to a bug in OSC. The issue is fixed and its working as expected. ././@PaxHeader0000000000000000000000000000021300000000000011451 xustar0000000000000000117 path=python-manilaclient-4.8.0/releasenotes/notes/add-access-visibility-and-deletion-locks-69978f052e25334c.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-access-visibility-and-deletion-locks-69978f052e25330000664000175000017500000000055700000000000032711 0ustar00zuulzuul00000000000000--- features: - | It is now possible to restrict the visibility of access rules' sensitive fields, as well as lock the access rule deletion while allowing access to a share. A lock reason can also be provided. - | It is now possible to filter access rules while listing them by its `access_to`, `access_type`, `access_key` and `access_level`. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-count-info-in-share-21a6b36c0f4c87b2.yaml0000664000175000017500000000012300000000000030746 0ustar00zuulzuul00000000000000--- features: - Added ``with_count`` option in share's list commands since 2.42. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-count-info-in-snapshot-c9600adad648a486.yaml0000664000175000017500000000012700000000000031520 0ustar00zuulzuul00000000000000--- features: - Added ``count`` option in share snapshot's list commands since 2.79. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-dash-dash-help-subcomand-ad4226454aa07bc6.yaml0000664000175000017500000000013200000000000031710 0ustar00zuulzuul00000000000000--- fixes: - Allow --help to print subcomands info. e.g. ``$ manila create --help`` ././@PaxHeader0000000000000000000000000000021000000000000011446 xustar0000000000000000114 path=python-manilaclient-4.8.0/releasenotes/notes/add-defaultadsite-to-security-service-33fd0a5d1b865b11.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-defaultadsite-to-security-service-33fd0a5d1b865b11.0000664000175000017500000000037200000000000033041 0ustar00zuulzuul00000000000000--- features: - | Users are now able to set a default active directory site while creating security services by specifying 'default_ad_site'. Refer `Launchpad bug #1988146 `_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-export-location-filter-4cf3114doe40k598.yaml0000664000175000017500000000015600000000000031634 0ustar00zuulzuul00000000000000--- features: - Shares and share instances can now be filtered with their export location IDs or paths. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-ipv6-access-type-4dko90r1a9a1e0b8.yaml0000664000175000017500000000022300000000000030465 0ustar00zuulzuul00000000000000--- features: - IPv6 ACL support has been added in manila. access-allow command now validates and allows access control to IPv6 clients. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-like-filter-591572762357ef4b.yaml0000664000175000017500000000047300000000000027204 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-message-list-and-delete-41b3323edd63d894.yaml0000664000175000017500000000013200000000000031507 0ustar00zuulzuul00000000000000--- features: - Added new subcommands message-list, message-show and message-delete. ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=python-manilaclient-4.8.0/releasenotes/notes/add-metadata-for-share-access-rule-11a6b36c0f4c87c2.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-metadata-for-share-access-rule-11a6b36c0f4c87c2.yam0000664000175000017500000000034700000000000032665 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-per-share-gigabytes-quotas-40bc404bd3cbdd89.yaml0000664000175000017500000000030400000000000032476 0ustar00zuulzuul00000000000000--- features: - Added support for per share gigabytes quotas. upgrade: - After addition of per share gigabytes quotas, it is now possible to get 'over limit' error while creating a share. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-query-params-pools-list-api-12cf1s14ddf40kdd.yaml0000664000175000017500000000017600000000000032733 0ustar00zuulzuul00000000000000--- features: - Added ``--detail`` argument to pool list command. - Added ``--share_type`` argument to pool list command. ././@PaxHeader0000000000000000000000000000021400000000000011452 xustar0000000000000000118 path=python-manilaclient-4.8.0/releasenotes/notes/add-quiesce-wait-time-for-replica-promote-30d9fa66afc854f2.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-quiesce-wait-time-for-replica-promote-30d9fa66afc850000664000175000017500000000047700000000000033236 0ustar00zuulzuul00000000000000--- features: - | Added 'quiesce_wait_time' option to share replica promote API. This will be used by driver as quiesce wait time for specified replica promote operation instead of global config value. For more details, refer `Launchpad bug #2000171 `_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-quota-per-share-type-support-3b2708ea232e69bc.yaml0000664000175000017500000000007300000000000032677 0ustar00zuulzuul00000000000000--- features: - Added support for per-share-type quotas. ././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=python-manilaclient-4.8.0/releasenotes/notes/add-scheduler-hints-to-share-create-70d429cb0aaf8f11.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-scheduler-hints-to-share-create-70d429cb0aaf8f11.ya0000664000175000017500000000054500000000000033001 0ustar00zuulzuul00000000000000--- features: - Added --scheduler_hints to the share create command upgrade: - Scheduler hints in the share create command allow scheduler to select appropriate host using hard affinity and anti-affinity filters. User needs to specify affinity/anti-affinity share ids using keys "same_host" or "different_host" when creating a manila share. ././@PaxHeader0000000000000000000000000000021600000000000011454 xustar0000000000000000120 path=python-manilaclient-4.8.0/releasenotes/notes/add-scheduler-hints-to-share-replica-create-d152a3934e5ad3a7.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-scheduler-hints-to-share-replica-create-d152a3934e50000664000175000017500000000065700000000000033115 0ustar00zuulzuul00000000000000--- features: - Added --scheduler_hints to the share-replica create command of manila shellclient. In case of OSC, --scheduler-hint is used. Scheduler hints in the share-replica create allow scheduler to select appropriate host using filters. For example, user needs to specify "only_host=host@backend#pool" when creating a manila share-replica in case of onlyhostFilter. Supported for microversion >= 2.67. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-share-group-quotas-support-b6563cec58209a1d.yaml0000664000175000017500000000040300000000000032451 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-share-group-support-a3166f6ca4d06a81.yaml0000664000175000017500000000033000000000000031130 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000023600000000000011456 xustar0000000000000000136 path=python-manilaclient-4.8.0/releasenotes/notes/add-share-network-sec-service-add-update-to-in-use-networks-ec7a60d07ebceaf4.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-share-network-sec-service-add-update-to-in-use-netw0000664000175000017500000000140600000000000033616 0ustar00zuulzuul00000000000000--- features: - | Added support for updating and adding security services to in use share networks. The command ``share-network-security-service-update`` was added to the client. Before each of these commands is executed, make sure to run the correspondent check command, being either ``share-network-security-service-add-check`` or ``share-network-security-service-update-check``. These commands will check if the desired share network can have security services added or updated, based on the cloud support. Also, these commands can be used for both to request a check and to check the outcome. The command ``share-network-reset-state`` was also implemented in case there is need to update the share network ``status`` field. ././@PaxHeader0000000000000000000000000000022200000000000011451 xustar0000000000000000124 path=python-manilaclient-4.8.0/releasenotes/notes/add-share-replicas-and-replica-gigabytes-quotas-909436c2b2420f2c.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-share-replicas-and-replica-gigabytes-quotas-909436c0000664000175000017500000000051500000000000033304 0ustar00zuulzuul00000000000000--- 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.././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-snapshot-instances-admin-api-3cf3114doe40k598.yaml0000664000175000017500000000013200000000000032674 0ustar00zuulzuul00000000000000--- features: - Add list, show, and reset-status admin commands for snapshot instances. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-snapshot-metadata-49b0288bf2f12bf0.yaml0000664000175000017500000000024600000000000030610 0ustar00zuulzuul00000000000000features: - | Adds support to set snapshot property on snapshot create, filter list on snapshot property, and snapshot property set and unset. Only in OSC. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-subnet-metadata-82426986431b0179.yaml0000664000175000017500000000026500000000000027706 0ustar00zuulzuul00000000000000--- features: - | Adds support to create share network subnet with properties and update the subnet properties with set and unset command (only with the OpenStackClient). ././@PaxHeader0000000000000000000000000000021300000000000011451 xustar0000000000000000117 path=python-manilaclient-4.8.0/releasenotes/notes/add-support-filter-search-for-share-type-fdbaaa9510cc59dd.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-support-filter-search-for-share-type-fdbaaa9510cc590000664000175000017500000000011000000000000033313 0ustar00zuulzuul00000000000000--- features: - Share types can now be filtered with its extra_specs. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add-support-to-check-quota-usage-scfdg14dod40k71a.yaml0000664000175000017500000000011700000000000033167 0ustar00zuulzuul00000000000000--- features: - Add support to check quota usage by '--detail' in quota-show ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=python-manilaclient-4.8.0/releasenotes/notes/add_support_multiple_subnet_per_az-46145c3e90e097be.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/add_support_multiple_subnet_per_az-46145c3e90e097be.yam0000664000175000017500000000046600000000000033404 0ustar00zuulzuul00000000000000--- features: - The command `share-network-subnet-create-check` was added. This command will check if the share network subnet can be created in a specific share network based on the cloud support. The OSC equivalent command is also added, run the subnet create with the new option --check-only. ././@PaxHeader0000000000000000000000000000021300000000000011451 xustar0000000000000000117 path=python-manilaclient-4.8.0/releasenotes/notes/bp-allow-locking-shares-against-deletion-89e51e27368cda46.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bp-allow-locking-shares-against-deletion-89e51e27368cda0000664000175000017500000000022200000000000033136 0ustar00zuulzuul00000000000000--- features: - | Added SDK and OSC commands to create, view, update and delete resource locks, alongside support for API version 2.81. ././@PaxHeader0000000000000000000000000000021200000000000011450 xustar0000000000000000116 path=python-manilaclient-4.8.0/releasenotes/notes/bp-export-locations-replica-az-commands-03aa32c08f59c42d.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bp-export-locations-replica-az-commands-03aa32c08f59c420000664000175000017500000000030500000000000033063 0ustar00zuulzuul00000000000000--- 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.././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bp-integrate-os-profiler-4818066dc2fbf7b7.yaml0000664000175000017500000000015300000000000031276 0ustar00zuulzuul00000000000000--- features: - | OS profiler is now supported within the v2 client and the manilaclient shell. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bp-ocata-migration-improvements-f63c5d233856fbee.yaml0000664000175000017500000000052300000000000032742 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000022400000000000011453 xustar0000000000000000126 path=python-manilaclient-4.8.0/releasenotes/notes/bp-support-group-spec-search-share-group-type-api-d5d9a6096f084b91.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bp-support-group-spec-search-share-group-type-api-d5d9a0000664000175000017500000000013000000000000033607 0ustar00zuulzuul00000000000000--- features: - | Share group types can now be filtered with their `group_specs`. ././@PaxHeader0000000000000000000000000000021600000000000011454 xustar0000000000000000120 path=python-manilaclient-4.8.0/releasenotes/notes/bp-support-query-user-messages-by-timestamp-34b70bba2d1b4d13.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bp-support-query-user-messages-by-timestamp-34b70bba2d10000664000175000017500000000023300000000000033450 0ustar00zuulzuul00000000000000--- features: - Added ``since`` and ``before`` to messages list API. User messages can be queried by stimestamp with API version ``2.52`` and beyond.././@PaxHeader0000000000000000000000000000021400000000000011452 xustar0000000000000000118 path=python-manilaclient-4.8.0/releasenotes/notes/bp-support-share-transfer-between-project-faefead551380eca.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bp-support-share-transfer-between-project-faefead5513800000664000175000017500000000014200000000000033455 0ustar00zuulzuul00000000000000--- features: - Support transferring shares between projects starting from API version ``2.77``.././@PaxHeader0000000000000000000000000000021300000000000011451 xustar0000000000000000117 path=python-manilaclient-4.8.0/releasenotes/notes/bp-update-share-type-name-or-description-32d98b5a42cd8090.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bp-update-share-type-name-or-description-32d98b5a42cd800000664000175000017500000000026400000000000033074 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000021200000000000011450 xustar0000000000000000116 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1558995-fix-endpoint-not-found.yaml-a06a3b0ba5006718.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1558995-fix-endpoint-not-found.yaml-a06a3b0ba5006710000664000175000017500000000014100000000000032203 0ustar00zuulzuul00000000000000--- fixes: - | Fix endpoint not found error when only the manilav2 endpoint is configured. ././@PaxHeader0000000000000000000000000000022500000000000011454 xustar0000000000000000127 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1608664-add-support-columns-share-replica-list-885bd8c8b4bfa8f1.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1608664-add-support-columns-share-replica-list-885b0000664000175000017500000000011100000000000032763 0ustar00zuulzuul00000000000000--- fixes: - Added "--columns" support for share-replica-list command. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1611506-allow-deletion-multiple-resources0000664000175000017500000000034700000000000031451 0ustar00zuulzuul00000000000000--- 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.././@PaxHeader0000000000000000000000000000021200000000000011450 xustar0000000000000000116 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1622540-add-missing-az-list-command-f90265de5c2f261b.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1622540-add-missing-az-list-command-f90265de5c2f2610000664000175000017500000000014000000000000032127 0ustar00zuulzuul00000000000000--- fixes: - Added support for missing command to list manila service availability zones. ././@PaxHeader0000000000000000000000000000021100000000000011447 xustar0000000000000000115 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1650774-share-instance-reset-state-9c4b26f44b5da0e9.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1650774-share-instance-reset-state-9c4b26f44b5da0e90000664000175000017500000000026300000000000032341 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1664877-c462bfad92ce03e5.yaml0000664000175000017500000000057400000000000026232 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1674915-allow-user-access-fix-495b3e42bdc985ec.yaml0000664000175000017500000000024300000000000032265 0ustar00zuulzuul00000000000000--- fixes: - Changed user access name limit from 32 to 255 characters since there are security services that allow user names longer than 32 characters. ././@PaxHeader0000000000000000000000000000023000000000000011450 xustar0000000000000000130 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1682726-remove-sort-key-export-location-from-list-885des26bd5ea2de.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1682726-remove-sort-key-export-location-from-list-80000664000175000017500000000010400000000000033150 0ustar00zuulzuul00000000000000--- fixes: - Remove unused sort key export_location from list CLI.././@PaxHeader0000000000000000000000000000023200000000000011452 xustar0000000000000000132 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1707303-fix-list-command-when-not-given-search-opts-c06af7b344e9cb91.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1707303-fix-list-command-when-not-given-search-opts0000664000175000017500000000023500000000000033132 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000024100000000000011452 xustar0000000000000000139 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1709746-add-group-specs-in-share-group-type-create-command-f91265de5c2f251b.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1709746-add-group-specs-in-share-group-type-create-0000664000175000017500000000010100000000000033023 0ustar00zuulzuul00000000000000--- fixes: - Added group specs in share group type create CLI. ././@PaxHeader0000000000000000000000000000024000000000000011451 xustar0000000000000000138 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1717940-dash-dash-column-reports-nothing-for-capabilities-db8c1234fae91af5.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1717940-dash-dash-column-reports-nothing-for-capabi0000664000175000017500000000017000000000000033164 0ustar00zuulzuul00000000000000--- fixes: - Fix Manila pool-list --column reports nothing for capabilities, And also format the detail contents. ././@PaxHeader0000000000000000000000000000021100000000000011447 xustar0000000000000000115 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1724183-add-share-type-description-8c4b26f44b5da1e9.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1724183-add-share-type-description-8c4b26f44b5da1e90000664000175000017500000000007700000000000032326 0ustar00zuulzuul00000000000000--- fixes: - Added description in list/create share type CLI.././@PaxHeader0000000000000000000000000000023200000000000011452 xustar0000000000000000132 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1733494-allow-user-group-name-with-blank-access-fix-885a3e42bdc985ec.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1733494-allow-user-group-name-with-blank-access-fix0000664000175000017500000000016300000000000033123 0ustar00zuulzuul00000000000000--- fixes: - Allows the use of blank in user group name since the AD allow user group name to include blank. ././@PaxHeader0000000000000000000000000000023600000000000011456 xustar0000000000000000136 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1738917-and-1738918-fix-access-share-group-type-by-name-3a8760522c147f28.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1738917-and-1738918-fix-access-share-group-type-by-0000775000175000017500000000016600000000000032240 0ustar00zuulzuul00000000000000--- fixes: - Fixed bugs 1738917 and 1738918. Names can now be used in commands pertaining to share group types. ././@PaxHeader0000000000000000000000000000022400000000000011453 xustar0000000000000000126 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1798229-fix-create-share-from-snap-using-name-44100b907ea6a040.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1798229-fix-create-share-from-snap-using-name-441000000664000175000017500000000040300000000000032442 0ustar00zuulzuul00000000000000--- fixes: - | Fixed the issue in which users were unable to create a share from a snapshot, specifing the snapshot's name. For more details, please refer to `launchpad bug 1798229 `_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1799934-add-ou-parameter-260f9aaf939d1919.yaml0000664000175000017500000000012300000000000031150 0ustar00zuulzuul00000000000000--- features: - organizational unit (--ou) parameter support added in manila cli ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1802059-fix-is_default-a8d3d95ffa0aede9.yaml0000664000175000017500000000014600000000000031263 0ustar00zuulzuul00000000000000--- fixes: - Fix share_group_type_create returning is_default as a function object instead of value ././@PaxHeader0000000000000000000000000000021000000000000011446 xustar0000000000000000114 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1811516-bug-1811627-python3-fixes-4b26130027b2c076.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1811516-bug-1811627-python3-fixes-4b26130027b2c076.0000664000175000017500000000015600000000000031046 0ustar00zuulzuul00000000000000--- fixes: - | The shell utility has been fixed to report errors correctly on python3 environments. ././@PaxHeader0000000000000000000000000000020700000000000011454 xustar0000000000000000113 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1814094-fix-_get_base_url-method-168e12292aeec8f1.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1814094-fix-_get_base_url-method-168e12292aeec8f1.y0000664000175000017500000000035300000000000032216 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1830677-fix-13b30d6a89f43246.yaml0000664000175000017500000000032300000000000026501 0ustar00zuulzuul00000000000000--- fixes: - | `Launchpad bug 1830677 `_ has been fixed and the client now correctly get description of share type instead of ``None``. ././@PaxHeader0000000000000000000000000000021100000000000011447 xustar0000000000000000115 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1855391-support-force-extend-share-6b5ebcfe1de0ca7b.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1855391-support-force-extend-share-6b5ebcfe1de0ca7b0000664000175000017500000000027500000000000032670 0ustar00zuulzuul00000000000000--- fixes: - | `Launchpad bug 1855391 `_ has been fixed by adding a "force" argument to the share extension commands. ././@PaxHeader0000000000000000000000000000026700000000000011462 xustar0000000000000000161 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1871252-update-default-quotas-via-quota-class-share-groups-share-group-snapshots-20ec1dfcc0a7e81c.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1871252-update-default-quotas-via-quota-class-share0000664000175000017500000000061300000000000033220 0ustar00zuulzuul00000000000000--- fixes: - | Share group and share group snapshots quotas have been available in manila since API 2.40. With this change, we added the possibility for users to update share group and share group snapshots quotas via the quota class API to the manilaclient CLI. For more details, please refer to `Launchpad bug 1871252 `_. ././@PaxHeader0000000000000000000000000000021600000000000011454 xustar0000000000000000120 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1898304-add-wait-to-share-create-delete-f121073f2f4402ff.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1898304-add-wait-to-share-create-delete-f121073f2f40000664000175000017500000000031200000000000032174 0ustar00zuulzuul00000000000000--- features: - | The commands "manila create" and "manila delete" now accept an optional "--wait" option that allows users to let the client poll for the completion of the operation. ././@PaxHeader0000000000000000000000000000021600000000000011454 xustar0000000000000000120 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1898308-add-wait-to-share-extend-shrink-c9cc413c50d9832a.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1898308-add-wait-to-share-extend-shrink-c9cc413c50d0000664000175000017500000000031400000000000032422 0ustar00zuulzuul00000000000000--- features: - | The commands "manila extend" and "manila shrink" now accept an optional "--wait" option that allows users to let the client poll for the completion of the operation. ././@PaxHeader0000000000000000000000000000022000000000000011447 xustar0000000000000000122 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1898309-add-wait-to-share-manage-unmanage-d2060c61cc295bfd.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1898309-add-wait-to-share-manage-unmanage-d2060c61c0000664000175000017500000000031200000000000032342 0ustar00zuulzuul00000000000000--- features: - | The commands "manila manage" and "manila unmanage" now accept an optional "--wait" flag that allows users to let the client poll for the completion of the operation. ././@PaxHeader0000000000000000000000000000023500000000000011455 xustar0000000000000000135 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1898315-add-wait-flag-to-manage-share-server-operation-be6488c2a57536e1.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1898315-add-wait-flag-to-manage-share-server-operat0000664000175000017500000000027000000000000033046 0ustar00zuulzuul00000000000000--- features: - | The command "manila share-server-manage" now accepts an optional "--wait" that allows users to let the client poll for the completion of the operation. ././@PaxHeader0000000000000000000000000000023200000000000011452 xustar0000000000000000132 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1898316-add-wait-option-for-deleting-a-share-server-e2228018585de5cb.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1898316-add-wait-option-for-deleting-a-share-server0000664000175000017500000000027200000000000033105 0ustar00zuulzuul00000000000000--- features: - | The command "manila share-server-delete" now accepts an optional "--wait" that allows users to let the client poll for the completion of the operation. ././@PaxHeader0000000000000000000000000000025700000000000011461 xustar0000000000000000153 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1898317-add-wait-option-for-force-deleting-share-snapshot-share-instance-fb2531b6033f0ae5.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1898317-add-wait-option-for-force-deleting-share-sn0000664000175000017500000000041500000000000033075 0ustar00zuulzuul00000000000000--- features: - | The commands "manila force-delete", "manila snapshot-force-delete" and "manila share-instance-force-delete" now accept an optional "--wait" that allows administrator users to let the client poll for the completion of the operation. ././@PaxHeader0000000000000000000000000000024100000000000011452 xustar0000000000000000139 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1898318-add-wait-flag-for-deleting-a-share-group-operation-c602ba9faad411be.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1898318-add-wait-flag-for-deleting-a-share-group-op0000664000175000017500000000026700000000000032756 0ustar00zuulzuul00000000000000--- features: - | The command "manila share-group-delete" now accepts an optional "--wait" that allows users to let the client poll for the completion of the operation. ././@PaxHeader0000000000000000000000000000023500000000000011455 xustar0000000000000000135 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1898318-add-wait-flag-to-create-sharee-group-operation-cd8310b241d377b0.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1898318-add-wait-flag-to-create-sharee-group-operat0000664000175000017500000000026700000000000033065 0ustar00zuulzuul00000000000000--- features: - | The command "manila share-group-create" now accepts an optional "--wait" that allows users to let the client poll for the completion of the operation. ././@PaxHeader0000000000000000000000000000024600000000000011457 xustar0000000000000000144 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1899325-implement-usage-of-c-or-column-without-additional-logic-2970ee294f32bd31.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1899325-implement-usage-of-c-or-column-without-addi0000664000175000017500000000027400000000000033140 0ustar00zuulzuul00000000000000--- fixes: - | The commands "openstack share type list" and "openstack share access list" no longer support the "--columns" option. Use "--column" to filter the output. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1902873-fix-py-raw-error-msg-a839fee2ac7b9d3d.yaml0000664000175000017500000000036600000000000032230 0ustar00zuulzuul00000000000000--- fixes: - | `Bug #1902873 `_: Fixed raw Python error message when using ``manila`` without a subcommand while passing an optional argument, such as ``--os-cache``. ././@PaxHeader0000000000000000000000000000024300000000000011454 xustar0000000000000000141 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1909477-fix-forbid-users-to-create-shares-with-the-name-none-cfb0a59baa597803.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1909477-fix-forbid-users-to-create-shares-with-the-0000664000175000017500000000033500000000000033047 0ustar00zuulzuul00000000000000--- fixes: - | `Launchpad bug 1909477 `_ has been fixed by prevent sending the share creation request with any capitalization of the name "None". ././@PaxHeader0000000000000000000000000000025500000000000011457 xustar0000000000000000151 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1920888-fix-error-in-cli-manila-list-with-sorting-key-availabilityzone-cea5a1f5d8a38fc3.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1920888-fix-error-in-cli-manila-list-with-sorting-k0000664000175000017500000000025200000000000033066 0ustar00zuulzuul00000000000000--- fixes: - | `Launchpad bug 1920888 `_: Fix the bug in CLI 'manila list' with sorting key 'availability_zone'.././@PaxHeader0000000000000000000000000000023500000000000011455 xustar0000000000000000135 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1925486-add-share-network-option-to-replica-create-api-7d2ff3628e93fc77.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1925486-add-share-network-option-to-replica-create-0000664000175000017500000000051700000000000033107 0ustar00zuulzuul00000000000000--- fixes: - | `Bug #1925486 `_ Share replica create command does not support share network option and manila internally uses parent share's share network. Fixed it to allow any share network by providing option ``share-network`` starting with microversion '2.72'. ././@PaxHeader0000000000000000000000000000023300000000000011453 xustar0000000000000000133 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1953670-fix-id-attr-for-share-group-type-access-repr-008338a53d7a6a50.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1953670-fix-id-attr-for-share-group-type-access-rep0000664000175000017500000000033400000000000033050 0ustar00zuulzuul00000000000000--- fixes: - Launchpad `bug 1953670 `_ has been fixed by updating the attribute name for the share group type access repr to be share_group_type_id. ././@PaxHeader0000000000000000000000000000021400000000000011452 xustar0000000000000000118 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1959329-fix-server-listing-by-subnets-fa6447fd43093cae.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1959329-fix-server-listing-by-subnets-fa6447fd430930000664000175000017500000000025200000000000032366 0ustar00zuulzuul00000000000000--- fixes: - Launchpad `bug 1959329 `_ has been fixed for share server listing with network subnet id. ././@PaxHeader0000000000000000000000000000021000000000000011446 xustar0000000000000000114 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1960422-fix-no-default-share-type-d6191ea0aa1e57fa.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1960422-fix-no-default-share-type-d6191ea0aa1e57fa.0000664000175000017500000000033200000000000032211 0ustar00zuulzuul00000000000000--- fixes: - | `Launchpad bug 1960422 `_ has been fixed by prevent sending the share creation request and provide early feedback to CLI users. ././@PaxHeader0000000000000000000000000000021300000000000011451 xustar0000000000000000117 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1960490-use-suitable-version-for-osc-b375a32273b56522.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1960490-use-suitable-version-for-osc-b375a32273b5650000664000175000017500000000050300000000000032151 0ustar00zuulzuul00000000000000--- fixes: - | The OpenStackClient plugin will now use most suitable version for openstack commmands client generation if user supplied version is above max version supported by server. For more details, please refer `Launchpad bug #1960490 `_ ././@PaxHeader0000000000000000000000000000022100000000000011450 xustar0000000000000000123 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1962288-fixed-share-network-create-command-879dc3deca131ef9.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1962288-fixed-share-network-create-command-879dc3de0000664000175000017500000000017200000000000032700 0ustar00zuulzuul00000000000000fixes: - | Fixed the use of the "--availability-zone" option with the "openstack share network create" command. ././@PaxHeader0000000000000000000000000000021600000000000011454 xustar0000000000000000120 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1975488-skip-force-kwarg-if-unspecified-f98c717df1d6e364.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1975488-skip-force-kwarg-if-unspecified-f98c717df1d0000664000175000017500000000031100000000000032516 0ustar00zuulzuul00000000000000--- fixes: - | Fixed a regression with the use of the "force" keyword in the "extend" API in the Share resource. See `LP #1975488 `_ for more details. ././@PaxHeader0000000000000000000000000000022400000000000011453 xustar0000000000000000126 path=python-manilaclient-4.8.0/releasenotes/notes/bug-1980985-dont-use-share-type-with-snapshot-ref-de0331c640afbbd3.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1980985-dont-use-share-type-with-snapshot-ref-de0330000664000175000017500000000034400000000000032723 0ustar00zuulzuul00000000000000--- fixes: - | When creating a share from snapshot with 'openstack share create', the client no longer forces the use of a share type. See `bug #1980985 `_ for more information. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-1999775-add-os-key-0cfc95c7b480df05.yaml0000664000175000017500000000022500000000000030113 0ustar00zuulzuul00000000000000--- fixes: - | Support --os-key option and OS_KEY environment variable which allows to provide client cert and its private key separately. ././@PaxHeader0000000000000000000000000000020700000000000011454 xustar0000000000000000113 path=python-manilaclient-4.8.0/releasenotes/notes/bug-2030686-fix-default-share-lookup-cc7e592a0dc855e1.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-2030686-fix-default-share-lookup-cc7e592a0dc855e1.y0000664000175000017500000000051100000000000032254 0ustar00zuulzuul00000000000000--- fixes: - | Fixed default share type lookup associated with the "openstack share create" command. The lookup now resolves default share types correctly even on environments where the default type isn't named "default". See `launchpad bug #2030686 `_ for more details. ././@PaxHeader0000000000000000000000000000022400000000000011453 xustar0000000000000000126 path=python-manilaclient-4.8.0/releasenotes/notes/bug-2047249-fix-osc-quota-set-per-share-gigabytes-fcff7f8ce2cc3c75.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-2047249-fix-osc-quota-set-per-share-gigabytes-fcff70000664000175000017500000000035200000000000033016 0ustar00zuulzuul00000000000000--- fixes: - | The `openstack share quota set` command has been fixed to not set `per_share_gigabytes` unless the user has specified the parameter, and unless the user requests, and the server supports API version 2.62. ././@PaxHeader0000000000000000000000000000021500000000000011453 xustar0000000000000000119 path=python-manilaclient-4.8.0/releasenotes/notes/bug-2051737-fix-share-force-delete-request-6d2578fb7da61e3f.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-2051737-fix-share-force-delete-request-6d2578fb7da60000664000175000017500000000042300000000000032430 0ustar00zuulzuul00000000000000--- fixes: - Share force delete command was sending two REST requests, first force delete and then delete. Fixed it by removing delete request. For more details, please refer to `launchpad bug 2051737 `_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug-share-access-list-3cf3114doe40k599.yaml0000664000175000017500000000024400000000000030557 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug_1570085_fix-905786b797379309.yaml0000664000175000017500000000025700000000000026462 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug_1603387_fix_env_variable_8ed5450aab41aa5f.yaml0000664000175000017500000000020400000000000031725 0ustar00zuulzuul00000000000000--- fixes: - Use consistent environment variable naming. All old variables are still supported due to compatibility reasons. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug_1606168_fix-54d3c3bb78389f01.yaml0000664000175000017500000000007700000000000026735 0ustar00zuulzuul00000000000000--- fixes: - Fix error handling for os-token and bypass-url. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug_1715769_fix-3ec701b0fb9d7910.yaml0000664000175000017500000000020600000000000027006 0ustar00zuulzuul00000000000000--- fixes: - Usernames can contain the $ symbol to allow for windows computer authentication in an Active Directory environment ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug_1777849_1779935_fix-344cb8f09b7df502.yaml0000664000175000017500000000015400000000000027755 0ustar00zuulzuul00000000000000--- fixes: - Fixed bugs 1777849 and 1779935. Name can be used for the sort option in the list command.././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/bug_1782672-1954059b373f03de.yaml0000664000175000017500000000012300000000000026002 0ustar00zuulzuul00000000000000--- fixes: - | Fixed bug 1782672. Name can now be used in type-show command. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/deprecate-manila-shell-0061cbcab5d3d75b.yaml0000664000175000017500000000124100000000000031065 0ustar00zuulzuul00000000000000--- prelude: > OpenStackClient is the preferred CLI client supported by this package. This CLI client has full feature parity to the legacy "manila" CLI client. upgrade: - | Usage of the "manila" CLI client is discouraged in favor of the "openstack" CLI. The "manila" CLI client will be removed in a future release. deprecations: - | The "manila" CLI client is now deprecated. The python-manilaclient package provides an openstack CLI plugin. As a replacement, the "openstack" CLI must be used. No new features will be added to the "manila" CLI client. The "manilalient" SDK/python bindings are not affected by this deprecation. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/deprecate-v1-a0cfa6fd723c2f46.yaml0000664000175000017500000000111500000000000027061 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/drop-py36-and-py37-85cf389b2842f045.yaml0000664000175000017500000000020600000000000027423 0ustar00zuulzuul00000000000000--- deprecations: - | Support for Python versions 3.6 and 3.7 was dropped. The minimum supported version is now Python 3.8. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/drop-python2-support-71c7b9e1dcf8c890.yaml0000664000175000017500000000044500000000000030625 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000020700000000000011454 xustar0000000000000000113 path=python-manilaclient-4.8.0/releasenotes/notes/expose-access-key-in-access-list-API-b57c386c9048ae55.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/expose-access-key-in-access-list-API-b57c386c9048ae55.y0000664000175000017500000000016600000000000032465 0ustar00zuulzuul00000000000000--- features: - Returns ``access_key`` as part of ``access_list`` API response for API microversions >= '2.21'. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/fix-and-improve-access-rules-a118a7f8e22f65bb.yaml0000664000175000017500000000036600000000000032127 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/fix-is-default-empty-557844001e0401e2.yaml0000664000175000017500000000037100000000000030110 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=python-manilaclient-4.8.0/releasenotes/notes/graduate-share-replication-feature-49770e921b4338fb.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/graduate-share-replication-feature-49770e921b4338fb.yam0000664000175000017500000000076300000000000033005 0ustar00zuulzuul00000000000000--- prelude: > - | Share replication APIs have graduated from their `experimental feature state `_ from API version ``2.56``. One or more share replicas can be created from a given share. They can also be promoted to be considered the active share, resynchronized and deleted. These actions no longer require the inclusion of ``X-OpenStack-Manila-API-Experimental`` header in the API requests. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/handle-missing-api-minor-version-5a6d242f28883442.yaml0000664000175000017500000000012100000000000032504 0ustar00zuulzuul00000000000000--- fixes: - Added proper error handling for missing API minor version number. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/manage-unmanage-share-servers-8c7b27a1fe80e5fa.yaml0000664000175000017500000000024500000000000032423 0ustar00zuulzuul00000000000000--- features: - Added CLI commands to manage and unmanage share servers. - Updated CLI command for managing shares to accept ``share_server_id`` parameter. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/manila-client-support-recycle-bin-4ecb5de770bd525f.yaml0000664000175000017500000000023300000000000033230 0ustar00zuulzuul00000000000000--- features: - Added CLI commands to soft delete share. - Added CLI commands to restore share. - Added CLI commands to query shares in recycle bin. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/manila-openstackclient-bf61ceb270d3afb7.yaml0000664000175000017500000000410700000000000031307 0ustar00zuulzuul00000000000000--- prelude: > The manilaclient repository now includes a plugin to openstackclient. You can use the ``openstack share ..`` command line interface to administer the Shared File System service (manila) on any deployment alongside using it to provision and manage the life cycle of manila resources. Refer to the `official documentation `_ to find command syntax and usage information. You may also view this documentation directly in the CLI with ``openstack help share ``. For example, ``openstack help share create``. upgrade: - | With the introduction of the openstackclient integration, we are also announcing our intent to deprecate the "manila" shell commands. These clients will emit a deprecation warning in a future release, and will eventually be removed. We recommend that you transition your workflows and tooling to use the "openstack" CLI when using the Shared File Systems service (manila) commands. The "manilaclient" SDK is unaffected by the introducion of OpenStackClient. other: - | The ``openstack share ..`` CLI is usable only with the "v2" API. At this time, we have no plans to support its use with the deprecated "v1" API provided by the Shared File System service (manila). - | The ``openstack share ..`` CLI does not yet support API microversion negotiation. This means that to use this release of the OpenStackClient with older Shared File System API service, users would have to set the API version in their environments. This can be done via cloud config (specify ``shared_file_system_api_version``) or via shell environment ``OS_SHARE_API_VERSION`` or via the CLI overrride: ``--os-share-api-version``. Without this override, the client will use a `hard-coded API version `_ to make API requests and this may fail in your deployment if the version is unsupported. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/migration-share-type-4fc3b7c6187f5201.yaml0000664000175000017500000000012200000000000030432 0ustar00zuulzuul00000000000000--- features: - Added parameter to change share type when performing migration. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/mountable_snapshot-ced01da7dffc6d7e.yaml0000664000175000017500000000012600000000000030735 0ustar00zuulzuul00000000000000--- features: - Added support for the mountable snapshots feature to manila client. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/newton-migration-improvements-166a03472948bdef.yaml0000664000175000017500000000146700000000000032432 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000022500000000000011454 xustar0000000000000000127 path=python-manilaclient-4.8.0/releasenotes/notes/remove-experimental-flag-from-share-groups-feature-dcf2b0b67fe4cac4.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/remove-experimental-flag-from-share-groups-feature-dcf20000664000175000017500000000110600000000000034011 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000021300000000000011451 xustar0000000000000000117 path=python-manilaclient-4.8.0/releasenotes/notes/remove-nova-net-id-option-for-share-nets-82e424b75221528b.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/remove-nova-net-id-option-for-share-nets-82e424b75221520000664000175000017500000000042000000000000032602 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/share-backup-98e11c6a28897e94.yaml0000664000175000017500000000050000000000000026675 0ustar00zuulzuul00000000000000--- features: - | Added support for share backup APIs in the SDK and the openstackclient plugin. You can use the openstack client to create a backup, restore a backup, delete a backup, list backups with filters, and update the name and description fields of a backup. Available from microversion 2.80. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/share-network-multiple-subnets-732309abfbf5987c.yaml0000664000175000017500000000104600000000000032564 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/share-revert-to-snapshot-e899a4b7e1126749.yaml0000664000175000017500000000010400000000000031206 0ustar00zuulzuul00000000000000--- features: - Added support for the revert-to-snapshot feature. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/share_type-skip-format-119595e62900e571.yaml0000664000175000017500000000046000000000000030552 0ustar00zuulzuul00000000000000--- fixes: - | The following openstack subcommands no longer format the required_extra_specs field and the optional_extra_specs field unless the default ``table`` format is used. - ``openstack share type create`` - ``openstack share type list`` - ``openstack share type show`` ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/start-using-reno-b744cd0259c7a88c.yaml0000664000175000017500000000012600000000000027675 0ustar00zuulzuul00000000000000--- features: - Started using release notes to track changes to python-manilaclient.././@PaxHeader0000000000000000000000000000022000000000000011447 xustar0000000000000000122 path=python-manilaclient-4.8.0/releasenotes/notes/support-create-share-from-snapshot-extra-spec-cdba92f179c1c5c6.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/support-create-share-from-snapshot-extra-spec-cdba92f170000664000175000017500000000023100000000000033571 0ustar00zuulzuul00000000000000--- features: - Support the new optional create_share_from_snapshot_support extra spec, and handle the newly-optional snapshot_support extra spec. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/support-share-server-migration-9804752270c6b153.yaml0000664000175000017500000000050200000000000032253 0ustar00zuulzuul00000000000000--- features: - | Added support for performing the share server migration. The new commands are: `share-server-migration-start`, `share-server-migration-complete`, `share-server-migration-cancel`, `share-server-migration-get-progress`, `share-server-migration-check` and `share-server-reset-task-state`. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/support-show-type-6380b7c539c95ba8.yaml0000664000175000017500000000006500000000000030053 0ustar00zuulzuul00000000000000--- features: - Support show type details command. ././@PaxHeader0000000000000000000000000000021200000000000011450 xustar0000000000000000116 path=python-manilaclient-4.8.0/releasenotes/notes/support_add_disabled_reason_to_services-08cb52e4711745c7.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/support_add_disabled_reason_to_services-08cb52e4711745c0000664000175000017500000000024100000000000033403 0ustar00zuulzuul00000000000000--- features: - Support add disabled reason to services command. See `bug 2037700 `_ for more details. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/type-create-extra-specs-691572762357ef3b.yaml0000664000175000017500000000011100000000000030712 0ustar00zuulzuul00000000000000--- fixes: - User can specify any number of extra-specs in type_create. ././@PaxHeader0000000000000000000000000000025000000000000011452 xustar0000000000000000146 path=python-manilaclient-4.8.0/releasenotes/notes/update-api-version-create-share-from-snapshot-another-pool-or-backend-694cfda84a41c4ff.yaml 22 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/update-api-version-create-share-from-snapshot-another-p0000664000175000017500000000040600000000000034037 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/notes/v2-0-0-deprecated-opts-removal-863565618535733d.yaml0000664000175000017500000000061200000000000031451 0ustar00zuulzuul00000000000000--- upgrade: - | manilaclient SDK no longer supports some options that were deprecated in version 2.0.0: 'share_service_name' (use 'service_name' instead), 'proxy_tenant_id', 'proxy_token', 'os_cache' (use 'use_keyring' instead) 'api_key'(use 'password' instead). The client.authenticate() method has been removed as well, since authentication is performed automatically. ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.633288 python-manilaclient-4.8.0/releasenotes/source/0000775000175000017500000000000000000000000021516 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/2023.1.rst0000664000175000017500000000020200000000000022767 0ustar00zuulzuul00000000000000=========================== 2023.1 Series Release Notes =========================== .. release-notes:: :branch: stable/2023.1 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/2023.2.rst0000664000175000017500000000020200000000000022770 0ustar00zuulzuul00000000000000=========================== 2023.2 Series Release Notes =========================== .. release-notes:: :branch: stable/2023.2 ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.633288 python-manilaclient-4.8.0/releasenotes/source/_static/0000775000175000017500000000000000000000000023144 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/_static/.placeholder0000664000175000017500000000000000000000000025415 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.633288 python-manilaclient-4.8.0/releasenotes/source/_templates/0000775000175000017500000000000000000000000023653 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/_templates/.placeholder0000664000175000017500000000000000000000000026124 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/conf.py0000664000175000017500000002131700000000000023021 0ustar00zuulzuul00000000000000# 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 ------------------------------------------------ # 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 openstackdocs_repo_name = 'openstack/python-manilaclient' openstackdocs_bug_project = 'python-manilaclient' openstackdocs_bug_tag = 'release notes' openstackdocs_auto_name = False # 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 = 'python-manilaclient Release Notes' copyright = '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 = 'native' # 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 = {} # 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 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', 'python-manilaclient Release Notes Documentation', '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', 'python-manilaclient Release Notes Documentation', ['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', 'python-manilaclient Release Notes Documentation', '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/'] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/index.rst0000664000175000017500000000044000000000000023355 0ustar00zuulzuul00000000000000 ================================= python-manilaclient Release Notes ================================= .. toctree:: :maxdepth: 1 unreleased 2023.2 2023.1 zed yoga xena wallaby victoria ussuri train stein rocky queens pike ocata newton ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/newton.rst0000664000175000017500000000023200000000000023557 0ustar00zuulzuul00000000000000=================================== Newton Series Release Notes =================================== .. release-notes:: :branch: origin/stable/newton ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/ocata.rst0000664000175000017500000000023000000000000023332 0ustar00zuulzuul00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/pike.rst0000664000175000017500000000021700000000000023200 0ustar00zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/queens.rst0000664000175000017500000000022300000000000023545 0ustar00zuulzuul00000000000000=================================== Queens Series Release Notes =================================== .. release-notes:: :branch: stable/queens ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/rocky.rst0000664000175000017500000000022100000000000023372 0ustar00zuulzuul00000000000000=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/stein.rst0000664000175000017500000000022100000000000023365 0ustar00zuulzuul00000000000000=================================== Stein Series Release Notes =================================== .. release-notes:: :branch: stable/stein ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/train.rst0000664000175000017500000000017600000000000023371 0ustar00zuulzuul00000000000000========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/unreleased.rst0000664000175000017500000000015300000000000024376 0ustar00zuulzuul00000000000000============================ Current Series Release Notes ============================ .. release-notes:: ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/ussuri.rst0000664000175000017500000000020200000000000023574 0ustar00zuulzuul00000000000000=========================== Ussuri Series Release Notes =========================== .. release-notes:: :branch: stable/ussuri ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/victoria.rst0000664000175000017500000000021200000000000024063 0ustar00zuulzuul00000000000000============================= Victoria Series Release Notes ============================= .. release-notes:: :branch: stable/victoria ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/wallaby.rst0000664000175000017500000000020600000000000023701 0ustar00zuulzuul00000000000000============================ Wallaby Series Release Notes ============================ .. release-notes:: :branch: stable/wallaby ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/xena.rst0000664000175000017500000000017200000000000023203 0ustar00zuulzuul00000000000000========================= Xena Series Release Notes ========================= .. release-notes:: :branch: stable/xena ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/yoga.rst0000664000175000017500000000020000000000000023177 0ustar00zuulzuul00000000000000========================= Yoga Series Release Notes ========================= .. release-notes:: :branch: unmaintained/yoga ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/releasenotes/source/zed.rst0000664000175000017500000000016600000000000023035 0ustar00zuulzuul00000000000000======================== Zed Series Release Notes ======================== .. release-notes:: :branch: stable/zed ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/requirements.txt0000664000175000017500000000151700000000000021015 0ustar00zuulzuul00000000000000# Requirements lower bounds listed here are our best effort to keep them up to # date but we do not test them so no guarantee of having them all correct. If # you find any incorrect lower bounds, let us know or propose a fix. # 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.7.1 # BSD requests>=2.14.2 # Apache-2.0 simplejson>=3.5.1 # MIT Babel!=2.4.0,>=2.3.4 # BSD osc-lib>=1.10.0 # Apache-2.0 python-keystoneclient>=3.8.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709286955.5492654 python-manilaclient-4.8.0/roles/0000775000175000017500000000000000000000000016651 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.633288 python-manilaclient-4.8.0/roles/populate-manilaclient-config/0000775000175000017500000000000000000000000024403 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/roles/populate-manilaclient-config/README.rst0000664000175000017500000000235100000000000026073 0ustar00zuulzuul00000000000000populate-manilaclient-config ============================ An ansible role to use devstack's helper scripts and create a configuration file for running python-manilaclient's functional tests. Role Variables -------------- .. zuul:rolevar:: base_dir :type: string :default: /opt/stack The base directory for the installation. The devstack folder is expected to be found in this location .. zuul:rolevar:: manilaclient_config :type: string :default: "{{ zuul.project.src_dir }}/etc/manilaclient/manilaclient.conf" The location of the manilaclient test configuration file. .. zuul:rolevar:: neutron_network_name :type: string :default: private A pre-created neutron network that the tests can use. This network must be accessible to the "demo" and "admin" users within the "demo" project. .. zuul:rolevar:: neutron_subnet_name :type: string :default: private-subnet A pre-created neutron subnet that the tests can use. This network must be accessible to the "demo" and "admin" users within the "demo" project. .. zuul:rolevar:: share_network_name :type: string :default: ci The name to give the share network created by this role, and configured for use by the tests. ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.633288 python-manilaclient-4.8.0/roles/populate-manilaclient-config/defaults/0000775000175000017500000000000000000000000026212 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/roles/populate-manilaclient-config/defaults/main.yaml0000664000175000017500000000030700000000000030022 0ustar00zuulzuul00000000000000--- base_dir: /opt/stack manilaclient_config: "{{ zuul.project.src_dir }}/etc/manilaclient/manilaclient.conf" neutron_network_name: private neutron_subnet_name: private-subnet share_network_name: ci ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.633288 python-manilaclient-4.8.0/roles/populate-manilaclient-config/tasks/0000775000175000017500000000000000000000000025530 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/roles/populate-manilaclient-config/tasks/main.yaml0000664000175000017500000000475500000000000027353 0ustar00zuulzuul00000000000000- name: create the manilaclient config file file: path: "{{ manilaclient_config }}" state: touch - name: Set OS_ env variables to config shell: cmd: | source {{ base_dir }}/devstack/functions source {{ base_dir }}/devstack/openrc demo demo &>/dev/null iniset {{ manilaclient_config }} DEFAULT username $OS_USERNAME iniset {{ manilaclient_config }} DEFAULT tenant_name $OS_PROJECT_NAME iniset {{ manilaclient_config }} DEFAULT password $OS_PASSWORD iniset {{ manilaclient_config }} DEFAULT auth_url $OS_AUTH_URL iniset {{ manilaclient_config }} DEFAULT project_domain_id $OS_PROJECT_DOMAIN_ID iniset {{ manilaclient_config }} DEFAULT user_domain_id $OS_USER_DOMAIN_ID source {{ base_dir }}/devstack/openrc admin demo &>/dev/null iniset {{ manilaclient_config }} DEFAULT admin_username $OS_USERNAME iniset {{ manilaclient_config }} DEFAULT admin_tenant_name $OS_TENANT_NAME iniset {{ manilaclient_config }} DEFAULT admin_password $OS_PASSWORD iniset {{ manilaclient_config }} DEFAULT admin_auth_url $OS_AUTH_URL iniset {{ manilaclient_config }} DEFAULT admin_project_domain_id $OS_PROJECT_DOMAIN_ID iniset {{ manilaclient_config }} DEFAULT admin_user_domain_id $OS_USER_DOMAIN_ID iniset {{ manilaclient_config }} DEFAULT access_types_mapping "nfs:ip,cifs:user" # Tests iniset {{ manilaclient_config }} DEFAULT suppress_errors_in_cleanup false iniset {{ manilaclient_config }} DEFAULT run_migration_tests true iniset {{ manilaclient_config }} DEFAULT run_manage_tests true iniset {{ manilaclient_config }} DEFAULT run_mount_snapshot_tests true iniset {{ manilaclient_config }} DEFAULT run_share_servers_migration_tests true args: executable: "/bin/bash" - name: create a share network shell: cmd: | source {{ base_dir }}/devstack/functions source {{ base_dir }}/devstack/openrc demo demo &>/dev/null NEUTRON_NET=$(openstack network show {{ neutron_network_name }} -c id -f value) NEUTRON_SUBNET=$(openstack subnet show {{ neutron_subnet_name }} -c id -f value) openstack share network create \ --name {{ share_network_name }} \ --neutron-net $NEUTRON_NET \ --neutron-subnet $NEUTRON_SUBNET iniset {{ manilaclient_config }} DEFAULT share_network {{ share_network_name }} iniset {{ manilaclient_config }} DEFAULT admin_share_network {{ share_network_name }} args: executable: "/bin/bash" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/run_tests.sh0000775000175000017500000001270200000000000020114 0ustar00zuulzuul00000000000000#!/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 ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.637289 python-manilaclient-4.8.0/setup.cfg0000664000175000017500000003072100000000000017351 0ustar00zuulzuul00000000000000[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.8 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 :: Only Programming Language :: Python :: 3 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 [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_resize = manilaclient.osc.v2.share:ResizeShare share_adopt = manilaclient.osc.v2.share:AdoptShare share_abandon = manilaclient.osc.v2.share:AbandonShare share_migration_start = manilaclient.osc.v2.share:ShareMigrationStart share_migration_cancel = manilaclient.osc.v2.share:ShareMigrationCancel share_migration_complete = manilaclient.osc.v2.share:ShareMigrationComplete share_migration_show = manilaclient.osc.v2.share:ShareMigrationShow share_export_location_show = manilaclient.osc.v2.share:ShareExportLocationShow share_export_location_list = manilaclient.osc.v2.share:ShareExportLocationList share_properties_show = manilaclient.osc.v2.share:ShowShareProperties share_restore = manilaclient.osc.v2.share:RestoreShare share_revert = manilaclient.osc.v2.share:RevertShare 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_backup_create = manilaclient.osc.v2.share_backups:CreateShareBackup share_backup_delete = manilaclient.osc.v2.share_backups:DeleteShareBackup share_backup_list = manilaclient.osc.v2.share_backups:ListShareBackup share_backup_show = manilaclient.osc.v2.share_backups:ShowShareBackup share_backup_restore = manilaclient.osc.v2.share_backups:RestoreShareBackup share_backup_set = manilaclient.osc.v2.share_backups:SetShareBackup share_backup_unset = manilaclient.osc.v2.share_backups:UnsetShareBackup 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 share_quota_set = manilaclient.osc.v2.quotas:QuotaSet share_quota_show = manilaclient.osc.v2.quotas:QuotaShow share_quota_delete = manilaclient.osc.v2.quotas:QuotaDelete share_snapshot_create = manilaclient.osc.v2.share_snapshots:CreateShareSnapshot share_snapshot_delete = manilaclient.osc.v2.share_snapshots:DeleteShareSnapshot share_snapshot_show = manilaclient.osc.v2.share_snapshots:ShowShareSnapshot share_snapshot_set = manilaclient.osc.v2.share_snapshots:SetShareSnapshot share_snapshot_unset = manilaclient.osc.v2.share_snapshots:UnsetShareSnapshot share_snapshot_list = manilaclient.osc.v2.share_snapshots:ListShareSnapshot share_snapshot_adopt = manilaclient.osc.v2.share_snapshots:AdoptShareSnapshot share_snapshot_abandon = manilaclient.osc.v2.share_snapshots:AbandonShareSnapshot share_snapshot_access_create = manilaclient.osc.v2.share_snapshots:ShareSnapshotAccessAllow share_snapshot_access_delete = manilaclient.osc.v2.share_snapshots:ShareSnapshotAccessDeny share_snapshot_access_list = manilaclient.osc.v2.share_snapshots:ShareSnapshotAccessList share_snapshot_export_location_list = manilaclient.osc.v2.share_snapshots:ShareSnapshotListExportLocation share_snapshot_export_location_show = manilaclient.osc.v2.share_snapshots:ShareSnapshotShowExportLocation share_snapshot_instance_list = manilaclient.osc.v2.share_snapshot_instances:ListShareSnapshotInstance share_snapshot_instance_show = manilaclient.osc.v2.share_snapshot_instances:ShowShareSnapshotInstance share_snapshot_instance_set = manilaclient.osc.v2.share_snapshot_instances:SetShareSnapshotInstance share_snapshot_instance_export_location_list = manilaclient.osc.v2.share_snapshot_instance_export_locations:ShareSnapshotInstanceExportLocationList share_snapshot_instance_export_location_show = manilaclient.osc.v2.share_snapshot_instance_export_locations:ShareSnapshotInstanceExportLocationShow share_message_delete = manilaclient.osc.v2.messages:DeleteMessage share_message_list = manilaclient.osc.v2.messages:ListMessage share_message_show = manilaclient.osc.v2.messages:ShowMessage share_replica_create = manilaclient.osc.v2.share_replicas:CreateShareReplica share_replica_delete = manilaclient.osc.v2.share_replicas:DeleteShareReplica share_replica_list = manilaclient.osc.v2.share_replicas:ListShareReplica share_replica_show = manilaclient.osc.v2.share_replicas:ShowShareReplica share_replica_set = manilaclient.osc.v2.share_replicas:SetShareReplica share_replica_promote = manilaclient.osc.v2.share_replicas:PromoteShareReplica share_replica_resync = manilaclient.osc.v2.share_replicas:ResyncShareReplica share_replica_export_location_list = manilaclient.osc.v2.share_replica_export_locations:ShareReplicaListExportLocation share_replica_export_location_show = manilaclient.osc.v2.share_replica_export_locations:ShareReplicaShowExportLocation share_availability_zone_list = manilaclient.osc.v2.availability_zones:ShareAvailabilityZoneList share_service_set = manilaclient.osc.v2.services:SetShareService share_service_list = manilaclient.osc.v2.services:ListShareService share_security_service_create = manilaclient.osc.v2.security_services:CreateShareSecurityService share_security_service_delete = manilaclient.osc.v2.security_services:DeleteShareSecurityService share_security_service_show = manilaclient.osc.v2.security_services:ShowShareSecurityService share_security_service_set = manilaclient.osc.v2.security_services:SetShareSecurityService share_security_service_unset = manilaclient.osc.v2.security_services:UnsetShareSecurityService share_security_service_list = manilaclient.osc.v2.security_services:ListShareSecurityService share_pool_list = manilaclient.osc.v2.share_pools:ListSharePools share_instance_delete = manilaclient.osc.v2.share_instances:ShareInstanceDelete share_instance_list = manilaclient.osc.v2.share_instances:ShareInstanceList share_instance_set = manilaclient.osc.v2.share_instances:ShareInstanceSet share_instance_show = manilaclient.osc.v2.share_instances:ShareInstanceShow share_instance_export_location_show = manilaclient.osc.v2.share_instance_export_locations:ShareInstanceShowExportLocation share_instance_export_location_list = manilaclient.osc.v2.share_instance_export_locations:ShareInstanceListExportLocation share_limits_show = manilaclient.osc.v2.share_limits:ShareLimitsShow share_network_list = manilaclient.osc.v2.share_networks:ListShareNetwork share_network_show = manilaclient.osc.v2.share_networks:ShowShareNetwork share_network_create = manilaclient.osc.v2.share_networks:CreateShareNetwork share_network_delete = manilaclient.osc.v2.share_networks:DeleteShareNetwork share_network_set = manilaclient.osc.v2.share_networks:SetShareNetwork share_network_unset = manilaclient.osc.v2.share_networks:UnsetShareNetwork share_network_subnet_create = manilaclient.osc.v2.share_network_subnets:CreateShareNetworkSubnet share_network_subnet_delete = manilaclient.osc.v2.share_network_subnets:DeleteShareNetworkSubnet share_network_subnet_show = manilaclient.osc.v2.share_network_subnets:ShowShareNetworkSubnet share_network_subnet_set = manilaclient.osc.v2.share_network_subnets:SetShareNetworkSubnet share_network_subnet_unset = manilaclient.osc.v2.share_network_subnets:UnsetShareNetworkSubnet share_group_create = manilaclient.osc.v2.share_groups:CreateShareGroup share_group_delete = manilaclient.osc.v2.share_groups:DeleteShareGroup share_group_list = manilaclient.osc.v2.share_groups:ListShareGroup share_group_show = manilaclient.osc.v2.share_groups:ShowShareGroup share_group_set = manilaclient.osc.v2.share_groups:SetShareGroup share_group_unset = manilaclient.osc.v2.share_groups:UnsetShareGroup share_group_type_create = manilaclient.osc.v2.share_group_types:CreateShareGroupType share_group_type_delete = manilaclient.osc.v2.share_group_types:DeleteShareGroupType share_group_type_list = manilaclient.osc.v2.share_group_types:ListShareGroupType share_group_type_show = manilaclient.osc.v2.share_group_types:ShowShareGroupType share_group_type_set = manilaclient.osc.v2.share_group_types:SetShareGroupType share_group_type_unset = manilaclient.osc.v2.share_group_types:UnsetShareGroupType share_group_type_access_create = manilaclient.osc.v2.share_group_type_access:ShareGroupTypeAccessAllow share_group_type_access_list = manilaclient.osc.v2.share_group_type_access:ListShareGroupTypeAccess share_group_type_access_delete = manilaclient.osc.v2.share_group_type_access:ShareGroupTypeAccessDeny share_group_snapshot_create = manilaclient.osc.v2.share_group_snapshots:CreateShareGroupSnapshot share_group_snapshot_delete = manilaclient.osc.v2.share_group_snapshots:DeleteShareGroupSnapshot share_group_snapshot_show= manilaclient.osc.v2.share_group_snapshots:ShowShareGroupSnapshot share_group_snapshot_list = manilaclient.osc.v2.share_group_snapshots:ListShareGroupSnapshot share_group_snapshot_set = manilaclient.osc.v2.share_group_snapshots:SetShareGroupSnapshot share_group_snapshot_unset = manilaclient.osc.v2.share_group_snapshots:UnsetShareGroupSnapshot share_group_snapshot_members_list = manilaclient.osc.v2.share_group_snapshots:ListShareGroupSnapshotMembers share_server_delete = manilaclient.osc.v2.share_servers:DeleteShareServer share_server_show = manilaclient.osc.v2.share_servers:ShowShareServer share_server_list = manilaclient.osc.v2.share_servers:ListShareServer share_server_adopt = manilaclient.osc.v2.share_servers:AdoptShareServer share_server_abandon = manilaclient.osc.v2.share_servers:AbandonShareServer share_server_set = manilaclient.osc.v2.share_servers:SetShareServer share_server_migration_cancel = manilaclient.osc.v2.share_servers:ShareServerMigrationCancel share_server_migration_complete = manilaclient.osc.v2.share_servers:ShareServerMigrationComplete share_server_migration_show = manilaclient.osc.v2.share_servers:ShareServerMigrationShow share_server_migration_start = manilaclient.osc.v2.share_servers:ShareServerMigrationStart share_transfer_create = manilaclient.osc.v2.share_transfers:CreateShareTransfer share_transfer_delete = manilaclient.osc.v2.share_transfers:DeleteShareTransfer share_transfer_list = manilaclient.osc.v2.share_transfers:ListShareTransfer share_transfer_show = manilaclient.osc.v2.share_transfers:ShowShareTransfer share_transfer_accept = manilaclient.osc.v2.share_transfers:AcceptShareTransfer share_lock_create = manilaclient.osc.v2.resource_locks:CreateResourceLock share_lock_list = manilaclient.osc.v2.resource_locks:ListResourceLock share_lock_show = manilaclient.osc.v2.resource_locks:ShowResourceLock share_lock_set = manilaclient.osc.v2.resource_locks:SetResourceLock share_lock_unset = manilaclient.osc.v2.resource_locks:UnsetResourceLock share_lock_delete = manilaclient.osc.v2.resource_locks:DeleteResourceLock [coverage:run] omit = manilaclient/tests/* branch = true [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/setup.py0000664000175000017500000000127100000000000017240 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/test-requirements.txt0000664000175000017500000000036100000000000021766 0ustar00zuulzuul00000000000000hacking>=6.1.0,<6.2.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 stestr>=2.0.0 # Apache-2.0 tempest>=17.1.0 # Apache-2.0 testtools>=2.2.0 # MIT python-openstackclient>=5.3.0 # Apache-2.0 ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.633288 python-manilaclient-4.8.0/tools/0000775000175000017500000000000000000000000016665 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/tools/manila.bash_completion0000664000175000017500000000055700000000000023225 0ustar00zuulzuul00000000000000_manila() { local cur prev opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" opts="$(manila bash_completion)" COMPLETION_CACHE=~/.cache/manilaclient/*/*-cache opts+=" "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ') COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) } complete -F _manila manila ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/tox.ini0000664000175000017500000000501700000000000017043 0ustar00zuulzuul00000000000000[tox] distribute = False envlist = py3,pep8 minversion = 4.11.0 # Automatic envs (pyXX) will only use the python version appropriate to that # env and ignore basepython inherited from [testenv] if we set # ignore_basepython_conflict. ignore_basepython_conflict = true [testenv] basepython = python3 usedevelop = true setenv = VIRTUAL_ENV={envdir} OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 PYTHONDONTWRITEBYTECODE=1 deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/test-requirements.txt commands = 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] deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/doc/requirements.txt allowlist_externals = rm commands = rm -rf doc/build sphinx-build -b html doc/source doc/build/html [testenv:pdf-docs] deps = {[testenv:docs]deps} allowlist_externals = make commands = sphinx-build -W -b latex doc/source doc/build/pdf make -C doc/build/pdf [testenv:releasenotes] deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/doc/requirements.txt allowlist_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 = 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] allowlist_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 ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1709286955.633288 python-manilaclient-4.8.0/zuul.d/0000775000175000017500000000000000000000000016746 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/zuul.d/project.yaml0000664000175000017500000000075400000000000021306 0ustar00zuulzuul00000000000000- project: templates: - publish-openstack-docs-pti - openstack-cover-jobs - openstack-python3-jobs - check-requirements - release-notes-jobs-python3 check: jobs: - python-manilaclient-functional - manila-rally-ss: voting: false - python-manilaclient-functional-fips: voting: false - manila-rally-no-ss: voting: false gate: jobs: - python-manilaclient-functional ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709286926.0 python-manilaclient-4.8.0/zuul.d/python-manilaclient-jobs.yaml0000664000175000017500000001122300000000000024543 0ustar00zuulzuul00000000000000- job: name: python-manilaclient-functional parent: devstack-minimal irrelevant-files: - ^manilaclient/tests/unit/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ required-projects: - openstack/keystone - openstack/manila - openstack/neutron - openstack/python-manilaclient timeout: 5400 run: playbooks/python-manilaclient-functional/run.yaml post-run: playbooks/python-manilaclient-functional/post.yaml vars: zuul_copy_output: "{{ zuul.project.src_dir }}/etc/manilaclient/manilaclient.conf": logs tox_environment: PYTHONUNBUFFERED: 'true' tox_envlist: functional devstack_plugins: manila: https://opendev.org/openstack/manila devstack_localrc: USE_PYTHON3: true INSTALL_TEMPEST: false # Enable manila with a fake driver that supports all capabilities MANILA_CONFIGURE_DEFAULT_TYPES: true MANILA_OPTGROUP_DEFAULT_quota_share_networks: 50 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_INSTALL_TEMPEST_PLUGIN_SYSTEMWIDE: false MANILA_SERVICE_IMAGE_ENABLED: false MANILA_SHARE_MIGRATION_PERIOD_TASK_INTERVAL: 1 MANILA_SERVER_MIGRATION_PERIOD_TASK_INTERVAL: 10 SHARE_DRIVER: manila.tests.share.drivers.dummy.DummyDriver MANILA_REPLICA_STATE_UPDATE_INTERVAL: 10 MANILA_ENABLED_BACKENDS: alpha,beta,gamma,delta MANILA_CONFIGURE_GROUPS: alpha,beta,gamma,delta,membernet,adminnet 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 MANILA_OPTGROUP_alpha_replication_domain: DUMMY_DOMAIN_2 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 MANILA_OPTGROUP_beta_replication_domain: DUMMY_DOMAIN_2 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 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 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 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 devstack_services: # Keystone key: true # OVN services ovn-controller: true ovn-northd: true ovs-vswitchd: true ovsdb-server: true # Neutron services q-svc: true q-ovn-metadata-agent: true - job: name: python-manilaclient-functional-fips parent: python-manilaclient-functional nodeset: devstack-single-node-centos-9-stream pre-run: - playbooks/enable-fips.yaml vars: configure_swap_size: 4096 nslookup_target: 'opendev.org'