././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.1290944 python_manilaclient-6.2.0/0000775000175000017500000000000015221154502014377 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/.git-blame-ignore-revs0000664000175000017500000000067615221154414020512 0ustar00zuulzuul# Git blame ignore revisions # This file contains commit hashes that should be ignored by git blame # to avoid showing formatting/style changes in blame output. # # To use this file, run: # git config blame.ignoreRevsFile .git-blame-ignore-revs # Ruff formatting commits to ignore 0ef57c0f49b9c0a6be358a87fff67869c0dc95b4 95bd3aca64ddec062b884521349914866ddb4672 16aaa8e7439bb8441c7a9ceff8d398fda276c553 3e3b385c8c20b94b57255a3f34b39a249ba0a3ae ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/.pre-commit-config.yaml0000664000175000017500000000204715221154414020665 0ustar00zuulzuul--- repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - id: trailing-whitespace - id: mixed-line-ending args: ['--fix', 'lf'] exclude: '.*\.(svg)$' - id: fix-byte-order-marker - id: check-executables-have-shebangs - id: check-merge-conflict - id: debug-statements - id: check-yaml files: .*\.(yaml|yml)$ exclude: '^(zuul.d|rally-jobs)/.*$' - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.14.0 hooks: - id: ruff-check args: ['--fix', '--unsafe-fixes'] - id: ruff-format - repo: https://opendev.org/openstack/hacking rev: 7.0.0 hooks: - id: hacking additional_dependencies: [] exclude: '^(doc|releasenotes|tools)/.*$' - repo: https://github.com/PyCQA/doc8 rev: v2.0.0 hooks: - id: doc8 args: ['--ignore', 'D001'] - repo: https://github.com/openstack/bashate rev: 2.1.1 hooks: - id: bashate args: ['--ignore', 'E006,E042,E043'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/.stestr.conf0000664000175000017500000000011215221154414016644 0ustar00zuulzuul[DEFAULT] test_path=${OS_TEST_PATH:-./manilaclient/tests/unit} top_dir=./ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896961.0 python_manilaclient-6.2.0/AUTHORS0000664000175000017500000001765615221154501015465 0ustar00zuulzuul119Vik 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 Carlos Eduardo Carlos da Silva ChangBo Guo(gcb) Chen Chris MacNaughton Christian Berendt Christian Espinal Chuan Miao Chuck Fouts Clifford Clifford Emeka Clinton Knight Cloud User Corey Bryant Cyril Roelandt Daniel Gonzalez Daniel Russell David Sariel Deepak C Shetty Denis Cavalcante Denver Baraka Dina Saparbaeva Dirk Mueller Dirk Müller Doug Hellmann Douglas Viroel Dustin Schoenbrun Eduardo Santos Eric Harney Eunkyung99 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 Jasek 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 Medha Chaturvedi Nahim Alves de Souza Namrata Sitlani Nguyen Hai Nguyen Hung Phuong OTSUKA, Yuanying Ondřej Nový OpenStack Release Bot Pete Zaitcev Pierre Riteau Premlata Ramana Raja Riane Torres Rodrigo Barbieri Rodrigo Barbieri Saikumar Pulluri Sally Karimi 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 Zachary Goggin altanai andrebeltrami annwesha.das archanaserver ashrod98 binean bswartz cFouts celenamichaud chenxiangui czl389 daiki kato debeltrami deepak_mourya dengzhaosen denver-baraka devin dineshbhor drngsl e gugug haixin <653186640@qq.com> haixin haixin hassanasghar houming-wang howardlee iswarya_vakati jacky06 jayaanand.borra@netapp.com ji-xuepeng junboli kafilat-adeleke kayrus kedy kpdev kuangcx kushalaa lijunbo lingyongxu liusheng liuyamin lkuchlan llmtech-dev luke.li maaoyu mark.sturdevant medhac1403 melakualehegn melissaml morozovalekseywot namrata nidhimittalhada paulali pengyuesheng qingszhao ricolin sdailey shu-mutou silvacarloss sneha.gaddam sonu.kumar sriram ramakrishnan sunjia ting.wang tpsilva tspyderboy venkatamahesh vik vkmc vponomaryov wangzhenyu wangzihao wu.chunyang wu.shiming wuchunyang xiaozhuangqing yanjun.fu yara yogesh yuyafei zhangboye zhiguo.li zhongjun zhongjun2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/CONTRIBUTING.rst0000664000175000017500000000121115221154414017035 0ustar00zuulzuulThe 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896961.0 python_manilaclient-6.2.0/ChangeLog0000664000175000017500000011056715221154501016162 0ustar00zuulzuulCHANGES ======= 6.2.0 ----- * added properties column to share replica list command 6.1.0 ----- * Replace OVN Metadata agent with OVN agent * Graduate share migration feature * Follow up on share replica metadata changes * fix stale API examble and typo in user/api.rst * update stale OpenStack references in README.rst * fix formatting errors in pyproject.toml * doc: fix share network docstrings * doc: Fix wrong class references in manager docstrings * doc: Update OS\_TENANT\_NAME to OS\_PROJECT\_NAME * Fix typo 'occured' should be 'occurred' * Fix option to override API endpoint * Update master for stable/2026.1 6.0.0 ----- * Handle single digit versions * Metadata for Share Replica * Add support for QoS type and specs (2.94) * Remove unused Makefile for doc * Support filtering services by ensuring field * fix share server get migration progress * Add mount\_point\_name option for manage shares * Switch auth to keystoneauth * Add requests-mock to test-requirements.txt * Remove the legacy "manila" CLI * Drop workaround for Python < 3.10 5.7.1 ----- * ruff: Use more specific name to enable pyupgrade rule * Remove dead code * Add missing api\_versions decorator for \_update\_share\_group * Add func test for share instance export locations * Test for share instances * Add a git-blame-ignore-revs file 5.7.0 ----- * reno: Update master for unmaintained/2024.1 * Remove unnecessary call * Re-enable E501 errors * Fix outstanding hacking issues * Fix outstanding ruff issues * Add ruff * pre-commit: Bump versions * Migrate setup configuration to pyproject.toml * Fix neutron validation in share network creation * fix respect region name in client initialization * Allow settings --task-state without value (to send null) or None * Add --wait flag to share-server-unmanage * Update master for stable/2025.2 * Fixes an issue that prevented ensure shares to be run 5.6.0 ----- * Add support for out of place share backup restores * Add support of encryption\_key\_ref option for share create * Add option to override API endpoint * Fixed negative user messages test names * Add negative functional test for message * Zuul: do not use USE\_PYTHON3 * Remove Python 3.9 support * pre-commit: Bump hacking and use native flake8 hook 5.5.0 ----- * [Doc] Address code block issue * Update master for stable/2025.1 5.4.0 ----- * bump osc-lib in requirements * Update microversion to 2.89 5.3.0 ----- * Run OSC plugin jobs * Add pre-commit * Remove unused Babel 5.2.0 ----- * reno: Update master for unmaintained/2023.1 * Allow to update access rule for type 'ip' * Fix broken exception handling on unit tests * Use time.sleep() instead of eventlet.sleep() * Add ensure shares command 5.1.0 ----- * Remove Python 3.8 support * Update master for stable/2024.2 * Metadata for share export location 5.0.0 ----- * Handle empty metadata properly in filter/list * Remove support for Identity v2 API * Add new filter options for share server list * Replace OS\_TENANT\_NAME 4.9.1 ----- * Remove unused simplejson * Don't set availability\_zone for 2.32 version of api * Fix show info about share network 4.9.0 ----- * reno: Update master for unmaintained/zed * Remove old excludes * adds bandit testing and Zuul CI job * Add "--wait" option for creating/promoting/deleting a share replicas * Remove --nova-net-id option * Added "--wait" option for creating/deleting a share group snapshot * "--wait" option added for snapshot create/revert/delete commands * "--wait" option added for managing/unmanaging a share snapshot * Human readable export location CLI changes * Update master for stable/2024.1 * reno: Update master for unmaintained/xena * reno: Update master for unmaintained/wallaby 4.8.0 ----- * Add new-note tox environment * 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 * [OSC] Fix output format for osc share subnet create * 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/HACKING0000664000175000017500000000434315221154414015374 0ustar00zuulzuulManila 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 """ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/LICENSE0000664000175000017500000002707515221154414015421 0ustar00zuulzuulCopyright (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. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.1290944 python_manilaclient-6.2.0/PKG-INFO0000644000175000017500000001250115221154502015471 0ustar00zuulzuulMetadata-Version: 2.4 Name: python-manilaclient Version: 6.2.0 Summary: Client library for OpenStack Shared File System Storage Author-email: OpenStack License: Apache-2.0 Project-URL: Homepage, https://docs.openstack.org/python-manilaclient/ Project-URL: Repository, https://opendev.org/openstack/python-manilaclient/ Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Requires-Python: >=3.10 Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: pbr>=3.0.0 Requires-Dist: oslo.config>=5.2.0 Requires-Dist: oslo.log>=3.36.0 Requires-Dist: oslo.serialization>=2.20.0 Requires-Dist: oslo.utils>=3.33.0 Requires-Dist: PrettyTable>=0.7.1 Requires-Dist: requests>=2.14.2 Requires-Dist: osc-lib>=3.2.0 Requires-Dist: keystoneauth1>=3.0.0 Requires-Dist: debtcollector>=1.2.0 Dynamic: license-file Dynamic: requires-dist ======================== 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 the OpenStack Manila API. You'll need to provide your OpenStack username and password. You can do this with the ``--os-username``, ``--os-password`` and ``--os-project-name`` params, but it's easier to just set them as environment variables:: export OS_USERNAME=foouser export OS_PASSWORD=barpass export OS_PROJECT_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/v3/ 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 v3 auth with http://example.com:5000/v3/ >>> from manilaclient.v2 import client >>> nt = client.Client("2", USER, PASS, PROJECT, 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/README.rst0000664000175000017500000000774015221154414016100 0ustar00zuulzuul======================== 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 the OpenStack Manila API. You'll need to provide your OpenStack username and password. You can do this with the ``--os-username``, ``--os-password`` and ``--os-project-name`` params, but it's easier to just set them as environment variables:: export OS_USERNAME=foouser export OS_PASSWORD=barpass export OS_PROJECT_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/v3/ 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 v3 auth with http://example.com:5000/v3/ >>> from manilaclient.v2 import client >>> nt = client.Client("2", USER, PASS, PROJECT, 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/bindep.txt0000664000175000017500000000071615221154414016407 0ustar00zuulzuul# 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] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0220873 python_manilaclient-6.2.0/doc/0000775000175000017500000000000015221154502015144 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/doc/requirements.txt0000664000175000017500000000025115221154414020430 0ustar00zuulzuulopenstackdocstheme>=2.2.1 # Apache-2.0 sphinx>=2.1.1 # BSD reno>=3.1.0 # Apache-2.0 sphinxcontrib-programoutput>=0.11 # BSD python-openstackclient>=3.12.0 # Apache-2.0 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0230875 python_manilaclient-6.2.0/doc/source/0000775000175000017500000000000015221154502016444 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0230875 python_manilaclient-6.2.0/doc/source/cli/0000775000175000017500000000000015221154502017213 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/doc/source/cli/decoder.rst0000664000175000017500000000115015221154414021351 0ustar00zuulzuul============================= OpenStackClient Mapping Guide ============================= .. note:: The legacy ``manila`` CLI has been removed. This guide is maintained for historical reference to help users upgrading from older versions. The following is a mapping between the legacy ``manila`` CLI client and OpenStackClient. Command options are only shown when necessary. .. only:: html .. csv-table:: :widths: 25, 25, 50 :file: osc/manila.csv :header-rows: 1 .. only:: pdf The mapping can be accessed `here `_ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0230875 python_manilaclient-6.2.0/doc/source/cli/osc/0000775000175000017500000000000015221154502017777 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/doc/source/cli/osc/manila.csv0000664000175000017500000003224615221154414021766 0ustar00zuulzuul# NOTE: The legacy "manila" CLI has been removed. This file is maintained # for historical reference and migration purposes. manila command,openstack command,command description --version,module list,List the client software version absolute-limits,share limits show --absolute,Print a list of absolute limits for a user 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 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0240877 python_manilaclient-6.2.0/doc/source/cli/osc/v2/0000775000175000017500000000000015221154502020326 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/doc/source/cli/osc/v2/index.rst0000664000175000017500000001104215221154414022167 0ustar00zuulzuulCommand 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 * ========= qos types ========= .. autoprogram-cliff:: openstack.share.v2 :command: share qos type * ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/doc/source/cli/osc_plugin_cli.rst0000664000175000017500000000365115221154414022745 0ustar00zuulzuul================================================ ``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_PROJECT_NAME=foobarproject export OS_AUTH_URL=http://... export OS_SHARE_API_VERSION=2.51 It is possible to use different Manila endpoint while using Openstack Client. In this case, you can use configuration option ``--os-endpoint-override`` or set the corresponding environment variable:: export OS_SHARED_FILE_SYSTEM_ENDPOINT_OVERRIDE=http://... 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/* ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/doc/source/conf.py0000664000175000017500000000704215221154414017750 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0250876 python_manilaclient-6.2.0/doc/source/contributor/0000775000175000017500000000000015221154502021016 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/doc/source/contributor/adding_release_notes.rst0000664000175000017500000001237115221154414025714 0ustar00zuulzuul.. _adding_release_notes: Release Notes ============= What are release notes? ~~~~~~~~~~~~~~~~~~~~~~~ Release notes are important for change management within manila. Since manila follows a release cycle with milestones, release notes provide a way for the community and users to quickly grasp what changes occurred within a development milestone. To the OpenStack release management and documentation teams, release notes are a way to compile changes per milestone. These notes are published on the `OpenStack Releases website `_. Automated tooling is built around ``releasenotes`` and they get appropriately handled per release milestone, including any back-ports to stable releases. What needs a release note? ~~~~~~~~~~~~~~~~~~~~~~~~~~ * Changes that impact an upgrade, most importantly, those that require a deployer to take some action while upgrading * A new feature is implemented * An existing feature is deprecated * An existing feature is removed * Behavior of an existing feature has changed in a discernible way to an end user or administrator * A security bug is fixed * New configuration option is added What does not need a release note? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * A code change that doesn't change the general behavior of any feature such as code refactor or logging changes. One case of this could be the removal of the python 2.x compatibility layer, i.e., the "six" library from code. * Functional or unit test coverage enhancement * Any change submitted with a justified TrivialFix flag added in the commit message * Adding or changing documentation within in-tree documentation guides How do I add a release note? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We use `Reno `_ to create and manage release notes. The new subcommand combines a random suffix with a "slug" value to make the new file with a unique name that is easy to identify again later. To create a release note for your change, use: .. code-block:: console $ reno new slug-goes-here If reno is not installed globally on your system, you can use a tox environment in manila: .. code-block:: console $ tox -e newnote -- slug-goes-here .. note:: When you are adding a bug-fix reno, name your file using the template: "bug--slug-goes-here". Then add the notes in ``yaml`` format in the file created. Pay attention to the type of section. The following are general sections to use: prelude General comments about the change. The prelude from all notes in a release are combined, in note order, to produce a single prelude introducing the release. features New features introduced issues A list of known issues with respect to the change being introduced. For example, if the new feature in the change is experimental or known to not work in some cases, it should be mentioned here. upgrade A list of upgrade notes in the release. Any removals that affect upgrades are to be noted here. deprecations Any features, APIs, configuration options that the change has deprecated. Deprecations are not removals. Deprecations suggest that there will be support for a certain timeline. Deprecation should allow time for users to make necessary changes for the removal to happen in a future release. It is important to note the timeline of deprecation in this section. critical A list of *fixed* critical bugs (descriptions only). security A list of *fixed* security issues (descriptions only). fixes A list of other *fixed* bugs (descriptions only). other Other notes that are important but do not fall into any of the given categories. :: --- prelude: > Replace this text with content to appear at the top of the section for this change. features: - List new features here, or remove this section. issues: - List known issues here, or remove this section. upgrade: - List upgrade notes here, or remove this section. deprecations: - List deprecation notes here, or remove this section critical: - Add critical notes here, or remove this section. security: - Add security notes here, or remove this section. fixes: - Add normal bug fixes here, or remove this section. other: - Add other notes here, or remove this section. Dos and Don'ts ~~~~~~~~~~~~~~ * Release notes need to be succinct. Short and unambiguous descriptions are preferred * Write in past tense, unless you are writing an imperative statement * Do not have blank sections in the file * Do not include code or links * Avoid special rst formatting unless absolutely necessary * Always prefer including a release note in the same patch * Release notes are not a replacement for developer/user/admin documentation * Release notes are not a way of conveying behavior of any features or usage of any APIs * Limit a release note to fewer than 2-3 lines per change per section * OpenStack prefers atomic changes. So remember that your change may need the fewest sections possible * General writing guidelines can be found `here `_ * Proofread your note. Pretend you are a user or a deployer who is reading the note after a milestone or a release has been cut ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/doc/source/contributor/contributing.rst0000664000175000017500000000531215221154414024262 0ustar00zuulzuul============================ 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/doc/source/contributor/functional-tests.rst0000664000175000017500000002113315221154414025054 0ustar00zuulzuulFunctional 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:: bash [[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:: bash 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:: bash 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:: bash # 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/doc/source/contributor/index.rst0000664000175000017500000000113615221154414022662 0ustar00zuulzuulContributing ============ Basic Information ----------------- .. toctree:: :maxdepth: 3 contributing Programming HowTos and Tutorials -------------------------------- .. toctree:: :maxdepth: 3 adding_release_notes 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/doc/source/index.rst0000664000175000017500000000163715221154414020316 0ustar00zuulzuulPython bindings to the OpenStack Manila API =========================================== This is a client for OpenStack Manila API. There's :doc:`a Python API ` (the :mod:`manilaclient` module) for programmatic access, and a command-line interface via the OpenStack CLI client. See :ref:`project-structure` for more information. You'll need credentials for an OpenStack cloud that implements the Manila API in order to use the manila client. Command-Line Reference ~~~~~~~~~~~~~~~~~~~~~~ Use the "openstack" CLI client to interact with the Manila API from the command line: .. toctree:: :maxdepth: 1 cli/osc_plugin_cli cli/decoder Using the python module ~~~~~~~~~~~~~~~~~~~~~~~ .. toctree:: :maxdepth: 2 user/api Contributing ~~~~~~~~~~~~ .. toctree:: :maxdepth: 2 contributor/index .. only:: html Indices and tables ~~~~~~~~~~~~~~~~~~ * :ref:`genindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0250876 python_manilaclient-6.2.0/doc/source/user/0000775000175000017500000000000015221154502017422 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/doc/source/user/api.rst0000664000175000017500000000410615221154414020730 0ustar00zuulzuulThe :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('2', $OS_USER_NAME, $OS_PASSWORD, $OS_PROJECT_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 (assumes 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. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0100868 python_manilaclient-6.2.0/etc/0000775000175000017500000000000015221154502015152 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0250876 python_manilaclient-6.2.0/etc/manilaclient/0000775000175000017500000000000015221154502017612 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/etc/manilaclient/README.manilaclient.conf0000664000175000017500000000021415221154414024054 0ustar00zuulzuulTo generate the sample manilaclient.conf file, run the following command from the top level of the manilaclient directory: tox -egenconfig ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0250876 python_manilaclient-6.2.0/etc/oslo-config-generator/0000775000175000017500000000000015221154502021355 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/etc/oslo-config-generator/manilaclient.conf0000664000175000017500000000014215221154414024663 0ustar00zuulzuul[DEFAULT] output_file = etc/manilaclient/manilaclient.conf.sample namespace = manilaclient.config ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0270877 python_manilaclient-6.2.0/manilaclient/0000775000175000017500000000000015221154502017037 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/__init__.py0000664000175000017500000000234115221154414021152 0ustar00zuulzuul# 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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/api_versions.py0000664000175000017500000003674115221154414022127 0ustar00zuulzuul# 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 from oslo_utils import strutils 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.96' MIN_VERSION = '2.0' DEPRECATED_VERSION = '1.0' _VERSIONED_METHOD_MAP = {} class APIVersion: """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 f"API Version Major: {self.ver_major}, Minor: {self.ver_minor}" def __repr__(self): if self.is_null(): return "" else: return f"" 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 f"{self.ver_major}.{self.ver_minor}" def get_major_version(self): return f"{self.ver_major}" class VersionedMethod: 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 ( f"Version Method {self.name}: min: {self.start_version}, " f"max: {self.end_version}" ) def __repr__(self): return f"" 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) if strutils.is_int_like(version_string): version_string = f"{version_string}.0" 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/base.py0000664000175000017500000004336715221154414020342 0ustar00zuulzuul# 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 oslo_utils import strutils 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.sha256( 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 = "{}-{}-cache".format(resource, cache_type.replace('_', '-')) path = os.path.join(cache_dir, filename) cache_attr = f"_{cache_type}_cache" try: setattr(self, cache_attr, open(path, mode)) except OSError: # 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, f"_{cache_type}_cache", None) if cache: try: cache.write(f"{val}\n") 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 = f"?{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 = f"No {self.resource_class.__name__} matching {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: """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(f"{k}={getattr(self, k)}" for k in reprkeys) return f"<{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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/client.py0000664000175000017500000000456315221154414020701 0ustar00zuulzuul# 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 '{}'. must be one of: {}".format( 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) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0290878 python_manilaclient-6.2.0/manilaclient/common/0000775000175000017500000000000015221154502020327 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/common/__init__.py0000664000175000017500000000000015221154414022430 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/common/_i18n.py0000664000175000017500000000215315221154414021622 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0290878 python_manilaclient-6.2.0/manilaclient/common/apiclient/0000775000175000017500000000000015221154502022277 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/common/apiclient/__init__.py0000664000175000017500000000000015221154414024400 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/common/apiclient/exceptions.py0000664000175000017500000003103015221154414025031 0ustar00zuulzuul# 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().__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().__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().__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 = f"{self.message} (HTTP {self.http_status})" if request_id: formatted_string += f" (Request-ID: {request_id})" super().__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().__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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/common/apiclient/utils.py0000664000175000017500000000541315221154414024016 0ustar00zuulzuul# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/common/cliutils.py0000664000175000017500000001773615221154414022551 0ustar00zuulzuul# 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().__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(f"'{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 += f'\n{k} = {v}' return data_string ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/common/constants.py0000664000175000017500000001043315221154414022720 0ustar00zuulzuul# 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', ) QOS_TYPE_SORT_KEY_VALUES = ( 'id', 'name', 'created_at', 'updated_at', ) 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' QOS_TYPE_VERSION = '2.94' SHARE_MIGRATION_GRADUATION_VERSION = '2.96' SHARE_MIGRATION_PRE_GRADUATION_VERSION = '2.95' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/common/httpclient.py0000664000175000017500000001616615221154414023073 0ustar00zuulzuul# 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 from time import sleep # noqa try: osprofiler_web = importutils.try_import("osprofiler.web") except ImportError: pass class HTTPClient: """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) # noqa: S113 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', f' -X {method}', f' {url}'] for element in headers: header = f' -H "{element}: {headers[element]}"' string_parts.append(header) if data: if "password" in data: data = strutils.mask_password(data) string_parts.append(f" -d '{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, }, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/config.py0000664000175000017500000002353715221154414020672 0ustar00zuulzuul# 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(f'OS_{PROJECT_NAME.upper()}_CONFIG_FILE') or f'{PROJECT_NAME}.conf' ) DEFAULT_CONFIG_DIR = os.environ.get( f'OS_{PROJECT_NAME.upper()}_CONFIG_DIR' ) 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 = f'/etc/{PROJECT_NAME}/{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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/exceptions.py0000664000175000017500000000344615221154414021603 0ustar00zuulzuul# 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 occurred.") 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} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/extension.py0000664000175000017500000000257315221154414021436 0ustar00zuulzuul# 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 f"" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1782896962.030088 python_manilaclient-6.2.0/manilaclient/osc/0000775000175000017500000000000015221154502017623 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/__init__.py0000664000175000017500000000000015221154414021724 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/plugin.py0000664000175000017500000001154515221154414021503 0ustar00zuulzuul# 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 = { f'2.{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] endpoint_override = getattr( instance._cli_options, 'os_endpoint_override', None ) if endpoint_override: manila_endpoint_url = endpoint_override service_type = constants.SFS_SERVICE_TYPE else: 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)', ) parser.add_argument( "--os-endpoint-override", metavar="", default=utils.env( "OS_SHARED_FILE_SYSTEM_ENDPOINT_OVERRIDE", "OS_ENDPOINT_OVERRIDE", "OS_MANILA_BYPASS_URL", "MANILACLIENT_BYPASS_URL", ), help=( "Use this API endpoint instead of the Service Catalog. " "Defaults to env[OS_SHARED_FILE_SYSTEM_ENDPOINT_OVERRIDE]." ), ) return parser ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/utils.py0000664000175000017500000000761015221154414021343 0ustar00zuulzuul# 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 = f"Following options were duplicated: {duplicate_str}" raise exceptions.CommandError(msg) return result_dict def format_properties(properties): formatted_data = [] for item in properties: formatted_data.append(f"{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( f"Argument '{key}' is specified twice." ) 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 = f"Argument '{key}' value specified twice." raise exceptions.CommandError(msg) elif key in bool_specs: if strutils.is_valid_boolstr(value): extra_specs[key] = value.capitalize() else: msg = ( f"Argument '{key}' is of boolean " f"type and has invalid value: {str(value)}" ) raise exceptions.CommandError(msg) else: extra_specs[key] = value except ValueError: msg = _("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 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0380886 python_manilaclient-6.2.0/manilaclient/osc/v2/0000775000175000017500000000000015221154502020152 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/__init__.py0000664000175000017500000000000015221154414022253 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/availability_zones.py0000664000175000017500000000246215221154414024422 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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 ), ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0380886 python_manilaclient-6.2.0/manilaclient/osc/v2/data/0000775000175000017500000000000015221154502021063 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/data/manila.csv0000664000175000017500000003224615221154414023052 0ustar00zuulzuul# NOTE: The legacy "manila" CLI has been removed. This file is maintained # for historical reference and migration purposes. manila command,openstack command,command description --version,module list,List the client software version absolute-limits,share limits show --absolute,Print a list of absolute limits for a user 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/messages.py0000664000175000017500000001540015221154414022335 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/qos_types.py0000664000175000017500000002707515221154414022567 0ustar00zuulzuul# Copyright (c) 2025 Cloudification GmbH. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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.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', 'description', 'specs', 'created_at', 'updated_at', ] def format_qos_type(qos_type, formatter='table'): specs = qos_type.specs if formatter == 'table': qos_type._info.update({'specs': utils.format_properties(specs)}) else: qos_type._info.update({'specs': specs}) return qos_type class CreateQosType(command.ShowOne): """Create new qos type.""" _description = _("Create new qos type") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar="", help=_('QoS type name. This must be unique.'), ) parser.add_argument( "--description", metavar="", default=None, help=_("QoS type description."), ) parser.add_argument( "--spec", type=str, metavar='', action='append', default=None, help=_( "Spec key and value of QoS type that will be" " used for QoS type creation. OPTIONAL: Default=None." " Example: --spec qos_type='fixed' --spec peak_iops=300." ), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share kwargs = {'name': parsed_args.name} if parsed_args.description: kwargs['description'] = parsed_args.description if parsed_args.spec: specs = utils.extract_properties(parsed_args.spec) kwargs['specs'] = specs qos_type = share_client.qos_types.create(**kwargs) formatted_type = format_qos_type(qos_type, parsed_args.formatter) return ( ATTRIBUTES, oscutils.get_dict_properties(formatted_type._info, ATTRIBUTES), ) class DeleteQosType(command.Command): """Delete a qos type.""" _description = _("Delete a qos type") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( 'qos_types', metavar="", nargs="+", help=_("Name or ID of the qos type(s) to delete"), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share result = 0 for qos_type in parsed_args.qos_types: try: qos_type_obj = apiutils.find_resource( share_client.qos_types, qos_type ) share_client.qos_types.delete(qos_type_obj) except Exception as e: result += 1 LOG.error( _( "Failed to delete qos type with " "name or ID '%(qos_type)s': %(e)s" ), {'qos_type': qos_type, 'e': e}, ) if result > 0: total = len(parsed_args.qos_types) msg = _("%(result)s of %(total)s qos types failed to delete.") % { 'result': result, 'total': total, } raise exceptions.CommandError(msg) class SetQosType(command.Command): """Set qos type description or specs.""" _description = _("Set qos type description or specs") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( 'qos_type', metavar="", help=_("Name or ID of the qos type to modify"), ) parser.add_argument( "--description", metavar="", default=None, help=_("New description of qos type."), ) parser.add_argument( "--spec", type=str, metavar='', action='append', default=None, help=_( "Spec key and value of qos type that will be " "used for QoS type. OPTIONAL: Default=None. For " "example --spec qos_type='fixed' --spec peak_iops=300" ), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share qos_type = apiutils.find_resource( share_client.qos_types, parsed_args.qos_type ) kwargs = {} if parsed_args.description: kwargs['description'] = parsed_args.description if kwargs: try: qos_type.update(**kwargs) except Exception as e: raise exceptions.CommandError( _("Failed to set qos type description: %s") % e ) # These are dict of key=value to be added as qos type specs. if parsed_args.spec: specs = utils.extract_properties(parsed_args.spec) try: qos_type.set_keys(specs) except Exception as e: raise exceptions.CommandError( _("Failed to set qos type spec: %s") % e ) class UnsetQosType(command.Command): """Unset qos type specs.""" _description = _("Unset qos type specs") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( 'qos_type', metavar="", help=_("Name or ID of the qos type to modify"), ) parser.add_argument( "--description", action='store_true', help=_("Unset qos type description."), ) parser.add_argument( '--spec', metavar='', action='append', help=_('Remove specified spec from this qos type'), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share qos_type = apiutils.find_resource( share_client.qos_types, parsed_args.qos_type ) kwargs = {} if parsed_args.description: kwargs['description'] = None if kwargs: try: qos_type.update(**kwargs) except Exception as e: raise exceptions.CommandError( _("Failed to unset qos type description: %s") % e ) # These are list of keys to be deleted from qos type specs. if parsed_args.spec: try: qos_type.unset_keys(parsed_args.spec) except Exception as e: raise exceptions.CommandError( _("Failed to remove qos type spec: %s") % e ) class ListQosType(command.Lister): """List Qos Types.""" _description = _("List qos types") def get_parser(self, prog_name): parser = super().get_parser(prog_name) 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 qos name pattern. "), ) parser.add_argument( '--description~', metavar="", default=None, help=_("Filter results matching a qos description pattern."), ) parser.add_argument( "--limit", metavar="", type=int, default=None, action=parseractions.NonNegativeAction, help=_("Limit the number of qos types returned. Default=None."), ) parser.add_argument( '--offset', metavar="", default=None, help=_('Start position of qos type records listing.'), ) parser.add_argument( '--sort-key', '--sort_key', metavar='', type=str, default=None, help=_( 'Key to be sorted with, available keys are ' '%(keys)s. Default=None.' ) % {'keys': constants.QOS_TYPE_SORT_KEY_VALUES}, ) parser.add_argument( '--sort-dir', '--sort_dir', metavar='', type=str, default=None, help=_( 'Sort direction, available values are ' '%(dirs)s. OPTIONAL: Default=None.' ) % {'dirs': constants.SORT_DIR_VALUES}, ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share search_opts = { 'name': parsed_args.name, 'description': parsed_args.description, 'limit': parsed_args.limit, 'offset': parsed_args.offset, } search_opts['name~'] = getattr(parsed_args, 'name~') search_opts['description~'] = getattr(parsed_args, 'description~') qos_types = share_client.qos_types.list( search_opts=search_opts, sort_key=parsed_args.sort_key, sort_dir=parsed_args.sort_dir, ) formatted_types = [] for qos_type in qos_types: formatted_types.append( format_qos_type(qos_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 ShowQosType(command.ShowOne): """Show a qos type.""" _description = _("Display qos type details") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( 'qos_type', metavar="", help=_("Qos type to display (name or ID)"), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share qos_type = apiutils.find_resource( share_client.qos_types, parsed_args.qos_type ) formatted_type = format_qos_type(qos_type, parsed_args.formatter) return ( ATTRIBUTES, oscutils.get_dict_properties(formatted_type._info, ATTRIBUTES), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/quotas.py0000664000175000017500000004037315221154414022051 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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( '--encryption-keys', metavar='', type=int, default=None, help=_( 'New value for the "encryption-keys" quota. ' 'Available only for microversion >= 2.90' ), ) 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 parsed_args.encryption_keys is not None: if share_client.api_version < api_versions.APIVersion('2.90'): raise exceptions.CommandError( _( "'encryption keys' quotas are available only " "starting with '2.90' API microversion." ) ) kwargs["encryption_keys"] = parsed_args.encryption_keys 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', 'encryption_keys'" ) ) if parsed_args.quota_class: kwargs.update( { "class_name": parsed_args.project, } ) try: share_client.quota_classes.update(**kwargs) except Exception as e: msg = _( "Failed to set quotas for class '%(project)s': '%(e)s'" ) raise exceptions.CommandError( msg % {'project': parsed_args.project, 'e': 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: msg = _( "Failed to set quotas for project '%(project)s': '%(e)s'" ) raise exceptions.CommandError( msg % {'project': parsed_args.project, 'e': 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().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( [f'{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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/resource_locks.py0000664000175000017500000003344515221154414023561 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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=( f'Key to be sorted, available keys are ' f'{constants.RESOURCE_LOCK_SORT_KEY_VALUES}. Default=None.' ), ) parser.add_argument( '--sort-dir', '--sort_dir', metavar='', type=str, default=None, choices=constants.SORT_DIR_VALUES, help=( f'Sort direction, available values are ' f'{constants.SORT_DIR_VALUES}. OPTIONAL: Default=None.' ), ) 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 identity_client = self.app.client_manager.identity 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().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().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().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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/security_services.py0000664000175000017500000005077615221154414024317 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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 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().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().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().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().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), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/services.py0000664000175000017500000001646415221154414022364 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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: msg = _("Failed to enable service: %(e)s") raise exceptions.CommandError(msg % {'e': 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: msg = _("Failed to disable service: %(e)s") raise exceptions.CommandError(msg % {'e': e}) class ListShareService(command.Lister): """List share services (Admin only).""" _description = _("List share services (Admin only).") def get_parser(self, prog_name): parser = super().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."), ) parser.add_argument( "--ensuring", metavar="", choices=['True', 'False'], default=None, help=_("Filter services running ensure shares or not."), ) 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, } if parsed_args.ensuring: if share_client.api_version < api_versions.APIVersion("2.93"): raise exceptions.CommandError( "Filtering services whether they are running ensure " "shares or not is only supported by manila API version " ">= 2.93" ) search_opts['ensuring'] = parsed_args.ensuring 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') if share_client.api_version >= api_versions.APIVersion("2.93"): columns.append('Ensuring') data = ( osc_utils.get_dict_properties(service._info, columns) for service in services ) return (columns, data) class EnsureShareService(command.Command): """Run ensure shares in a back end (Admin only).""" _description = _("Run ensure shares in a back end (Admin only).") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( 'host', metavar='', help=_( "Host to run ensure shares. 'example_host@example_backend'." ), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share if share_client.api_version < api_versions.APIVersion("2.86"): raise exceptions.CommandError( "Ensure shares API is only available in " "manila API version >= 2.86" ) try: share_client.services.ensure_shares(parsed_args.host) except Exception as e: msg = _("Failed to ensure shares: %(e)s") raise exceptions.CommandError(msg % {'e': e}) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share.py0000664000175000017500000016745415221154414021651 0ustar00zuulzuul# 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', 'encryption_key_ref', ] 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', 'Encryption Key Ref', ] class CreateShare(command.ShowOne): """Create a new share.""" _description = _("Create new share") def get_parser(self, prog_name): parser = super().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)" ), ) parser.add_argument( '--mount-point-name', metavar="", default=None, help=_( 'Optional custom export location. Available for ' 'microversion >= 2.84' ), ) parser.add_argument( '--encryption-key-ref', metavar="", default=None, help=_( 'Set encryption key reference i.e. UUID of the secret ' 'stored in the key manager. Available for ' 'microversion >= 2.90' ), ) 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) mount_point_name = None if parsed_args.mount_point_name: if share_client.api_version < api_versions.APIVersion('2.84'): raise exceptions.CommandError( 'Setting share mount point name is ' 'available only for API microversion >= 2.84' ) else: mount_point_name = parsed_args.mount_point_name encryption_key_ref = None if parsed_args.encryption_key_ref: if share_client.api_version < api_versions.APIVersion('2.90'): raise exceptions.CommandError( 'Setting share encryption key reference is ' 'available only for API microversion >= 2.90' ) else: encryption_key_ref = parsed_args.encryption_key_ref 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, 'mount_point_name': mount_point_name, 'encryption_key_ref': encryption_key_ref, } 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().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().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." ), ) parser.add_argument( '--encryption-key-ref', metavar="", help=_( 'Filter shares by their encryption key ref. ' 'Available for microversion >= 2.90' ), ) 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" ) if share_client.api_version >= api_versions.APIVersion('2.90'): search_opts['encryption_key_ref'] = parsed_args.encryption_key_ref elif getattr(parsed_args, 'encryption_key_ref'): raise exceptions.CommandError( "Filtering shares by encryption key ref is only " "available with manila API version >= 2.90" ) # 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().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().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, nargs='?', 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 hasattr(parsed_args, 'task_state'): task_state = parsed_args.task_state if task_state and task_state.lower() == "none": task_state = None try: share_obj.reset_task_state(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().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().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: msg = _("Share resize failed: %(e)s") raise exceptions.CommandError(msg % {'e': 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: msg = _("Share resize failed: %(e)s") raise exceptions.CommandError(msg % {'e': e}) else: msg = _("Share size is already at %(new_size)s GiBs") raise exceptions.CommandError(msg % {'new_size': 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().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( '--mount-point-name', metavar="", default=None, help=_( 'Optional custom export location. Available only for ' 'microversion >= 2.92. (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' ) if parsed_args.mount_point_name: if share_client.api_version >= api_versions.APIVersion("2.92"): kwargs['mount_point_name'] = parsed_args.mount_point_name else: raise exceptions.CommandError( 'Setting share mount point name is available only for ' 'API microversion >= 2.92' ) 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().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().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 ) data = export_location._info data.update( { 'properties': format_columns.DictColumn( data.pop('metadata', {}) ), }, ) return self.dict2columns(data) 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().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 ShareExportLocationSet(command.Command): """Set an export location property.""" _description = _("Set an export location property.") def get_parser(self, prog_name): parser = super().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 export location'), ) parser.add_argument( "--property", metavar="", default={}, action=parseractions.KeyValueAction, help=_( "Set a property to this export location " "(repeat option to set multiple properties). " "Available only for microversion >= 2.87." ), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_id = apiutils.find_resource( share_client.shares, parsed_args.share ).id if ( parsed_args.property and share_client.api_version < api_versions.APIVersion("2.87") ): raise exceptions.CommandError( "Property can be specified only with manila API " "version >= 2.87." ) if parsed_args.property: try: share_client.share_export_locations.set_metadata( share_id, parsed_args.property, subresource=parsed_args.export_location, ) except Exception as e: raise exceptions.CommandError( _( "Failed to set export location property " "'%(properties)s': %(e)s" ) % {'properties': parsed_args.property, 'e': e} ) class ShareExportLocationUnset(command.Command): """Unset a share export location property""" _description = _("Unset a share export location property") def get_parser(self, prog_name): parser = super().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 export location'), ) parser.add_argument( "--property", metavar="", action='append', help=_( "Remove a property from export location " "(repeat option to remove multiple properties). " "Available only for microversion >= 2.87." ), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share share_id = apiutils.find_resource( share_client.shares, parsed_args.share ).id if ( parsed_args.property and share_client.api_version < api_versions.APIVersion("2.87") ): raise exceptions.CommandError( "Property can be specified only with manila API " "version >= 2.87." ) if parsed_args.property: result = 0 for key in parsed_args.property: try: share_client.share_export_locations.delete_metadata( share_id, [key], subresource=parsed_args.export_location, ) except Exception as e: result += 1 LOG.error( "Failed to unset export location property " "'%(key)s': %(e)s", {'key': key, 'e': e}, ) if result > 0: total = len(parsed_args.property) raise exceptions.CommandError( f"{result} of {total} export location properties failed " f"to be unset." ) class ShowShareProperties(command.ShowOne): """Show properties of a share""" _description = _("Show share properties") def get_parser(self, prog_name): parser = super().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().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: msg = _("Failed to revert share to snapshot: %(e)s") raise exceptions.CommandError(msg % {'e': 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().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().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().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().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().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: msg = _( "Failed to restore %(failure_count)s out of %(total)s " "shares." ) raise exceptions.CommandError( msg % { 'failure_count': failure_count, 'total': len(parsed_args.share), }, ) else: raise exceptions.CommandError( "Restoring a share from the recycle bin is only " "available with manila API version >= 2.69" ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_access_rules.py0000664000175000017500000004562415221154414024376 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.common._i18n import _ from manilaclient.common.apiclient import utils as apiutils from manilaclient.osc import utils ACCESS_RULE_ATTRIBUTES = [ 'id', 'share_id', 'access_level', 'access_to', 'access_type', 'state', 'access_key', 'created_at', 'updated_at', 'properties', ] LOG = logging.getLogger(__name__) class ShareAccessAllow(command.ShowOne): """Create a new share access rule.""" _description = _("Create new share access rule") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( 'share', metavar="", 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( f"Failed to create access to share '{share}': {e}" ) class ShareAccessDeny(command.Command): """Delete a share access rule.""" _description = _("Delete a share access rule") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( 'share', metavar="", 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 try: share.deny(parsed_args.id, **kwargs) if parsed_args.wait: if not oscutils.wait_for_delete( manager=share_client.share_access_rules, res_id=parsed_args.id, ): raise Exception('timed out while waiting for deletion') except Exception as e: msg = _( "Failed to delete share access rule %(id)s for share " "%(share)s: %(e)s" ) raise exceptions.CommandError( msg % {'id': parsed_args.id, 'share': parsed_args.share, 'e': e} ) class ListShareAccess(command.Lister): """List share access rules.""" _description = _("List share access rule") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( 'share', metavar="", 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().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().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.' ), ) parser.add_argument( "--access-level", metavar="", default=None, choices=['rw', 'ro'], help=_( 'Share access level ("rw" and "ro" access levels ' 'are supported) to set.' ), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share if ( parsed_args.property and share_client.api_version < api_versions.APIVersion("2.45") ): raise exceptions.CommandError( "Setting properties to access rule is supported only " "with API microversion 2.45 and higher" ) if ( parsed_args.access_level and share_client.api_version < api_versions.APIVersion("2.88") ): raise exceptions.CommandError( "Setting access level to access rule is supported only " "with API microversion 2.88 and higher" ) access_rule = share_client.share_access_rules.get( parsed_args.access_id ) if parsed_args.property: try: share_client.share_access_rules.set_metadata( access_rule, parsed_args.property ) except Exception as e: raise exceptions.CommandError( "Failed to set properties to share access rule with ID " f"'{access_rule.id}': {e}" ) if parsed_args.access_level: try: share_client.share_access_rules.set_access_level( access_rule, parsed_args.access_level ) except Exception as e: raise exceptions.CommandError( "Failed to set access level to share access rule with ID " f"'{access_rule.id}': {e}" ) class UnsetShareAccess(command.Command): """Unset properties of share access rule.""" _description = _( "Unset properties of share access rule. " "Available for API microversion 2.45 and higher" ) def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( 'access_id', metavar="", 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 " f"with ID '{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" ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_backups.py0000664000175000017500000003663415221154414023354 0ustar00zuulzuul# 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 import api_versions 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().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().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().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=( f'Key to be sorted, available keys are ' f'{constants.BACKUP_SORT_KEY_VALUES}. Default=None.' ), ) parser.add_argument( '--sort-dir', '--sort_dir', metavar='', type=str, default=None, help=( f'Sort direction, available values are ' f'{constants.SORT_DIR_VALUES}. OPTIONAL: Default=None.' ), ) 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().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().get_parser(prog_name) parser.add_argument( "backup", metavar="", help=_('ID of backup to restore.') ) parser.add_argument( "--target-share", metavar="", default=None, help=_( 'share to restore backup to. Source share if none supplied' ), ) parser.add_argument( '--wait', action='store_true', default=False, help=_('Wait for restore conclusion'), ) return parser def take_action(self, parsed_args): share_client = self.app.client_manager.share kwargs = {} share_backup = osc_utils.find_resource( share_client.share_backups, parsed_args.backup ) target_share_id = None if parsed_args.target_share is not None: if share_client.api_version < api_versions.APIVersion('2.91'): raise exceptions.CommandError( 'performing targeted restores is only available ' 'for API microversion >= 2.91' ) else: target_share_id = osc_utils.find_resource( share_client.shares, parsed_args.target_share ).id kwargs['target_share_id'] = target_share_id share_client.share_backups.restore(share_backup.id, **kwargs) if parsed_args.wait: if not osc_utils.wait_for_status( status_f=share_client.shares.get, res_id=(target_share_id or share_backup.share_id), success_status=['available'], error_status=['error', 'backup_restoring_error'], ): LOG.error(_("ERROR: share is in error state.")) class SetShareBackup(command.Command): """Set share backup properties.""" _description = _("Set share backup properties") def get_parser(self, prog_name): parser = super().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().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}, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_group_snapshots.py0000664000175000017500000003504115221154414025151 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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().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().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().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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_group_type_access.py0000664000175000017500000001450415221154414025432 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_group_types.py0000664000175000017500000002715115221154414024276 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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 = _( "Failed to find the share type with name or ID " "'%(share_type)s': %(e)s" ) raise exceptions.CommandError( msg % {'share_type': share_type, 'e': e} ) 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().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().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().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().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 = _( "Failed to find the share group type with " "name or ID '%(share_group_type)s': %(e)s" ) raise exceptions.CommandError( msg % {'share_group_type': parsed_args.share_group_type, 'e': e} ) 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( f"Failed to set share group type key: {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().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 = _( "Failed to find the share group type with " "name or ID '%(share_group_type)s': %(e)s" ) raise exceptions.CommandError( msg % {'share_group_type': parsed_args.share_group_type, 'e': e} ) if parsed_args.group_specs: try: share_group_type_obj.unset_keys(parsed_args.group_specs) except Exception as e: raise exceptions.CommandError( f"Failed to remove share type group extra spec: {e}" ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_groups.py0000664000175000017500000004475415221154414023245 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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 = osc_utils.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().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().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().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: msg = _( "Failed to unset share_group name or description: %(e)s" ) raise exceptions.CommandError(msg % {'e': e}) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_instance_export_locations.py0000664000175000017500000000563515221154414027201 0ustar00zuulzuul# 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().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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_instances.py0000664000175000017500000001716415221154414023710 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_limits.py0000664000175000017500000000422015221154414023207 0ustar00zuulzuul# 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().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), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_network_subnets.py0000664000175000017500000003500515221154414025147 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 _ from manilaclient.common import cliutils 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().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." ) neutron_client = getattr(self.app.client_manager, 'network', None) neutron_net_id = parsed_args.neutron_net_id neutron_subnet_id = parsed_args.neutron_subnet_id if xor(bool(neutron_net_id), bool(neutron_subnet_id)): raise exceptions.CommandError( "Both neutron_net_id and neutron_subnet_id should be " "specified. Alternatively, neither of them should be " "specified." ) if neutron_client and neutron_net_id: try: neutron_net_id = neutron_client.find_network( neutron_net_id, ignore_missing=False ).id except Exception: raise exceptions.CommandError( f"Neutron network '{neutron_net_id}' not found." ) if neutron_client and neutron_subnet_id: try: neutron_subnet_id = neutron_client.find_subnet( neutron_subnet_id, ignore_missing=False ).id except Exception: raise exceptions.CommandError( f"Neutron subnet '{neutron_subnet_id}' not found." ) 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=neutron_net_id, neutron_subnet_id=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] if subnet_data: if parsed_args.formatter == 'table': for k, v in subnet_data.items(): if isinstance(v, dict): capabilities_list = [v] dict_values = cliutils.convert_dict_list_to_string( capabilities_list ) subnet_data[k] = dict_values else: share_network_subnet = share_client.share_network_subnets.create( neutron_net_id=neutron_net_id, neutron_subnet_id=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().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().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().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().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." ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_networks.py0000664000175000017500000007256715221154414023605 0ustar00zuulzuul# 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().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().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 if 'share_network_subnets' not in data: data['share_network_subnets'] = [] # 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().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 neutron_client = getattr(self.app.client_manager, 'network', None) neutron_net_id = parsed_args.neutron_net_id neutron_subnet_id = parsed_args.neutron_subnet_id if neutron_client and neutron_net_id: try: neutron_net_id = neutron_client.find_network( neutron_net_id, ignore_missing=False ).id except Exception: raise exceptions.CommandError( f"Neutron network '{parsed_args.neutron_net_id}'" f" not found." ) if neutron_client and neutron_subnet_id: try: neutron_subnet_id = neutron_client.find_subnet( neutron_subnet_id, ignore_missing=False ).id except Exception: raise exceptions.CommandError( f"Neutron subnet '{parsed_args.neutron_subnet_id}'" f" not found." ) 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 kwargs = { "name": parsed_args.name, "description": parsed_args.description, "neutron_net_id": neutron_net_id, "neutron_subnet_id": neutron_subnet_id, } if availability_zone: kwargs['availability_zone'] = availability_zone share_network = share_client.share_networks.create(**kwargs) share_network_data = share_network._info if 'share_network_subnets' not in share_network_data: share_network_data['share_network_subnets'] = [] 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().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().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 {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( # noqa: E501 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().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 {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") ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_pools.py0000664000175000017500000000754115221154414023053 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_replica_export_locations.py0000664000175000017500000000523715221154414027012 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_replicas.py0000664000175000017500000005171715221154414023525 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 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().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' ), ) parser.add_argument( "--property", metavar="", default={}, action=parseractions.KeyValueAction, help=_( "Set a property to this replica " "(repeat option to set multiple properties). " "Available only for microversion >= 2.95" ), ) 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, } metadata = {} if parsed_args.property: if share_client.api_version < api_versions.APIVersion("2.95"): raise exceptions.CommandError( _( "arg '--property' is available only starting " "with API microversion '2.95'." ) ) metadata = parsed_args.property if metadata: body['metadata'] = metadata 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().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().get_parser(prog_name) parser.add_argument( "--share", metavar="", default=None, help=_("Name or ID of the share to list replicas for."), ) parser.add_argument( '--property', metavar='', action=parseractions.KeyValueAction, help=_( 'Filter replicas having a given metadata key=value ' 'property. (repeat option to filter by multiple ' 'properties)' ), ) 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 ) properties = parsed_args.property or {} if properties: if share_client.api_version < api_versions.APIVersion("2.95"): raise exceptions.CommandError( "Property based filtering is only available " "with manila API version >= 2.95" ) search_opts = {} if properties: meta_str = ",".join(f"{k}:{v}" for k, v in properties.items()) search_opts['metadata'] = meta_str replicas = share_client.share_replicas.list( share=share, search_opts=search_opts ) if properties: replicas = [ r for r in replicas if all( r._info.get('metadata', {}).get(k) == v for k, v in properties.items() ) ] columns = [ 'id', 'status', 'replica_state', 'share_id', 'host', 'availability_zone', 'updated_at', ] if share_client.api_version >= api_versions.APIVersion("2.95"): for replica in replicas: replica._info.update( { 'properties': format_columns.DictColumn( replica._info.pop('metadata', {}) ), }, ) columns += ['properties'] 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().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'] ) ) # Special mapping for columns to make the output easier to read: # 'metadata' --> 'properties' replica._info.update( { 'properties': format_columns.DictColumn( replica._info.pop('metadata', {}) ), }, ) 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 " "and/or property" ) def get_parser(self, prog_name): parser = super().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." ), ) parser.add_argument( "--property", metavar="", default={}, action=parseractions.KeyValueAction, help=_( "Set a property to this replica " "(repeat option to set multiple properties). " "Available only for microversion >= 2.95" ), ) 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 parsed_args.property: if share_client.api_version < api_versions.APIVersion("2.95"): raise exceptions.CommandError( _( "Setting properties in share replicas is available " "only starting with API microversion '2.95'." ) ) try: replica.set_metadata(parsed_args.property) except Exception as e: LOG.error( _( "Failed to set share replica properties " "'%(properties)s': %(exception)s" ), {'properties': parsed_args.property, 'exception': e}, ) result += 1 if ( not parsed_args.replica_state and not parsed_args.status and not parsed_args.property ): raise exceptions.CommandError( _( "Nothing to set. Please define " "either '--replica_state' or '--status' or '--property'." ) ) if result > 0: raise exceptions.CommandError( _("One or more of the set operations failed") ) class UnsetShareReplica(command.Command): """Unset a share replica property.""" _description = _("Unset a share replica property") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( "replica", metavar="", help=_("ID of the share replica to unset property from"), ) parser.add_argument( '--property', metavar='', action='append', help=_( 'Remove a property from replica ' '(repeat option to remove multiple properties)' ), ) 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 ) if parsed_args.property: if share_client.api_version < api_versions.APIVersion("2.95"): raise exceptions.CommandError( _( "arg '--property' is available only starting " "with API microversion '2.95'." ) ) result = 0 for key in parsed_args.property: try: replica.delete_metadata([key]) except Exception as e: result += 1 LOG.error( _( "Failed to unset replica property " "'%(key)s': %(exception)s" ), {'key': key, 'exception': e}, ) if result > 0: raise exceptions.CommandError( _("One or more of the unset operations failed") ) else: raise exceptions.CommandError( "Please specify '--property ' to unset a property." ) class PromoteShareReplica(command.Command): """Promote share replica""" _description = _("Promote specified replica to 'active' replica_state.") def get_parser(self, prog_name): parser = super().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' ), ) parser.add_argument( '--wait', action='store_true', default=False, help=_('Wait for share replica promotion'), ) 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) if parsed_args.wait: if not osc_utils.wait_for_status( status_f=share_client.share_replicas.get, res_id=replica.id, success_status=['active'], status_field='replica_state', ): LOG.error(_("ERROR: Share replica is in error state.")) except Exception as e: msg = "Failed to promote replica to 'active': %(e)s" raise exceptions.CommandError(msg % {'e': 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().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: msg = "Failed to resync share replica: %(e)s" raise exceptions.CommandError(msg % {'e': e}) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_servers.py0000664000175000017500000006274415221154414023416 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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: msg = "Failed to delete %(result)d servers out %(total)d" raise exceptions.CommandError( msg % {'result': result, 'total': len(parsed_args.share_servers)}, ) 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().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().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)" ), ) parser.add_argument( '--source-share-server-id', metavar='', type=str, default=None, help=_( "Share server ID to be used as a filter. Available for " "microversion >= 2.57 (Optional, Default=None)" ), ) parser.add_argument( '--identifier', metavar='', type=str, default=None, help=_( "Identifier of the share server in the share back end. " "Available for microversion >= 2.49 " "(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.identifier and share_client.api_version < api_versions.APIVersion("2.49") ): raise exceptions.CommandError( "Filtering by identifier is only allowed with manila API " "version >= 2.49." ) 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" ) if ( parsed_args.source_share_server_id and share_client.api_version < api_versions.APIVersion("2.57") ): raise exceptions.CommandError( "Filtering by source_share_server_id is only allowed with " "manila API version >= 2.57." ) 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.source_share_server_id: search_opts['source_share_server_id'] = ( parsed_args.source_share_server_id ) if parsed_args.identifier: search_opts['identifier'] = parsed_args.identifier 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().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().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: msg = _('Failed to abandon %(result)s of %(total)s servers.') raise exceptions.CommandError( msg % {'result': result, 'total': len(parsed_args.share_server)} ) class SetShareServer(command.Command): """Set share server properties.""" _description = _("Set share server properties (Admin only).") def get_parser(self, 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 = super().get_parser(prog_name) 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, nargs='?', 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 hasattr(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: task_state = parsed_args.task_state if task_state and task_state.lower() == "none": task_state = None result = 0 try: share_client.share_servers.reset_task_state( share_server, 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().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 share server (Admin only, Experimental).""" _description = _("Completes migration for a given share server") def get_parser(self, prog_name): parser = super().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().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"): result = share_client.share_servers.migration_get_progress( parsed_args.share_server ) 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().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" ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_snapshot_instance_export_locations.py0000664000175000017500000000620215221154414031107 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_snapshot_instances.py0000664000175000017500000001212715221154414025621 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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: %(e)s") raise exceptions.CommandError(msg % {'e': e}) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_snapshots.py0000664000175000017500000007371515221154414023747 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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().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().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: msg = _( "Failed to unset snapshot display name or display " "description: %(e)s" ) raise exceptions.CommandError(msg % {'e': e}) if parsed_args.property: for key in parsed_args.property: try: share_snapshot.delete_metadata([key]) except Exception as e: msg = _( "Failed to unset snapshot property '%(key)s': %(e)s" ) raise exceptions.CommandError(msg % {'key': key, 'e': e}) class ListShareSnapshot(command.Lister): """List snapshots.""" _description = _("List snapshots") def get_parser(self, prog_name): parser = super().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(f"Total number of snapshots: {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().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().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().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 " f"'{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().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().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().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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_transfers.py0000664000175000017500000002225715221154414023727 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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=( f'Key to be sorted, available keys are ' f'{constants.SHARE_TRANSFER_SORT_KEY_VALUES}. ' f'Default=None.' ), ) parser.add_argument( '--sort-dir', '--sort_dir', metavar='', type=str, default=None, help=( f'Sort direction, available values are ' f'{constants.SORT_DIR_VALUES}. ' f'OPTIONAL: Default=None.' ), ) 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().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().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, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_type_access.py0000664000175000017500000000757315221154414024226 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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( f"Failed to add access to share type : {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().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().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( f"Failed to remove access from share type : {e}" ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/osc/v2/share_types.py0000664000175000017500000004045215221154414023061 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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 " f"argument is not valid: {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().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().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( f"Failed to set share type key: {e}" ) class UnsetShareType(command.Command): """Unset share type extra specs.""" _description = _("Unset share type extra specs") def get_parser(self, prog_name): parser = super().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( f"Failed to remove share type extra spec: {e}" ) class ListShareType(command.Lister): """List Share Types.""" _description = _("List share types") def get_parser(self, prog_name): parser = super().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().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), ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0380886 python_manilaclient-6.2.0/manilaclient/tests/0000775000175000017500000000000015221154502020201 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/__init__.py0000664000175000017500000000000015221154414022302 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0390885 python_manilaclient-6.2.0/manilaclient/tests/functional/0000775000175000017500000000000015221154502022343 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/__init__.py0000664000175000017500000000000015221154414024444 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/exceptions.py0000664000175000017500000000442315221154414025103 0ustar00zuulzuul# 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." ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1782896962.045089 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/0000775000175000017500000000000015221154502023127 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/__init__.py0000664000175000017500000000000015221154414025230 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/base.py0000664000175000017500000004473015221154414024425 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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( f'{object_name} show -c status -f value {object_id}' ).rstrip() ): break time.sleep(interval) else: self.fail( f"{object_name} {object_id} did not reach status {status} " f"after {timeout} seconds." ) def check_object_deleted( self, object_name, object_id, timeout=CONF.build_timeout, interval=CONF.build_interval, ): """Check that object deleted successfully""" cmd = f'{object_name} list -c ID -f value' 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( f"{object_name} {object_id} not deleted after {timeout} " f"seconds." ) 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 + f'--os-share-api-version {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} {size} {name} {desc} {public} {stype}'.format( protocol=share_protocol or 'NFS', size=size or '1', name=f'--name {name}', desc=f'--description {description}', public=f'--public {public}', stype=f'--share-type {share_type}', ) if snapshot_id: cmd = cmd + f' --snapshot-id {snapshot_id}' if properties: for key, value in properties.items(): cmd = cmd + f' --property {key}={value}' if share_network: cmd = cmd + f' --share-network {share_network}' if availability_zone: cmd = cmd + f' --availability-zone {availability_zone}' if share_group: cmd = cmd + f' --share-group {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 {} --wait'.format(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 += ( f' --revert-to-snapshot-support {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, properties=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 + f' --share-network {share_network}' if properties: for key, value in properties.items(): cmd = cmd + f' --property {key}={value}' replica_object = self.dict_result('share', cmd) self._wait_for_object_status( 'share replica', replica_object['id'], 'available' ) if add_cleanup: self.addCleanup( self._delete_share_replica_and_wait, replica_object['id'] ) return replica_object def _delete_share_replica_and_wait(self, replica_id): """Delete a share replica and wait for it to be gone. Uses check_object_deleted (respects CONF.build_timeout) instead of the CLI --wait flag which is limited to osc_lib's hardcoded 300s. """ self.openstack( f'share replica delete {replica_id}', fail_ok=True, ) self.check_object_deleted('share replica', replica_id) 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 {name} {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 {}'.format(share_object['id']), ) return share_object def share_group_type_access_create(self, group_type, project): cmd = f'group type access create {group_type} {project} ' self.dict_result('share', cmd) def share_group_type_access_delete(self, group_type, access_id): cmd = f'group type access delete {group_type} {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 {}'.format(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 def create_qos_type( self, name=None, description=None, specs=None, add_cleanup=True, client=None, formatter=None, ): name = name or data_utils.rand_name('autotest_qos_type_name') specs = specs or {} cmd = f'create {name} ' if description: cmd += f' --description {description}' if specs: q_specs = '' for key, value in specs.items(): q_specs += f' --spec {key}={value}' cmd += q_specs if formatter == 'json': cmd = f'share qos type {cmd} -f {formatter} ' qos_type = json.loads(self.openstack(cmd, client=client)) else: qos_type = self.dict_result('share qos type', cmd, client=client) if add_cleanup: self.addCleanup( self.openstack, f'share qos type delete {qos_type["id"]}' ) return qos_type ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_availability_zones.py0000664000175000017500000000163115221154414030433 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_messages.py0000664000175000017500000001067715221154414026364 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 from manilaclient.tests.functional.osc import base class MessagesCLITest(base.OSCClientTestBase): def setUp(self): super().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)) def test_delete_message_invalid_id(self): self.assertRaises( exceptions.CommandFailed, self.openstack, 'share message delete 0' ) def test_show_message_missing_id(self): self.assertRaises( exceptions.CommandFailed, self.openstack, 'share message show' ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_qos_types.py0000664000175000017500000001145215221154414026573 0ustar00zuulzuul# Copyright (c) 2025 Cloudification GmbH. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 QosTypesCLITest(base.OSCClientTestBase): def test_qos_type_create(self): name = 'test_qos_type' description = 'Description' qos_type = self.create_qos_type(name=name, description=description) self.assertEqual(name, qos_type["name"]) self.assertEqual(description, qos_type["description"]) qos_types_list = self.listing_result('share qos type', 'list') self.assertIn(qos_type["id"], [item['ID'] for item in qos_types_list]) def test_qos_type_create_specs(self): name = 'test_qos_type2' qos_type = self.create_qos_type( name=name, specs={"foo": "bar", "manila": "gazpacho"}, formatter='json', ) specs = qos_type["specs"] self.assertEqual("bar", specs["foo"]) self.assertEqual("gazpacho", specs["manila"]) def test_qos_type_create_specs_using_command(self): self.openstack( 'share qos type create test_qos_type2_cli ' '--description test_cli --spec foo1=bar1 --spec foo2=bar2' ) self.addCleanup( self.openstack, 'share qos type delete test_qos_type2_cli' ) qos_type = json.loads( self.openstack('share qos type show test_qos_type2_cli -f json') ) self.assertEqual('test_cli', qos_type["description"]) self.assertEqual('bar1', qos_type["specs"]["foo1"]) self.assertEqual('bar2', qos_type["specs"]["foo2"]) def test_qos_type_delete(self): qos_type_1 = self.create_qos_type( name='test_qos_type3', add_cleanup=False ) qos_type_2 = self.create_qos_type( name='test_qos_type4', add_cleanup=False ) self.openstack( f'share qos type delete {qos_type_1["id"]} {qos_type_2["id"]}' ) self.check_object_deleted('share qos type', qos_type_1["id"]) self.check_object_deleted('share qos type', qos_type_2["id"]) def test_qos_type_set(self): qos_type = self.create_qos_type(name='test_qos_type5') self.openstack( f'share qos type set {qos_type["id"]} --description Description' ' --spec foo=bar2' ) qos_type = json.loads( self.openstack(f'share qos type show {qos_type["id"]} -f json') ) self.assertEqual('Description', qos_type["description"]) self.assertEqual('bar2', qos_type["specs"]["foo"]) def test_qos_type_unset(self): qos_type = self.create_qos_type( name='test_qos_type6', specs={'foo': 'bar', 'foo1': 'bar1'} ) self.openstack( f'share qos type unset {qos_type["id"]} --spec foo --spec foo1' ) qos_type = json.loads( self.openstack(f'share qos type show {qos_type["id"]} -f json') ) self.assertNotIn('foo', qos_type["specs"]) self.assertNotIn('foo1', qos_type["specs"]) def test_qos_type_list(self): qos_type_1 = self.create_qos_type(name='test_qos_type7') qos_type_2 = self.create_qos_type( name='test_qos_type8', specs={'foo': 'bar'} ) types_list = self.listing_result( 'share qos type', 'list', client=self.admin_client ) self.assertTableStruct( types_list, [ 'ID', 'Name', 'Description', 'Specs', ], ) id_list = [item['ID'] for item in types_list] self.assertIn(qos_type_1['id'], id_list) self.assertIn(qos_type_2['id'], id_list) types_list = self.listing_result('share qos type', 'list') id_list = [item['ID'] for item in types_list] self.assertIn(qos_type_1['id'], id_list) self.assertIn(qos_type_2['id'], id_list) def test_qos_type_show(self): qos_type = self.create_qos_type( name='test_qos_type10', specs={'foo': 'bar'} ) result = json.loads( self.openstack(f'share qos type show {qos_type["id"]} -f json') ) self.assertEqual(qos_type["name"], result["name"]) self.assertEqual('bar', result["specs"]["foo"]) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_resource_locks.py0000664000175000017500000001446615221154414027577 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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', f'lock list --lock-context user --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( f'share lock set --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']} --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 --lock-reason "I can do this" {lock["id"]}', client=self.admin_client, ) self.openstack( f'share lock delete {lock["id"]}', client=self.admin_client ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_access_rules.py0000664000175000017500000002313615221154414030404 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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"]} --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_metadata(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"]} --property foo=bar', ) access_rule = self.dict_result( 'share', f'access show {access_rule["id"]}' ) self.assertEqual(access_rule['properties'], 'foo : bar') def test_set_share_access_level(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['access_level'], 'rw') self.openstack( 'share', params=f'access set {access_rule["id"]} --access-level ro' ) access_rule = self.dict_result( 'share', f'access show {access_rule["id"]}' ) self.assertEqual(access_rule['access_level'], 'ro') 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 --property foo {access_rule["id"]}' ) access_rule_unset = self.dict_result( 'share', f'access show {access_rule["id"]}' ) self.assertEqual(access_rule_unset['properties'], '') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_backups.py0000664000175000017500000001313415221154414027356 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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_restore(self): share = self.create_share() backup = self.create_backup( share_id=share['id'], backup_options={'dummy': True} ) self.openstack(f'share backup restore {backup["id"]} --wait') backup = json.loads( self.openstack(f'share backup show -f json {backup["id"]}') ) share = json.loads(self.openstack(f'share show -f json {share["id"]}')) self.assertEqual('available', backup['status']) self.assertEqual('available', share['status']) 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]) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_group_type_access.py0000664000175000017500000000712315221154414031445 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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']) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_instance_export_locations.py0000664000175000017500000000166615221154414033215 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 from manilaclient.tests.functional.osc import base class ShareInstanceExportLocationsCLITest(base.OSCClientTestBase): def test_share_instance_export_locations_list_invalid_id(self): self.assertRaises( exceptions.CommandFailed, self.openstack, 'share instance export location list wrong_ID', ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_instances.py0000664000175000017500000000657715221154414027732 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 from manilaclient.tests.functional.osc import base class ShareInstancesCLITest(base.OSCClientTestBase): def setUp(self): super().setUp() # Create a share to test with self.share = self.create_share() # Get share instances for testing self.share_instances = self.listing_result('share instance', 'list') self.share_instance = ( self.share_instances[0] if self.share_instances else None ) self.non_existent_id = '76a7dd8e-7564-41d4-a184-06dc20e13d7a' self.invalid_id = 'invalid-format' def test_share_instance_list(self): # Test basic listing instances = self.listing_result('share instance', 'list') self.assertTableStruct( instances, [ 'ID', 'Share ID', 'Host', 'Status', 'Availability Zone', 'Share Network ID', 'Share Server ID', 'Share Type ID', ], ) def test_share_instance_show(self): result = self.dict_result( 'share instance', f'show {self.share_instance["ID"]}' ) self.assertEqual(self.share_instance['ID'], result['id']) def test_share_instance_delete_non_existent(self): self.assertRaises( exceptions.CommandFailed, self.openstack, f'share instance delete {self.non_existent_id}', ) def test_share_instance_set_non_existent(self): self.assertRaises( exceptions.CommandFailed, self.openstack, f'share instance set {self.non_existent_id} --status available', ) def test_share_instance_set_invalid_status(self): self.assertRaises( exceptions.CommandFailed, self.openstack, f'share instance set {self.invalid_id} --status available', ) def test_share_instance_show_non_existent(self): self.assertRaises( exceptions.CommandFailed, self.openstack, f'share instance show {self.non_existent_id}', ) def test_share_instance_show_invalid_id_format(self): self.assertRaises( exceptions.CommandFailed, self.openstack, f'share instance show {self.invalid_id}', ) def test_share_instance_list_invalid_filter_share_id_format(self): self.assertRaises( exceptions.CommandFailed, self.openstack, f'share instance list --share {self.invalid_id}', ) def test_share_instance_list_invalid_filter_non_existent(self): self.assertRaises( exceptions.CommandFailed, self.openstack, f'share instance list --share {self.non_existent_id}', ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_limits.py0000664000175000017500000000242115221154414027224 0ustar00zuulzuul# 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', ], ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_network_subnets.py0000664000175000017500000000260115221154414031157 0ustar00zuulzuul# 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']) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_networks.py0000664000175000017500000000744515221154414027612 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 {}'.format(share_network['id']) ) self.assertEqual(share_network['id'], result['id']) listing_result = self.listing_result( 'share network', 'show {}'.format(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 {}'.format(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 {} --name {}'.format( share_network['id'], 'new_name' ) ) result = self.dict_result( 'share network', 'show {}'.format(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 {}'.format(share_network['id']) ) self.assertEqual(share_network['id'], result1['id']) self.assertEqual(share_network['name'], result1['name']) self.openstack( 'share network unset {} --name'.format(share_network['id']) ) result2 = self.dict_result( 'share network', 'show {}'.format(share_network['id']) ) self.assertEqual(share_network['id'], result2['id']) self.assertEqual('None', result2['name']) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_pools.py0000664000175000017500000000260715221154414027065 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_replica_export_locations.py0000664000175000017500000000516315221154414033024 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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']) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_replicas.py0000664000175000017500000001152215221154414027527 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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, properties=None): 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, properties=properties, 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"]) def test_share_replica_create_with_metadata(self): """Create a replica with --property and verify it appears in show.""" share_replica = self._create_share_and_replica( properties={'test_key': 'test_value'} ) show_result = self.dict_result( 'share', f'replica show {share_replica["id"]}' ) self.assertEqual(share_replica['id'], show_result['id']) self.assertIn('test_key', show_result['properties']) self.assertIn('test_value', show_result['properties']) def test_share_replica_set_property(self): """Set a property on a replica and verify it appears in show.""" share_replica = self._create_share_and_replica() self.openstack( f'share replica set {share_replica["id"]}' ' --property custom_role=secondary' ) show_result = self.dict_result( 'share', f'replica show {share_replica["id"]}' ) self.assertEqual(share_replica['id'], show_result['id']) self.assertIn('custom_role', show_result['properties']) self.assertIn('secondary', show_result['properties']) def test_share_replica_unset_property(self): """Unset a property from a replica and verify it is removed.""" share_replica = self._create_share_and_replica( properties={ 'custom_role': 'secondary', 'custom_policy': 'async', } ) self.openstack( f'share replica unset {share_replica["id"]} --property custom_role' ) show_result = self.dict_result( 'share', f'replica show {share_replica["id"]}' ) self.assertEqual(share_replica['id'], show_result['id']) self.assertNotIn('custom_role', show_result['properties']) self.assertIn('custom_policy', show_result['properties']) self.assertIn('async', show_result['properties']) def test_share_replica_list_with_property_filter(self): """List replicas filtered by property and verify correct results.""" share_replica = self._create_share_and_replica( properties={'filter_key': 'filter_value'} ) share_id = self.dict_result( 'share', f'replica show {share_replica["id"]}' )['share_id'] # List filtered by property; the replica should appear filtered_list = self.listing_result( 'share', f'replica list --share {share_id}' ' --property filter_key=filter_value', ) filtered_ids = [item['ID'] for item in filtered_list] self.assertIn(share_replica['id'], filtered_ids) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_services.py0000664000175000017500000000431215221154414027547 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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' ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_snapshot_instances.py0000664000175000017500000000645315221154414031642 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 {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']) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_snapshots.py0000664000175000017500000001705515221154414027756 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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', ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_transfers.py0000664000175000017500000000703615221154414027741 0ustar00zuulzuul# 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().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') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_share_types.py0000664000175000017500000001236715221154414027101 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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"]) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_shares.py0000664000175000017500000002106715221154414026035 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 {}'.format(share['id'])) self.assertEqual(share['id'], result['id']) listing_result = self.listing_result( 'share', 'show {}'.format(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 {}'.format(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 --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"]} {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] ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/osc/test_shares_group_type.py0000664000175000017500000001404315221154414030306 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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} {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]) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/functional/utils.py0000664000175000017500000001177615221154414024073 0ustar00zuulzuul# 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 = ( f"Skipped. Test requires microversion {microversion} that is not " "allowed to be used by configuration." ) 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' ) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1782896962.046089 python_manilaclient-6.2.0/manilaclient/tests/unit/0000775000175000017500000000000015221154502021160 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/__init__.py0000664000175000017500000000000015221154414023261 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1782896962.047089 python_manilaclient-6.2.0/manilaclient/tests/unit/common/0000775000175000017500000000000015221154502022450 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/common/__init__.py0000664000175000017500000000000015221154414024551 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1782896962.047089 python_manilaclient-6.2.0/manilaclient/tests/unit/common/apiclient/0000775000175000017500000000000015221154502024420 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/common/apiclient/__init__.py0000664000175000017500000000000015221154414026521 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/common/test_httpclient.py0000664000175000017500000002014015221154414026236 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/fakes.py0000664000175000017500000000546715221154414022641 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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(f"found unexpected keys: {list(extra_keys)}") class FakeClient: 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 {} {} but no calls were made.".format(*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, f"Expected {body}; got {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 {} {} but no calls were made.".format(*expected) ) found = False for entry in self.client.callstack: if expected == entry[0:2]: found = True break assert found, ( f'Expected {expected[0]} {expected[1]}; ' f'got {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 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1782896962.048089 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/0000775000175000017500000000000015221154502021744 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/__init__.py0000664000175000017500000000000015221154414024045 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/osc_fakes.py0000664000175000017500000001455215221154414024264 0ustar00zuulzuul# 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: 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: 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: 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: def __init__(self, **kwargs): self.os_beta_command = False class FakeClient: def __init__(self, **kwargs): self.endpoint = kwargs['endpoint'] self.token = kwargs['token'] class FakeClientManager: _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: 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(f"{k}={getattr(self, k)}" for k in reprkeys) return f"<{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'], ) ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/osc_utils.py0000664000175000017500000000501315221154414024323 0ustar00zuulzuul# 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 = f'method {m} should not have been called' self.fail(msg) class TestCommand(TestCase): """Test cliff command classes""" def setUp(self): super().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 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0560896 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/0000775000175000017500000000000015221154502022273 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/__init__.py0000664000175000017500000000000015221154414024374 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/fakes.py0000664000175000017500000014754415221154414023757 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 osc_lib.cli import format_columns from manilaclient.tests.unit.osc import osc_fakes from manilaclient.tests.unit.osc import osc_utils class FakeShareClient: def __init__(self, **kwargs): super().__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() self.qos_types = mock.Mock() class TestShare(osc_utils.TestCommand): def setUp(self): super().setUp() self.app.client_manager.share = FakeShareClient( endpoint=osc_fakes.AUTH_URL, token=osc_fakes.AUTH_TOKEN ) self.app.client_manager.identity = identity_fakes.FakeIdentityClient( endpoint=osc_fakes.AUTH_URL, token=osc_fakes.AUTH_TOKEN ) class FakeShare: """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": {}, "mount_point_name": None, "encryption_key_ref": None, } # Overwrite default attributes. share_info.update(attrs) share = osc_fakes.FakeResource( info=copy.deepcopy(share_info), methods=methods, loaded=True ) return share @staticmethod def create_shares(attrs=None, count=2): """Create multiple fake shares. :param Dictionary attrs: A dictionary with all share attributes :param Integer count: The number of shares to be faked :return: A list of FakeResource objects """ shares = [] for n in range(0, count): shares.append(FakeShare.create_one_share(attrs)) return shares @staticmethod def get_shares(shares=None, count=2): """Get an iterable MagicMock object with a list of faked shares. If a shares list is provided, then initialize the Mock object with the list. Otherwise create one. :param List shares: A list of FakeResource objects faking shares :param Integer count: The number of shares to be faked :return An iterable Mock object with side_effect set to a list of faked shares """ if shares is None: shares = FakeShare.create_shares(count) return mock.Mock(side_effect=shares) @staticmethod def get_share_columns(share=None): """Get the shares columns from a faked shares object. :param shares: A FakeResource objects faking shares :return A tuple which may include the following keys: ('id', 'name', 'description', 'status', 'size', 'share_type', 'metadata', 'snapshot', 'availability_zone') """ if share is not None: return tuple(k for k in sorted(share.keys())) return tuple([]) @staticmethod def get_share_data(share=None): """Get the shares data from a faked shares object. :param shares: A FakeResource objects faking shares :return A tuple which may include the following values: ('ce26708d', 'fake name', 'fake description', 'available', 20, 'fake share type', "Manila='zorilla', Zorilla='manila', Zorilla='zorilla'", 1, 'nova') """ data_list = [] if share is not None: for x in sorted(share.keys()): if x == 'tags': # The 'tags' should be format_list data_list.append( format_columns.ListColumn(share.info.get(x)) ) else: data_list.append(share.info.get(x)) return tuple(data_list) class FakeShareType: """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: """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, "properties": {}, "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: """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: """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, 'encryption_keys': 100, } quotas_info.update(attrs) quotas = osc_fakes.FakeResource( info=copy.deepcopy(quotas_info), loaded=True ) return quotas class FakeShareSnapshotIntances: """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: """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: """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: """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: """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: """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: """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: """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, "properties": format_columns.DictColumn({}), } 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: """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: """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: """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: """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: """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: """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: """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: """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: """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: """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: """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: """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()), 'source_share_server_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: """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: """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 class FakeQosType: """Fake one or more qos types""" @staticmethod def create_one_qostype(attrs=None, methods=None): """Create a fake qos 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 {} qos_type_info = { "id": 'qos-type-id-' + uuid.uuid4().hex, "name": 'qos-type-name-' + uuid.uuid4().hex, "description": 'qos-type-description-' + uuid.uuid4().hex, "specs": { "expected_iops": "2000", "peak_iops": "5000", }, "created_at": 'time-' + uuid.uuid4().hex, "updated_at": 'time-' + uuid.uuid4().hex, } qos_type_info.update(attrs) qos_type = osc_fakes.FakeResource( info=copy.deepcopy(qos_type_info), methods=methods, loaded=True ) return qos_type @staticmethod def create_qos_types(attrs=None, count=2): """Create multiple fake qos types. :param Dictionary attrs: A dictionary with all attributes :param Integer count: The number of qos types to be faked :return: A list of FakeResource objects """ qos_types = [] for n in range(0, count): qos_types.append(FakeQosType.create_one_qostype(attrs)) return qos_types @staticmethod def get_qos_types(qos_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 qos_types is None: qos_types = FakeQosType.create_qos_types(count) return mock.Mock(side_effect=qos_types) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_availability_zones.py0000664000175000017500000000361515221154414027603 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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() # noqa: E501 COLUMNS = ("Id", "Name", "Created At", "Updated At") def setUp(self): super().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)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_messages.py0000664000175000017500000001344115221154414025520 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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().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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_qos_types.py0000664000175000017500000003264415221154414025745 0ustar00zuulzuul# Copyright (c) 2025 Cloudification GmbH. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 qos_types as osc_qos_types from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes COLUMNS = [ 'id', 'name', 'description', 'specs', 'created_at', 'updated_at', ] class TestQosType(manila_fakes.TestShare): def setUp(self): super().setUp() self.qos_types_mock = self.app.client_manager.share.qos_types self.qos_types_mock.reset_mock() self.app.client_manager.share.api_version = api_versions.APIVersion( api_versions.MAX_VERSION ) class TestQosTypeCreate(TestQosType): def setUp(self): super().setUp() self.new_qos_type = manila_fakes.FakeQosType.create_one_qostype() self.qos_types_mock.create.return_value = self.new_qos_type # Get the command object to test self.cmd = osc_qos_types.CreateQosType(self.app, None) self.data = [ self.new_qos_type.id, self.new_qos_type.name, self.new_qos_type.description, ('expected_iops : 2000\npeak_iops : 5000'), self.new_qos_type.created_at, self.new_qos_type.updated_at, ] self.raw_data = [ self.new_qos_type.id, self.new_qos_type.name, self.new_qos_type.description, { 'expected_iops': '2000', 'peak_iops': '5000', }, self.new_qos_type.created_at, self.new_qos_type.updated_at, ] def test_qos_type_create_required_args(self): """Verifies required arguments.""" arglist = [self.new_qos_type.name] verifylist = [ ('name', self.new_qos_type.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.qos_types_mock.create.assert_called_with( name=self.new_qos_type.name, ) self.assertCountEqual(COLUMNS, columns) self.assertCountEqual(self.data, data) def test_qos_type_create_json_format(self): """Verifies --format json.""" arglist = [self.new_qos_type.name, '-f', 'json'] verifylist = [ ('name', self.new_qos_type.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.qos_types_mock.create.assert_called_with( name=self.new_qos_type.name, ) self.assertCountEqual(COLUMNS, columns) self.assertCountEqual(self.raw_data, data) def test_qos_type_create_missing_required_arg(self): """Verifies missing required arguments.""" arglist = [] verifylist = [] self.assertRaises( osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist, ) def test_qos_type_create_specs(self): arglist = [ self.new_qos_type.name, '--spec', 'peak_iops=100', '--spec', 'expected_iops=20', ] verifylist = [ ('name', self.new_qos_type.name), ('spec', ['peak_iops=100', 'expected_iops=20']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.qos_types_mock.create.assert_called_with( name=self.new_qos_type.name, specs={'peak_iops': '100', 'expected_iops': '20'}, ) self.assertCountEqual(COLUMNS, columns) self.assertCountEqual(self.data, data) class TestQosTypeDelete(TestQosType): qos_types = manila_fakes.FakeQosType.create_qos_types(count=2) def setUp(self): super().setUp() self.qos_types_mock.get = manila_fakes.FakeQosType.get_qos_types( self.qos_types ) self.qos_types_mock.delete.return_value = None # Get the command object to test self.cmd = osc_qos_types.DeleteQosType(self.app, None) def test_qos_type_delete_one(self): arglist = [self.qos_types[0].id] verifylist = [('qos_types', [self.qos_types[0].id])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.qos_types_mock.delete.assert_called_with(self.qos_types[0]) self.assertIsNone(result) def test_qos_type_delete_multiple(self): arglist = [] for t in self.qos_types: arglist.append(t.id) verifylist = [ ('qos_types', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) calls = [] for t in self.qos_types: calls.append(mock.call(t)) self.qos_types_mock.delete.assert_has_calls(calls) self.assertIsNone(result) def test_delete_qos_type_with_exception(self): arglist = [ 'non_existing_type', ] verifylist = [ ('qos_types', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.qos_types_mock.delete.side_effect = exceptions.CommandError() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestQosTypeSet(TestQosType): def setUp(self): super().setUp() self.qos_type = manila_fakes.FakeQosType.create_one_qostype( methods={'set_keys': None, 'update': None} ) self.qos_types_mock.get.return_value = self.qos_type # Get the command object to test self.cmd = osc_qos_types.SetQosType(self.app, None) def test_qos_type_set_specs(self): arglist = [ self.qos_type.id, '--spec', 'peak_iops=100', ] verifylist = [ ('qos_type', self.qos_type.id), ('spec', ['peak_iops=100']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.qos_type.set_keys.assert_called_with({'peak_iops': '100'}) self.assertIsNone(result) def test_qos_type_set_description(self): arglist = [self.qos_type.id, '--description', 'new description'] verifylist = [ ('qos_type', self.qos_type.id), ('description', 'new description'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.qos_type.update.assert_called_with(description='new description') self.assertIsNone(result) def test_qos_type_set_specs_exception(self): arglist = [ self.qos_type.id, '--spec', 'peak_iops=100', ] verifylist = [ ('qos_type', self.qos_type.id), ('spec', ['peak_iops=100']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.qos_type.set_keys.side_effect = BadRequest() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestQosTypeUnset(TestQosType): def setUp(self): super().setUp() self.qos_type = manila_fakes.FakeQosType.create_one_qostype( methods={'unset_keys': None, 'update': None} ) self.qos_types_mock.get.return_value = self.qos_type # Get the command object to test self.cmd = osc_qos_types.UnsetQosType(self.app, None) def test_qos_type_unset_description(self): arglist = [self.qos_type.id, '--description'] verifylist = [ ('qos_type', self.qos_type.id), ('description', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.qos_type.update.assert_called_with(description=None) self.assertIsNone(result) def test_qos_type_unset_specs(self): arglist = [self.qos_type.id, '--spec', 'peak_iops'] verifylist = [ ('qos_type', self.qos_type.id), ('spec', ['peak_iops']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.qos_type.unset_keys.assert_called_with(['peak_iops']) self.assertIsNone(result) def test_qos_type_unset_exception(self): arglist = [self.qos_type.id, '--spec', 'peak_iops'] verifylist = [ ('qos_type', self.qos_type.id), ('spec', ['peak_iops']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.qos_type.unset_keys.side_effect = NotFound() self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestQosTypeList(TestQosType): qos_types = manila_fakes.FakeQosType.create_qos_types() columns = utils.format_column_headers(COLUMNS) def setUp(self): super().setUp() self.qos_types_mock.list.return_value = self.qos_types # Get the command object to test self.cmd = osc_qos_types.ListQosType(self.app, None) self.values = ( oscutils.get_dict_properties(s._info, COLUMNS) for s in self.qos_types ) def test_qos_type_list(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.qos_types_mock.list.assert_called_once_with( search_opts={ 'offset': None, 'limit': None, 'name': None, 'description': None, 'name~': None, 'description~': None, }, sort_key=None, sort_dir=None, ) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) def test_qos_type_list_by_name(self): arglist = ['--name', 'fake_name'] verifylist = [('name', 'fake_name')] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.qos_types_mock.list.assert_called_with( search_opts={ 'offset': None, 'limit': None, 'name': 'fake_name', 'description': None, 'name~': None, 'description~': None, }, sort_key=None, sort_dir=None, ) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) class TestQosTypeShow(TestQosType): def setUp(self): super().setUp() self.qos_type = manila_fakes.FakeQosType.create_one_qostype() self.qos_types_mock.get.return_value = self.qos_type # Get the command object to test self.cmd = osc_qos_types.ShowQosType(self.app, None) self.data = [ self.qos_type.id, self.qos_type.name, self.qos_type.description, ('expected_iops : 2000\npeak_iops : 5000'), self.qos_type.created_at, self.qos_type.updated_at, ] self.raw_data = [ self.qos_type.id, self.qos_type.name, self.qos_type.description, { 'expected_iops': '2000', 'peak_iops': '5000', }, self.qos_type.created_at, self.qos_type.updated_at, ] def test_qos_type_show(self): arglist = [self.qos_type.id] verifylist = [("qos_type", self.qos_type.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.qos_types_mock.get.assert_called_with(self.qos_type.id) self.assertCountEqual(COLUMNS, columns) self.assertCountEqual(self.data, data) def test_qos_type_show_json_format(self): arglist = [ self.qos_type.id, '-f', 'json', ] verifylist = [("qos_type", self.qos_type.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.qos_types_mock.get.assert_called_with(self.qos_type.id) self.assertCountEqual(COLUMNS, columns) self.assertCountEqual(self.raw_data, data) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_quotas.py0000664000175000017500000004111315221154414025222 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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) def test_quota_set_encryption_keys_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.89' ) arglist = [ self.project.id, '--encryption-keys', '10', ] verifylist = [('project', self.project.id), ('encryption_keys', 10)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) def test_quota_set_encryption_keys(self): arglist = [ self.project.id, '--encryption-keys', '10', ] verifylist = [('project', self.project.id), ('encryption_keys', 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, encryption_keys=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().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().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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_resource_locks.py0000664000175000017500000002330615221154414026734 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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().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().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().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().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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_security_services.py0000664000175000017500000005443615221154414027474 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().setUp() self.security_service = manila_fakes.FakeShareSecurityService.create_fake_security_service() # noqa: E501 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().setUp() self.security_service = manila_fakes.FakeShareSecurityService.create_fake_security_service() # noqa: E501 self.security_services_mock.get.return_value = self.security_service self.security_services = manila_fakes.FakeShareSecurityService.create_fake_security_services() # noqa: E501 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().setUp() self.security_service = manila_fakes.FakeShareSecurityService.create_fake_security_service() # noqa: E501 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().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().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().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() # noqa: E501 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)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_services.py0000664000175000017500000002544615221154414025544 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().setUp() self.services_mock = self.app.client_manager.share.services self.services_mock.reset_mock() class TestShareServiceSet(TestShareService): def setUp(self): super().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().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)) @ddt.ddt class TestShareServiceEnsureShares(TestShareService): def setUp(self): super().setUp() self.cmd = osc_services.EnsureShareService(self.app, None) def test_ensure_shares(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.86' ) fake_host = 'fake_host@fakebackend' arglist = [ fake_host, ] verifylist = [ ('host', fake_host), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.services_mock.ensure_shares.assert_called_with(fake_host) def test_ensure_shares_invalid_version(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.85' ) fake_host = 'fake_host@fakebackend' arglist = [ fake_host, ] verifylist = [ ('host', fake_host), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) def test_ensure_shares_command_error(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.86' ) self.services_mock.ensure_shares.side_effect = Exception() fake_host = 'fake_host@fakebackend' arglist = [ fake_host, ] verifylist = [ ('host', fake_host), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share.py0000664000175000017500000022541715221154414025023 0ustar00zuulzuul# 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 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().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().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={}, mount_point_name=None, encryption_key_ref=None, ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) def test_share_create_missing_required_arg(self): """Verifies missing required arguments.""" arglist = [ self.new_share.share_proto, ] verifylist = [('share_proto', self.new_share.share_proto)] self.assertRaises( osc_utils.ParserException, self.check_parser, self.cmd, arglist, verifylist, ) def test_share_create_metadata(self): arglist = [ self.new_share.share_proto, str(self.new_share.size), '--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={}, mount_point_name=None, encryption_key_ref=None, ) 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', (f'same_host={share1_name}'), '--scheduler-hint', (f'different_host={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, }, mount_point_name=None, encryption_key_ref=None, ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) def test_share_create_mount_point_name(self): """Verifies that the mount point name has been passed correctly.""" self.app.client_manager.share.api_version = api_versions.APIVersion( "2.84" ) mount_point_name = "fake_mp" arglist = [ self.new_share.share_proto, str(self.new_share.size), '--share-type', self.share_type.id, '--mount-point-name', mount_point_name, ] verifylist = [ ('share_proto', self.new_share.share_proto), ('size', self.new_share.size), ('mount_point_name', mount_point_name), ('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={}, mount_point_name='fake_mp', encryption_key_ref=None, ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) def test_share_create_encryption_key_ref(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.90" ) encryption_key_ref = 'fake_ekr' arglist = [ self.new_share.share_proto, str(self.new_share.size), '--share-type', self.share_type.id, '--encryption-key-ref', encryption_key_ref, ] verifylist = [ ('share_proto', self.new_share.share_proto), ('size', self.new_share.size), ('encryption_key_ref', encryption_key_ref), ('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={}, mount_point_name=None, encryption_key_ref=encryption_key_ref, ) 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={}, mount_point_name=None, encryption_key_ref=None, ) 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={}, mount_point_name=None, encryption_key_ref=None, ) 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={}, mount_point_name=None, encryption_key_ref=None, ) 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().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().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, 'encryption_key_ref': 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', 'Encryption Key Ref', ] 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.new_share.encryption_key_ref, ), ) 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( osc_utils.ParserException, 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_encryption_key_ref_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.89" ) arglist = [ '--encryption-key-ref', 'fake', ] verifylist = [ ('encryption_key_ref', 'fake'), ] 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().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( sorted(manila_fakes.FakeShare.get_share_columns(self._share)), sorted(columns), ) self.assertCountEqual( manila_fakes.FakeShare.get_share_data(self._share), data ) class TestShareSet(TestShare): def setUp(self): super().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) def test_share_set_task_state_none(self): arglist = [self._share.id, '--task-state'] verifylist = [('share', self._share.id), ('task_state', None)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self._share.reset_task_state.assert_called_with(None) self.assertIsNone(result) def test_share_set_task_state_string_none(self): arglist = [self._share.id, '--task-state', 'None'] verifylist = [('share', self._share.id), ('task_state', 'None')] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self._share.reset_task_state.assert_called_with(None) self.assertIsNone(result) class TestShareUnset(TestShare): def setUp(self): super().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().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().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().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().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().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 TestExportLocationSet(TestShare): def setUp(self): super().setUp() self._share = manila_fakes.FakeShare.create_one_share( methods={"set_metadata": None} ) self.shares_mock.get.return_value = self._share self._export_location = ( manila_fakes.FakeShareExportLocation.create_one_export_location( {'fake_share_instance_id': self._share.id} ) ) self.export_locations_mock.get.return_value = self._export_location self.cmd = osc_shares.ShareExportLocationSet(self.app, None) def test_share_set_export_location_property(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.87' ) arglist = [ self._share.id, self._export_location.id, '--property', 'Bobcat=manila', ] verifylist = [ ('share', self._share.id), ('export_location', self._export_location.id), ('property', {'Bobcat': 'manila'}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.export_locations_mock.set_metadata.assert_called_once_with( self._share.id, {'Bobcat': 'manila'}, subresource=self._export_location.id, ) def test_share_set_export_location_property_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.87' ) arglist = [ self._share.id, self._export_location.id, '--property', 'key=', ] verifylist = [ ('share', self._share.id), ('export_location', self._export_location.id), ('property', {'key': ''}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.export_locations_mock.set_metadata.assert_called_once_with( self._share.id, {'key': ''}, subresource=self._export_location.id ) self.export_locations_mock.set_metadata.side_effect = ( exceptions.BadRequest ) self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestExportLocationUnset(TestShare): def setUp(self): super().setUp() self._share = manila_fakes.FakeShare.create_one_share( methods={"set_metadata": None} ) self.shares_mock.get.return_value = self._share self._export_location = ( manila_fakes.FakeShareExportLocation.create_one_export_location( {'fake_share_instance_id': self._share.id} ) ) self.export_locations_mock.get.return_value = self._export_location self.cmd = osc_shares.ShareExportLocationUnset(self.app, None) def test_share_unset_export_location_property(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.87' ) arglist = [ self._share.id, self._export_location.id, '--property', 'Bobcat', ] verifylist = [ ('share', self._share.id), ('export_location', self._export_location.id), ('property', ['Bobcat']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.export_locations_mock.delete_metadata.assert_called_once_with( self._share.id, ['Bobcat'], subresource=self._export_location.id ) def test_share_unset_export_location_property_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.87' ) arglist = [ self._share.id, self._export_location.id, '--property', 'key', ] verifylist = [ ('share', self._share.id), ('export_location', self._export_location.id), ('property', ['key']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.export_locations_mock.delete_metadata.assert_has_calls( [ mock.call( self._share.id, ['key'], subresource=self._export_location.id, ) ] ) self.export_locations_mock.delete_metadata.side_effect = ( exceptions.NotFound ) self.assertRaises( osc_exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestShowShareProperties(TestShare): properties = {'key1': 'value1', 'key2': 'value2'} def setUp(self): super().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().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().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().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().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().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().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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_access_rules.py0000664000175000017500000004746415221154414027562 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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().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().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().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_metadata(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) def test_access_rule_set_access_level(self): arglist = [self.access_rule.id, '--access-level', 'ro'] verifylist = [ ("access_id", self.access_rule.id), ('access_level', 'ro'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.access_rules_mock.set_access_level.assert_called_with( self.access_rule, 'ro' ) self.assertIsNone(result) class TestShareAccessUnset(TestShareAccess): def setUp(self): super().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_backups.py0000664000175000017500000003277615221154414026537 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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().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().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().setUp() self.share_backup = manila_fakes.FakeShareBackup.create_one_backup() self.target_share = manila_fakes.FakeShare.create_one_share() self.backups_mock.get.return_value = self.share_backup self.shares_mock.get.return_value = self.target_share 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) def test_share_backup_restore_to_target(self): arglist = [ self.share_backup.id, '--target-share', self.target_share.id, ] verifylist = [ ('backup', self.share_backup.id), ('target_share', self.target_share.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, target_share_id=self.target_share.id ) self.assertIsNone(result) class TestShareBackupSet(TestShareBackup): def setUp(self): super().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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_group_snapshots.py0000664000175000017500000005041115221154414030327 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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() # noqa: E501 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().setUp() self.share_group_snapshot = manila_fakes.FakeShareGroupSnapshot.create_one_share_group_snapshot() # noqa: E501 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().setUp() self.share_group_snapshot = manila_fakes.FakeShareGroupSnapshot.create_one_share_group_snapshot() # noqa: E501 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().setUp() self.share_group_snapshot = manila_fakes.FakeShareGroupSnapshot.create_one_share_group_snapshot() # noqa: E501 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().setUp() self.share_group_snapshot = manila_fakes.FakeShareGroupSnapshot.create_one_share_group_snapshot() # noqa: E501 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().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( # noqa: E501 {'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().setUp() self.share = manila_fakes.FakeShare.create_one_share() self.share_group_snapshot = manila_fakes.FakeShareGroupSnapshot.create_one_share_group_snapshot( # noqa: E501 {'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)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_group_type.py0000664000175000017500000004236015221154414027272 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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().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().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().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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_group_type_access.py0000664000175000017500000001473415221154414030617 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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().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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_groups.py0000664000175000017500000005620715221154414026421 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 from osc_lib import exceptions from osc_lib import exceptions as osc_exceptions from osc_lib import utils as oscutils from manilaclient import api_versions from manilaclient.osc import utils from manilaclient.osc.v2 import share_groups as osc_share_groups from manilaclient.tests.unit.osc import osc_utils from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes class TestShareGroup(manila_fakes.TestShare): def setUp(self): super().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().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().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().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().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().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().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( osc_utils.ParserException, 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_instance_export_locations.py0000664000175000017500000001167715221154414032364 0ustar00zuulzuul# 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().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().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() # noqa: E501 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( # noqa: E501 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().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( # noqa: E501 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_instances.py0000664000175000017500000002710015221154414027057 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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)] api_version = api_versions.APIVersion('2.34') self.app.client_manager.share.api_version = api_version 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().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().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().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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_limits.py0000664000175000017500000000545215221154414026377 0ustar00zuulzuul# 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().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().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)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_network_subnets.py0000664000175000017500000005060315221154414030330 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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_valid_neutron_info(self): fake_neutron_net_id = str(uuid.uuid4()) fake_neutron_subnet_id = str(uuid.uuid4()) neutron_client = mock.Mock() self.app.client_manager.network = neutron_client neutron_client.find_network.return_value = mock.Mock( id=fake_neutron_net_id ) neutron_client.find_subnet.return_value = mock.Mock( id=fake_neutron_subnet_id ) arglist = [ self.share_network.id, '--neutron-net-id', fake_neutron_net_id, '--neutron-subnet-id', fake_neutron_subnet_id, ] verifylist = [ ('share_network', self.share_network.id), ('neutron_net_id', fake_neutron_net_id), ('neutron_subnet_id', fake_neutron_subnet_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) neutron_client.find_network.assert_called_once_with( fake_neutron_net_id, ignore_missing=False ) neutron_client.find_subnet.assert_called_once_with( fake_neutron_subnet_id, ignore_missing=False ) self.share_subnets_mock.create.assert_called_once_with( share_network_id=self.share_network.id, neutron_net_id=fake_neutron_net_id, neutron_subnet_id=fake_neutron_subnet_id, availability_zone=None, metadata={}, ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_network_subnet_create_invalid_neutron_network(self): fake_neutron_net_id = str(uuid.uuid4()) fake_neutron_net_id = str(uuid.uuid4()) neutron_client = mock.Mock() self.app.client_manager.network = neutron_client neutron_client.find_network.side_effect = Exception( "Network not found." ) arglist = [ self.share_network.id, '--neutron-net-id', fake_neutron_net_id, '--neutron-subnet-id', fake_neutron_net_id, ] verifylist = [ ('share_network', self.share_network.id), ('neutron_net_id', fake_neutron_net_id), ('neutron_subnet_id', fake_neutron_net_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) neutron_client.find_network.assert_called_once_with( fake_neutron_net_id, ignore_missing=False ) neutron_client.find_subnet.assert_not_called() self.share_subnets_mock.create.assert_not_called() def test_share_network_subnet_create_invalid_neutron_subnet(self): fake_neutron_net_id = str(uuid.uuid4()) fake_neutron_subnet_id = str(uuid.uuid4()) neutron_client = mock.Mock() self.app.client_manager.network = neutron_client neutron_client.find_subnet.side_effect = Exception("Subnet not found.") arglist = [ self.share_network.id, '--neutron-net-id', fake_neutron_net_id, '--neutron-subnet-id', fake_neutron_subnet_id, ] verifylist = [ ('share_network', self.share_network.id), ('neutron_net_id', fake_neutron_net_id), ('neutron_subnet_id', fake_neutron_subnet_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) neutron_client.find_network.assert_called_once_with( fake_neutron_net_id, ignore_missing=False ) neutron_client.find_subnet.assert_called_once_with( fake_neutron_subnet_id, ignore_missing=False ) self.share_subnets_mock.create.assert_not_called() 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().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().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().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().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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_networks.py0000664000175000017500000007260215221154414026753 0ustar00zuulzuul# 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().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().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, ) 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) def test_share_network_create_with_valid_neutron_info(self): fake_neutron_net_id = str(uuid.uuid4()) fake_neutron_subnet_id = str(uuid.uuid4()) neutron_client = mock.Mock() self.app.client_manager.network = neutron_client neutron_client.find_network.return_value = mock.Mock( id=fake_neutron_net_id ) neutron_client.find_subnet.return_value = mock.Mock( id=fake_neutron_subnet_id ) arglist = [ '--neutron-net-id', fake_neutron_net_id, '--neutron-subnet-id', fake_neutron_subnet_id, ] verifylist = [ ('neutron_net_id', fake_neutron_net_id), ('neutron_subnet_id', fake_neutron_subnet_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) neutron_client.find_network.assert_called_once_with( fake_neutron_net_id, ignore_missing=False ) neutron_client.find_subnet.assert_called_once_with( fake_neutron_subnet_id, ignore_missing=False ) self.share_networks_mock.create.assert_called_once_with( name=None, description=None, neutron_net_id=fake_neutron_net_id, neutron_subnet_id=fake_neutron_subnet_id, ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_network_create_with_invalid_neutron_network(self): fake_neutron_net_id = str(uuid.uuid4()) neutron_client = mock.Mock() self.app.client_manager.network = neutron_client neutron_client.find_network.side_effect = Exception( "Network not found" ) arglist = ['--neutron-net-id', fake_neutron_net_id] verifylist = [('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 ) neutron_client.find_network.assert_called_once_with( fake_neutron_net_id, ignore_missing=False ) self.share_networks_mock.create.assert_not_called() def test_share_network_create_with_invalid_neutron_subnet(self): fake_neutron_subnet_id = str(uuid.uuid4()) neutron_client = mock.Mock() self.app.client_manager.network = neutron_client neutron_client.find_subnet.side_effect = Exception("Subnet not found") arglist = ['--neutron-subnet-id', fake_neutron_subnet_id] verifylist = [('neutron_subnet_id', fake_neutron_subnet_id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) neutron_client.find_subnet.assert_called_once_with( fake_neutron_subnet_id, ignore_missing=False ) self.share_networks_mock.create.assert_not_called() @ddt.ddt class TestShareNetworkDelete(TestShareNetwork): def setUp(self): super().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().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 = f'security-service-{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().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().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().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( # noqa: E501 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_pools.py0000664000175000017500000001240015221154414026221 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_replica_export_locations.py0000664000175000017500000000727715221154414032200 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_replicas.py0000664000175000017500000007151515221154414026703 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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.cli import format_columns 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().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().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_with_metadata(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.95" ) arglist = [ self.share.id, '--availability-zone', self.share.availability_zone, '--property', 'fake_key1=fake_value1', '--property', 'fake_key2=fake_value2', ] verifylist = [ ('share', self.share.id), ('availability_zone', self.share.availability_zone), ( 'property', {'fake_key1': 'fake_value1', 'fake_key2': 'fake_value2'}, ), ] 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, metadata={'fake_key1': 'fake_value1', 'fake_key2': 'fake_value2'}, ) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_share_replica_create_metadata_api_version_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.91' ) arglist = [ self.share.id, '--property', 'fake_key=fake_value', ] verifylist = [ ('share', self.share.id), ('property', {'fake_key': 'fake_value'}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) 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().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', 'properties', ] column_headers = utils.format_column_headers(columns) def setUp(self): super().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, search_opts={}) 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, search_opts={} ) self.assertEqual(self.column_headers, columns) self.assertEqual(list(self.values), list(data)) def test_share_replica_list_with_properties_column(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.95" ) metadata = {'replication_policy': 'Sync'} self.replicas_list = ( manila_fakes.FakeShareReplica.create_share_replicas( attrs={'metadata': metadata}, count=2 ) ) self.replicas_mock.list.return_value = self.replicas_list arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.assertEqual(self.column_headers, columns) data_list = list(data) self.assertEqual(2, len(data_list)) for row in data_list: props = row[-1] self.assertIsInstance(props, format_columns.DictColumn) self.assertEqual({'replication_policy': 'Sync'}, props._value) def test_share_replica_list_without_properties_column(self): self.app.client_manager.share.api_version = api_versions.APIVersion( "2.94" ) arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.assertNotIn('Properties', columns) class TestShareReplicaShow(TestShareReplica): def setUp(self): super().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().setUp() self.share_replica = manila_fakes.FakeShareReplica.create_one_replica( methods={"set_metadata": None} ) 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 ) def test_share_replica_set_property(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.95' ) arglist = [ self.share_replica.id, '--property', 'fake_key=fake_value', ] verifylist = [ ('replica', self.share_replica.id), ('property', {'fake_key': 'fake_value'}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_replica.set_metadata.assert_called_once_with( {'fake_key': 'fake_value'}, ) def test_share_replica_set_property_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.95' ) arglist = [ self.share_replica.id, '--property', 'fake_key=fake_value', ] verifylist = [ ('replica', self.share_replica.id), ('property', {'fake_key': 'fake_value'}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_replica.set_metadata.assert_called_once_with( {'fake_key': 'fake_value'}, ) self.share_replica.set_metadata.side_effect = exceptions.BadRequest self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestShareReplicaUnset(TestShareReplica): def setUp(self): super().setUp() self.share_replica = manila_fakes.FakeShareReplica.create_one_replica( methods={"delete_metadata": None} ) self.replicas_mock.get.return_value = self.share_replica self.cmd = osc_share_replicas.UnsetShareReplica(self.app, None) def test_share_replica_unset_property(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.95' ) arglist = [ '--property', 'test_key', self.share_replica.id, ] verifylist = [ ('property', ['test_key']), ('replica', self.share_replica.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.share_replica.delete_metadata.assert_called_once_with( parsed_args.property ) def test_share_replica_unset_multiple_properties(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.95' ) arglist = [ '--property', 'key1', '--property', 'key2', self.share_replica.id, ] verifylist = [ ('property', ['key1', 'key2']), ('replica', self.share_replica.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) # Assert delete_metadata was called for each key expected_calls = [mock.call(['key1']), mock.call(['key2'])] self.share_replica.delete_metadata.assert_has_calls( expected_calls, any_order=False ) self.assertEqual(self.share_replica.delete_metadata.call_count, 2) def test_share_replica_unset_property_exception(self): self.app.client_manager.share.api_version = api_versions.APIVersion( '2.95' ) arglist = [ self.share_replica.id, '--property', 'test_key', ] verifylist = [ ('replica', self.share_replica.id), ('property', ['test_key']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # 404 Not Found would be raised, if property 'test_key' doesn't exist. self.share_replica.delete_metadata.side_effect = exceptions.NotFound self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) class TestShareReplicaPromote(TestShareReplica): def setUp(self): super().setUp() self.share_replica = manila_fakes.FakeShareReplica.create_one_replica( attrs={'status': 'available'} ) 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), ('wait', False)] 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) @mock.patch.object( osc_share_replicas.osc_utils, 'wait_for_status', mock.Mock() ) def test_share_replica_promote_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) result = self.cmd.take_action(parsed_args) self.replicas_mock.promote.assert_called_with(self.share_replica) self.assertIsNone(result) osc_share_replicas.osc_utils.wait_for_status.assert_called_once_with( status_f=self.replicas_mock.get, res_id=self.share_replica.id, success_status=['active'], status_field='replica_state', ) 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().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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_servers.py0000664000175000017500000006512715221154414026574 0ustar00zuulzuul# All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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().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)) def test_share_server_list_by_source_share_server(self): expected_source_share_server_id = self.servers_list[ 0 ].source_share_server_id arglist = [ '--source-share-server-id', expected_source_share_server_id, ] verifylist = [ ('source_share_server_id', expected_source_share_server_id), ] 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, 'source_share_server_id': expected_source_share_server_id, } self.servers_mock.list.assert_called_once_with( search_opts=search_opts, ) self.assertEqual(self.columns, columns) self.assertEqual(list(self.values), list(data)) def test_share_server_list_by_identifier(self): expected_identifier = self.servers_list[0].identifier arglist = [ '--identifier', expected_identifier, ] verifylist = [ ('identifier', expected_identifier), ] 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, 'identifier': expected_identifier, } 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().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().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().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_task_state_none(self): arglist = [ self.share_server.id, '--task-state', ] verifylist = [ ('share_server', self.share_server.id), ('task_state', None), ] 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, None ) self.assertIsNone(result) def test_share_server_set_task_state_string_none(self): arglist = [self.share_server.id, '--task-state', 'None'] verifylist = [ ('share_server', self.share_server.id), ('task_state', 'None'), ] 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, None ) 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().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().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().setUp() self.new_share_network = ( manila_fakes.FakeShareNetwork.create_one_share_network() ) self.share_networks_mock.get.return_value = self.new_share_network migration_dict = { 'total_progress': 'fake', 'task_state': 'migration_in_progress', 'destination_share_server_id': 'fake_id', } 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 share_client = self.app.client_manager.share share_client.share_servers.migration_get_progress.return_value = ( migration_dict ) # 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().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 ) ././@PaxHeader0000000000000000000000000000021100000000000010207 xustar00115 path=python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_snapshot_instance_export_locations.py 22 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_snapshot_instance_export_locatio0000664000175000017500000001260115221154414033277 0ustar00zuulzuul# 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 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().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 # noqa: E501 ) self.share_snapshot_instances_el_mock.reset_mock() class TestShareSnapshotInstanceExportLocationList( TestShareSnapshotInstanceExportLocation ): def setUp(self): super().setUp() self.share_snapshot_instance = manila_fakes.FakeShareSnapshotIntances.create_one_snapshot_instance() # noqa: E501 self.share_snapshot_instances_export_locations = manila_fakes.FakeShareSnapshotInstancesExportLocations.create_share_snapshot_instances( # noqa: E501 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 = share_snapshot_instance_export_locations.ShareSnapshotInstanceExportLocationList( # noqa: E501 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().setUp() self.share_snapshot_instance = manila_fakes.FakeShareSnapshotIntances.create_one_snapshot_instance() # noqa: E501 self.share_snapshot_instances_export_location = ( manila_fakes.FakeShareSnapshotInstancesExportLocations.create_one_snapshot_instance() # noqa: E501 ) 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 = share_snapshot_instance_export_locations.ShareSnapshotInstanceExportLocationShow( # noqa: E501 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_snapshot_instances.py0000664000175000017500000001772415221154414031011 0ustar00zuulzuul# 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().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 # noqa: E501 ) self.share_snapshot_instances_el_mock.reset_mock() class TestShareSnapshotInstanceList(TestShareSnapshotInstance): def setUp(self): super().setUp() self.share_snapshot_instances = manila_fakes.FakeShareSnapshotIntances.create_share_snapshot_instances( # noqa: E501 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().setUp() self.share_snapshot_instance = manila_fakes.FakeShareSnapshotIntances.create_one_snapshot_instance() # noqa: E501 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( # noqa: E501 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().setUp() self.share_snapshot_instance = manila_fakes.FakeShareSnapshotIntances.create_one_snapshot_instance() # noqa: E501 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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_snapshots.py0000664000175000017500000012312515221154414027116 0ustar00zuulzuul# 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().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().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().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().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().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().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().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().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().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().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().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().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().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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_transfers.py0000664000175000017500000002040615221154414027101 0ustar00zuulzuul# 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().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().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().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().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().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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_type.py0000664000175000017500000004762215221154414026064 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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().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().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().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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/osc/v2/test_share_type_access.py0000664000175000017500000001370015221154414027373 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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().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().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().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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/test_api_versions.py0000664000175000017500000003422615221154414025303 0ustar00zuulzuul# 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), (f"API Version Major: {version_major}, Minor: {version_minor}"), ) self.assertEqual(repr(api_version), f"") 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", ) @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().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"), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/test_base.py0000664000175000017500000000523015221154414023505 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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} ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/test_client.py0000664000175000017500000001252515221154414024056 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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), ] ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/test_functional_utils.py0000664000175000017500000001744715221154414026172 0ustar00zuulzuul# 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/utils.py0000664000175000017500000000607215221154414022701 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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().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() 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 ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1782896962.05909 python_manilaclient-6.2.0/manilaclient/tests/unit/v1/0000775000175000017500000000000015221154502021506 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v1/test_limits.py0000664000175000017500000000227115221154414024424 0ustar00zuulzuul# 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 " f"with error: {str(e)}" ) assert False, msg for cls in ('Limits', 'RateLimit', 'AbsoluteLimit', 'LimitsManager'): msg = f"Module 'limits' has no '{cls}' attr." self.assertTrue(hasattr(limits, cls), msg) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v1/test_quota_classes.py0000664000175000017500000000232215221154414025766 0ustar00zuulzuul# 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 " f"with error: {str(e)}" ) assert False, msg for cls in ('QuotaClassSet', 'QuotaClassSetManager'): msg = f"Module 'quota_classes' has no '{cls}' attr." self.assertTrue(hasattr(quota_classes, cls), msg) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v1/test_quotas.py0000664000175000017500000000223715221154414024441 0ustar00zuulzuul# 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 " f"with error: {str(e)}" ) assert False, msg for cls in ('QuotaSet', 'QuotaSetManager'): msg = f"Module 'quotas' has no '{cls}' attr." self.assertTrue(hasattr(quotas, cls), msg) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v1/test_scheduler_stats.py0000664000175000017500000000231415221154414026315 0ustar00zuulzuul# 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 " f"imported with error: {str(e)}" ) assert False, msg for cls in ('Pool', 'PoolManager'): msg = f"Module 'scheduler_stats' has no '{cls}' attr." self.assertTrue(hasattr(scheduler_stats, cls), msg) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v1/test_security_services.py0000664000175000017500000000235615221154414026701 0ustar00zuulzuul# 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 " f"imported with error: {str(e)}" ) assert False, msg for cls in ('SecurityService', 'SecurityServiceManager'): msg = f"Module 'security_services' has no '{cls}' attr." self.assertTrue(hasattr(security_services, cls), msg) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v1/test_services.py0000664000175000017500000000225115221154414024744 0ustar00zuulzuul# 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 " f"with error: {str(e)}" ) assert False, msg for cls in ('Service', 'ServiceManager'): msg = f"Module 'services' has no '{cls}' attr." self.assertTrue(hasattr(services, cls), msg) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v1/test_share_networks.py0000664000175000017500000000232615221154414026162 0ustar00zuulzuul# 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 " f"imported with error: {str(e)}" ) assert False, msg for cls in ('ShareNetwork', 'ShareNetworkManager'): msg = f"Module 'share_networks' has no '{cls}' attr." self.assertTrue(hasattr(share_networks, cls), msg) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v1/test_share_servers.py0000664000175000017500000000231615221154414025776 0ustar00zuulzuul# 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 " f"with error: {str(e)}" ) assert False, msg for cls in ('ShareServer', 'ShareServerManager'): msg = f"Module 'share_servers' has no '{cls}' attr." self.assertTrue(hasattr(share_servers, cls), msg) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v1/test_share_snapshots.py0000664000175000017500000000233615221154414026331 0ustar00zuulzuul# 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 " f"imported with error: {str(e)}" ) assert False, msg for cls in ('ShareSnapshot', 'ShareSnapshotManager'): msg = f"Module 'share_snapshots' has no '{cls}' attr." self.assertTrue(hasattr(share_snapshots, cls), msg) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v1/test_share_type_access.py0000664000175000017500000000235515221154414026612 0ustar00zuulzuul# 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 " f"imported with error: {str(e)}" ) assert False, msg for cls in ('ShareTypeAccess', 'ShareTypeAccessManager'): msg = f"Module 'share_type_access' has no '{cls}' attr." self.assertTrue(hasattr(share_type_access, cls), msg) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v1/test_share_types.py0000664000175000017500000000227615221154414025456 0ustar00zuulzuul# 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 " f"with error: {str(e)}" ) assert False, msg for cls in ('ShareType', 'ShareTypeManager'): msg = f"Module 'share_types' has no '{cls}' attr." self.assertTrue(hasattr(share_types, cls), msg) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v1/test_shares.py0000664000175000017500000000223115221154414024404 0ustar00zuulzuul# 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 " f"with error: {str(e)}" ) assert False, msg for cls in ('Share', 'ShareManager'): msg = f"Module 'shares' has no '{cls}' attr." self.assertTrue(hasattr(shares, cls), msg) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0680904 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/0000775000175000017500000000000015221154502021507 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/__init__.py0000664000175000017500000000000015221154414023610 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/fake_clients.py0000664000175000017500000001527615221154414024525 0ustar00zuulzuul# 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 = f"{method.lower()}_{munged_url}" if not hasattr(self, callback): raise AssertionError( f'Called unknown API method: {method} {url}, ' f'expected fakes method name: {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, }, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/fakes.py0000664000175000017500000014707615221154414023173 0ustar00zuulzuul# 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_id1234 = '1234' share_id1111 = '1111' share_id2222 = '2222' share_id3333 = '3333' shares = { 'shares': [ { 'id': share_id1234, '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_id1234, "rel": "self", }, ], 'metadata': {}, }, { 'id': share_id1111, 'name': 'sharename1', 'status': 'fake_status1', 'size': 1, 'host': 'fake_host', 'export_location': 'fake_export_location', 'snapshot_id': 'fake_snapshot_id', 'links': [ { "href": endpoint + "/fake_project/shares/" + share_id1111, "rel": "self", }, ], 'metadata': {}, }, { 'id': share_id2222, 'name': 'sharename2', 'status': 'fake_status2', 'size': 1, 'host': 'fake_host', 'export_location': 'fake_export_location', 'snapshot_id': 'fake_snapshot_id', 'links': [ { "href": endpoint + "/fake_project/shares/" + share_id2222, "rel": "self", }, ], 'metadata': {'key1': 'value1'}, }, { 'id': share_id3333, 'name': 'sharename3', 'status': 'fake_status3', 'size': 1, 'host': 'fake_host', 'export_location': 'fake_export_location', 'snapshot_id': 'fake_snapshot_id', 'links': [ { "href": endpoint + "/fake_project/shares/" + share_id3333, "rel": "self", }, ], 'metadata': {'key1': 'value1', 'key2': 'value2'}, }, ], } if kw.get('with_count'): shares.update({'count': 2}) return (200, {}, shares) def get_snapshots_1234(self, **kw): snapshot = { 'snapshot': { 'id': 1234, 'name': 'sharename', 'status': 'available', } } 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(f"Unexpected action: {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(f"Unexpected share action: {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 = f"expected '{expected}', actual is '{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(f"Unexpected share action: {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 = f"expected '{expected}', actual is '{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(f"Unexpected share action: {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(f"Unexpected share action: {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(f'Unexpected action: {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_qos_types(self, **kw): response_body = { 'qos_types': [ { 'id': 1, 'name': 'test-type-1', 'specs': {'test1': 'test1'}, }, { 'id': 2, 'name': 'test-type-2', 'specs': {'test1': 'test1'}, }, ] } return 200, {}, response_body def get_qos_types_1(self, **kw): return ( 200, {}, { 'qos_type': { 'id': 1, 'name': 'test-qos-type-1', 'specs': {'test1': 'test1'}, } }, ) def post_qos_types(self, body, **kw): qos_type = body['qos_type'] return ( 202, {}, { 'qos_type': { 'id': 3, 'name': 'test-qos-type-3', 'description': 'test description', 'specs': qos_type['specs'], } }, ) def post_qos_types_1_specs(self, body, **kw): assert list(body) == ['specs'] return (200, {}, {'specs': {'k': 'v'}}) def delete_qos_types_1_specs_k(self, **kw): return (204, {}, None) def delete_qos_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(f"Unexpected share action: {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(f'Unexpected action: {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: def __init__(self, dictionary): self.dictionary = dictionary def to_dict(self): return self.dictionary class ShareNetwork: id = 'fake share network id' name = 'fake share network name' class ShareType: id = 'fake share type id' name = 'fake share type name' class ShareGroupType: id = 'fake group type id' name = 'fake group type name' share_types = [ShareType().id] is_public = False class ShareGroupTypeAccess: id = 'fake group type access id' name = 'fake group type access name' class ShareGroup: 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: id = 'fake group snapshot id' share_group_id = (ShareGroup().id,) share_network_id = ShareNetwork().id name = 'fake name' description = 'fake description' class 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' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_availability_zones.py0000664000175000017500000000414615221154414027017 0ustar00zuulzuul# 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_client.py0000664000175000017500000003074515221154414024411 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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_auth_and_session') def test_valid_region_name_v1(self, mock_get_auth): self.mock_object(client.httpclient, 'HTTPClient') self.mock_object(client.adapter, 'LegacyJsonAdapter') # Mock the auth and session returned by _get_keystone_auth_and_session mock_auth = mock.Mock() mock_session = mock.Mock() mock_get_auth.return_value = (mock_auth, mock_session) # Mock the adapter to return token and endpoint mocked_adapter = client.adapter.LegacyJsonAdapter.return_value mocked_adapter.session.get_token.return_value = 'fake_token' mocked_adapter.session.get_endpoint.return_value = 'http://1.2.3.4' mocked_adapter.auth = mock_auth c = client.Client( api_version=manilaclient.API_DEPRECATED_VERSION, service_type="share", region_name='TestRegion', ) self.assertTrue(mock_get_auth.called) client.httpclient.HTTPClient.assert_called_with( 'http://1.2.3.4', 'fake_token', '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_auth_and_session') def test_nonexistent_region_name(self, mock_get_auth): self.mock_object(client.adapter, 'LegacyJsonAdapter') # Mock the auth and session returned by _get_keystone_auth_and_session mock_auth = mock.Mock() mock_session = mock.Mock() mock_get_auth.return_value = (mock_auth, mock_session) # Mock the adapter to return token but no endpoint (None) mocked_adapter = client.adapter.LegacyJsonAdapter.return_value mocked_adapter.session.get_token.return_value = 'fake_token' mocked_adapter.session.get_endpoint.return_value = None mocked_adapter.auth = mock_auth self.assertRaises( RuntimeError, client.Client, api_version=manilaclient.API_MAX_VERSION, region_name='FakeRegion', ) self.assertTrue(mock_get_auth.called) mocked_adapter.session.get_endpoint.assert_called_with( mock_auth, interface='publicURL', service_type='sharev2', region_name='FakeRegion', ) @mock.patch.object(client.Client, '_get_keystone_auth_and_session') def test_regions_with_same_name(self, mock_get_auth): self.mock_object(client.httpclient, 'HTTPClient') self.mock_object(client.adapter, 'LegacyJsonAdapter') # Mock the auth and session returned by _get_keystone_auth_and_session mock_auth = mock.Mock() mock_session = mock.Mock() mock_get_auth.return_value = (mock_auth, mock_session) # Mock the adapter to return token and endpoint mocked_adapter = client.adapter.LegacyJsonAdapter.return_value mocked_adapter.session.get_token.return_value = 'fake_token' mocked_adapter.session.get_endpoint.return_value = 'http://2.2.2.2' mocked_adapter.auth = mock_auth c = client.Client( api_version=manilaclient.API_MIN_VERSION, service_type='sharev2', region_name='SecondRegion', ) self.assertTrue(mock_get_auth.called) mocked_adapter.session.get_endpoint.assert_called_with( mock_auth, interface='publicURL', service_type='sharev2', region_name='SecondRegion', ) client.httpclient.HTTPClient.assert_called_with( 'http://2.2.2.2', 'fake_token', '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 test_client_respects_region_name(self): mock_session = mock.Mock() mock_auth = mock.Mock() region = 'region1' mock_session.get_endpoint.return_value = 'http://fake-endpoint/' client.Client( session=mock_session, auth=mock_auth, service_type='sharev2', endpoint_type='public', region_name=region, ) mock_session.get_endpoint.assert_called_once_with( mock_auth, service_type='sharev2', interface='public', region_name=region, ) def _get_client_args(self, **kwargs): client_args = { 'auth_url': 'http://identity.example.com', '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': 'http://identity.example.com', '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(self, kwargs): def fake_url_for(version): if version == 'v3.0': return 'url_v3.0' else: return None self.mock_object(client.httpclient, 'HTTPClient') self.mock_object(client.identity.v3, 'Password') self.mock_object(client.adapter, 'LegacyJsonAdapter') 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'] client.session.discover.Discover.return_value.url_for.side_effect = ( fake_url_for ) # Mock the adapter to return token and endpoint mocked_adapter = client.adapter.LegacyJsonAdapter.return_value mocked_adapter.session.get_token.return_value = 'fake_token' mocked_adapter.session.get_endpoint.return_value = 'http://3.3.3.3' mocked_adapter.auth = client.identity.v3.Password.return_value client.Client(**client_args) client.httpclient.HTTPClient.assert_called_with( 'http://3.3.3.3', 'fake_token', 'python-manilaclient', insecure=False, cacert=None, cert=client_args['cert'], timeout=None, retries=None, http_log_debug=False, api_version=manilaclient.API_MIN_VERSION, ) # Verify identity.v3.Password was called with correct credentials client.identity.v3.Password.assert_called_with( 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'], ) # Verify LegacyJsonAdapter was created client.adapter.LegacyJsonAdapter.assert_called_with( session=mock.ANY, auth=mock.ANY, interface=client_args['endpoint_type'], service_type=client_args['service_type'], service_name=None, region_name=client_args['region_name'], ) # Verify session.get_token() was called mocked_adapter.session.get_token.assert_called_with(mock.ANY) # Verify session.get_endpoint() was called mocked_adapter.session.get_endpoint.assert_called_with( mock.ANY, interface=client_args['endpoint_type'], service_type=client_args['service_type'], region_name=client_args['region_name'], ) @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') self.mock_object(client.identity.v3, 'Password') self.mock_object(client.adapter, 'LegacyJsonAdapter') 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 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.identity.v3.Password.called) self.assertFalse(client.adapter.LegacyJsonAdapter.called) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_limits.py0000664000175000017500000001326315221154414024430 0ustar00zuulzuul# 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_messages.py0000664000175000017500000001055515221154414024737 0ustar00zuulzuul# 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().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().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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_qos_types.py0000664000175000017500000001336415221154414025157 0ustar00zuulzuul# Copyright (c) 2025 Cloudification GmbH. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 from unittest import mock import ddt from manilaclient import api_versions from manilaclient.common import constants from manilaclient import config from manilaclient.tests.unit import utils from manilaclient.tests.unit.v2 import fakes from manilaclient.v2 import qos_types cs = fakes.FakeClient(api_versions.APIVersion(constants.QOS_TYPE_VERSION)) CONF = config.CONF LATEST_MICROVERSION = CONF.max_api_microversion @ddt.ddt class QosTypesTest(utils.TestCase): def setUp(self): super().setUp() microversion = api_versions.APIVersion(constants.QOS_TYPE_VERSION) mock_microversion = mock.Mock(api_version=microversion) self.manager = qos_types.QosTypeManager(api=mock_microversion) @ddt.data( {'expected_iops': '100'}, {'peak_iops': '500', 'expected_iops': '100'}, ) def test_init(self, specs): info = {'specs': specs} qos_type = qos_types.QosType(qos_types.QosTypeManager, info) self.assertTrue(hasattr(qos_type, '_specs')) self.assertIsInstance(qos_type._specs, dict) def test_list_types(self): tl = cs.qos_types.list() cs.assert_called('GET', '/qos-types') for t in tl: t.api_version = api_versions.APIVersion(constants.QOS_TYPE_VERSION) self.assertIsInstance(t, qos_types.QosType) self.assertTrue(callable(getattr(t, 'get_keys', ''))) self.assertEqual({'test1': 'test1'}, t.get_keys()) @ddt.data( {'qos_type': 'fixed', 'max_iops': '100'}, {'qos_type': 'adaptive', 'peak_iops': '200'}, {'qos_type': 'fixed', 'max_iops': '100', 'expected_iops': '100'}, ) def test_create(self, specs): specs = copy.copy(specs) self.mock_object( self.manager, '_create', mock.Mock(return_value="fake") ) result = self.manager.create( 'test-qos-type-1', specs=specs, ) if specs is None: specs = {} expected_specs = dict(specs) expected_body = { "qos_type": { "name": 'test-qos-type-1', "specs": expected_specs, } } self.manager._create.assert_called_once_with( "/qos-types", expected_body, "qos_type" ) self.assertEqual("fake", result) def test_set_key(self): t = cs.qos_types.get(1) t.api_version = api_versions.APIVersion(constants.QOS_TYPE_VERSION) t.set_keys({'k': 'v'}) cs.assert_called('POST', '/qos-types/1/specs', {'specs': {'k': 'v'}}) def test_unset_keys(self): t = cs.qos_types.get(1) t.api_version = api_versions.APIVersion(constants.QOS_TYPE_VERSION) t.unset_keys(['k']) cs.assert_called('DELETE', '/qos-types/1/specs/k') def test_update(self): self.mock_object( self.manager, '_update', mock.Mock(return_value="fake") ) qos_type = 1234 body = dict(description="updated test description") expected_body = { "qos_type": dict(description="updated test description") } result = self.manager.update(qos_type, **body) self.manager._update.assert_called_once_with( f"/qos-types/{qos_type}", expected_body, "qos_type" ) self.assertEqual("fake", result) def test_delete(self): cs.qos_types.delete(1) cs.assert_called('DELETE', '/qos-types/1') def test_get_keys_from_resource_data(self): version = api_versions.APIVersion(constants.QOS_TYPE_VERSION) manager = mock.Mock() manager.api = mock.Mock() manager.api.client = mock.Mock(return_value=None) manager.api.client.get = mock.Mock(return_value=(200, {})) valid_specs = {'test': 'test'} qos_type = qos_types.QosType( manager, {'specs': valid_specs, 'name': 'test'}, loaded=True, ) qos_type.api_version = version actual_result = qos_type.get_keys() self.assertEqual(actual_result, valid_specs) self.assertEqual(manager.api.client.get.call_count, 0) @ddt.data( {'prefer_resource_data': True, 'resource_specs': {}}, { 'prefer_resource_data': False, 'resource_specs': {'fake': 'fake'}, }, {'prefer_resource_data': False, 'resource_specs': {}}, ) @ddt.unpack def test_get_keys_from_api(self, prefer_resource_data, resource_specs): version = api_versions.APIVersion(constants.QOS_TYPE_VERSION) manager = mock.Mock() manager.api = mock.Mock() manager.api.client = mock.Mock(return_value=None) valid_specs = {'test': 'test'} manager.api.client.get = mock.Mock( return_value=(200, {'specs': valid_specs}) ) info = { 'name': 'test', 'uuid': 'fake', 'specs': resource_specs, } qos_type = qos_types.QosType(manager, info, loaded=True) qos_type.api_version = version actual_result = qos_type.get_keys(prefer_resource_data) self.assertEqual(actual_result, valid_specs) self.assertEqual(manager.api.client.get.call_count, 1) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_quota_classes.py0000664000175000017500000000546115221154414025776 0ustar00zuulzuul# 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 = f"{resource_path}/test" 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 = f"{resource_path}/test" 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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_quotas.py0000664000175000017500000002760215221154414024445 0ustar00zuulzuul# 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 = f"{resource_path}/test/detail" else: expected_url = f"{resource_path}/test" 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 = f"{resource_path}/test/detail?user_id=fake_user" else: expected_url = f"{resource_path}/test?user_id=fake_user" 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 = ( f"{resource_path}/{tenant_id}/detail?share_type={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 = f"{resource_path}/test/defaults" 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 = f"{resource_path}/test" 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 = f"{resource_path}/test?user_id=fake_user" 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 = f"{resource_path}/{tenant_id}?share_type={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 = f"{resource_path}/test" 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 = f"{resource_path}/test?user_id=fake_user" 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 = f"{resource_path}/test?share_type=fake_st" 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, f'_{operation}', mock.Mock(return_value='fake_delete') ): self.assertRaises( TypeError, getattr(manager, operation), 'fake_tenant_id', share_type='fake_share_type', ) getattr(manager, f'_{operation}').assert_not_called() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_scheduler_stats.py0000664000175000017500000000557615221154414026333 0ustar00zuulzuul# 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().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(f'', repr(self.resource_class)) class PoolManagerTest(utils.TestCase): def setUp(self): super().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 = f"?host={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 = f"?backend={backend}&host={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, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_security_services.py0000664000175000017500000002062115221154414026675 0ustar00zuulzuul# 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: id = 'fake_security_service_id' def setUp(self): super().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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_services.py0000664000175000017500000000762615221154414024760 0ustar00zuulzuul# 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 = f"?host={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 = f"?binary={binary}&host={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}, ) def test_ensure_shares(self): microversion = '2.86' manager = self._get_manager(microversion) manager.api.client.post = mock.Mock(return_value='fake') host = 'fake_host' result = manager.ensure_shares(host) self.assertEqual(result, 'fake') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_backups.py0000664000175000017500000000777315221154414025752 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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' FAKE_TARGET_SHARE_ID = 'fake_target_share_id' @ddt.ddt class ShareBackupsTest(utils.TestCase): class _FakeShareBackup: id = 'fake_share_backup_id' def setUp(self): super().setUp() microversion = api_versions.APIVersion("2.91") 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, info=None ) def test_restore_to_target(self): with mock.patch.object(self.manager, '_action', mock.Mock()): self.manager.restore( FAKE_BACKUP, target_share_id=FAKE_TARGET_SHARE_ID ) self.manager._action.assert_called_once_with( 'restore', FAKE_BACKUP, info=FAKE_TARGET_SHARE_ID ) 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} ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_export_locations.py0000664000175000017500000000344115221154414027702 0ustar00zuulzuul# 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', f'/shares/{share_id}/export_locations') 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', (f'/shares/{share_id}/export_locations/{el_uuid}') ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_group_snapshots.py0000664000175000017500000002410015221154414027537 0ustar00zuulzuul# 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().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().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'}}, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_group_type_access.py0000664000175000017500000001010315221154414030015 0ustar00zuulzuul# 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().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( f'', result, ) @ddt.ddt class ShareGroupTypeAccessManagerTest(utils.TestCase): def setUp(self): super().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, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_group_types.py0000664000175000017500000002004515221154414026665 0ustar00zuulzuul# 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().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( f'', 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().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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_groups.py0000664000175000017500000002715115221154414025631 0ustar00zuulzuul# 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().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().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'}}, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_instance_export_locations.py0000664000175000017500000000411015221154414031560 0ustar00zuulzuul# 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', f'/share_instances/{share_instance_id}/export_locations' ) 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', ( f'/share_instances/{share_instance_id}/export_locations/' f'{el_uuid}' ), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_instances.py0000664000175000017500000001060515221154414026275 0ustar00zuulzuul# 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} ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_network_subnets.py0000664000175000017500000000600315221154414027537 0ustar00zuulzuul# 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().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, } ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_networks.py0000664000175000017500000002646715221154414026177 0ustar00zuulzuul# 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: id = 'fake_share_network_id' class _FakeSecurityService: id = 'fake_security_service_id' def setUp(self): super().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 = ( f"{share_networks.RESOURCES_PATH}/detail?all_tenants=1&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( 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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_replica_export_locations.py0000664000175000017500000000357515221154414031411 0ustar00zuulzuul# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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', f'/share-replicas/{share_replica_id}/export-locations' ) 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_replicas.py0000664000175000017500000001542315221154414026113 0ustar00zuulzuul# 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: id = 'fake_share_replica_id' def setUp(self): super().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']) @ddt.data("2.95") def test_create_with_metadata(self, microversion): api_version = api_versions.APIVersion(microversion) values = { 'availability_zone': 'az1', 'share': 's1', 'share_network': 'sn1', 'metadata': {"fake_key": "fake_value"}, } 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'} ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_servers.py0000664000175000017500000002734715221154414026012 0ustar00zuulzuul# 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: _info = { "backend_details": { "fake_key1": "fake_value1", "fake_key2": "fake_value2", } } class ShareServerTest(utils.TestCase): def setUp(self): super().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( f'ShareServer: {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().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/{}/action'.format(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 = f"?host={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 = f"?host={host}&status={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( f"{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( f"{share_servers.RESOURCES_PATH}/{share_server_id}/details", '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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_snapshot_export_locations.py0000664000175000017500000000300515221154414031615 0ustar00zuulzuul# 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', f'/snapshots/{snapshot_id}/export-locations') 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', (f'/snapshots/{snapshot_id}/export-locations/{el_id}') ) ././@PaxHeader0000000000000000000000000000020500000000000010212 xustar00111 path=python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_snapshot_instance_export_locations.py 22 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_snapshot_instance_export_locations.p0000664000175000017500000000344215221154414033315 0ustar00zuulzuul# 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', f'/snapshot-instances/{snapshot_instance_id}/export-locations', ) 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', ( f'/snapshot-instances/{snapshot_instance_id}/export-locations/' f'{el_id}' ), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_snapshot_instances.py0000664000175000017500000000707615221154414030224 0ustar00zuulzuul# 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().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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_snapshots.py0000664000175000017500000002346415221154414026337 0ustar00zuulzuul# 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'}} ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_share_transfers.py0000664000175000017500000000330715221154414026316 0ustar00zuulzuul# 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', f'/{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', f'/{TRANSFER_URL}/{transfer_id}') def test_list(self): cs.transfers.list() cs.assert_called('GET', f'/{TRANSFER_URL}/detail') def test_delete(self): cs.transfers.delete('5678') cs.assert_called('DELETE', f'/{TRANSFER_URL}/5678') def test_accept(self): transfer_id = '5678' auth_key = '12345' cs.transfers.accept(transfer_id, auth_key) cs.assert_called('POST', f'/{TRANSFER_URL}/{transfer_id}/accept') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_shares.py0000664000175000017500000010415515221154414024415 0ustar00zuulzuul# 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().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( {'mount_point_name': 'fake_mount_pt1'}, {'mount_point_name': 'fake_mount_pt2'}, ) @ddt.unpack def test_create_share_with_mount_point_name(self, mount_point_name): body = { 'share': { 'is_public': False, 'share_type': None, 'name': None, 'snapshot_id': None, 'description': None, 'metadata': {}, 'share_proto': 'nfs', 'share_network_id': None, 'size': 1, 'availability_zone': None, 'scheduler_hints': {}, 'mount_point_name': mount_point_name, } } cs.shares.create('nfs', 1, mount_point_name=mount_point_name) cs.assert_called('POST', '/shares', body) @ddt.data( {'encryption_key_ref': 'fake_key1'}, {'encryption_key_ref': 'fake_key2'}, ) @ddt.unpack def test_create_share_with_encryption_key_ref(self, encryption_key_ref): body = { 'share': { 'is_public': False, 'share_type': None, 'name': None, 'snapshot_id': None, 'description': None, 'metadata': {}, 'share_proto': 'nfs', 'share_network_id': None, 'size': 1, 'availability_zone': None, 'scheduler_hints': {}, 'encryption_key_ref': encryption_key_ref, } } cs.shares.create('nfs', 1, encryption_key_ref=encryption_key_ref) 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'), ("2.92", "/shares/manage", False, None, 'fake_mount_pt1'), ) @ddt.unpack def test_manage_share( self, microversion, resource_path, is_public=False, share_server_id=None, mount_point_name=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 if version >= api_versions.APIVersion('2.92'): expected_body["mount_point_name"] = mount_point_name 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, ) elif ( api_versions.APIVersion('2.49') <= version < api_versions.APIVersion('2.92') ): result = manager.manage( service_host, protocol, export_path, driver_options, share_type, name, description, is_public, share_server_id, ) else: result = manager.manage( service_host, protocol, export_path, driver_options, share_type, name, description, is_public, share_server_id, mount_point_name, ) 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(4, 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': "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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_type_access.py0000664000175000017500000000677615221154414025444 0ustar00zuulzuul# 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( f"/types/3/{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} ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/tests/unit/v2/test_types.py0000664000175000017500000004455315221154414024301 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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( f"/types/{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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/utils.py0000664000175000017500000000405615221154414020560 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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: """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 f"{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", "~") ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0710907 python_manilaclient-6.2.0/manilaclient/v1/0000775000175000017500000000000015221154502017365 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/__init__.py0000664000175000017500000000161515221154414021503 0ustar00zuulzuul# 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: 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/client.py0000664000175000017500000002316715221154414021230 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 identity from keystoneauth1 import session 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: """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 keystoneauth1.identity import v3 >>> from keystoneauth1 import session >>> from manilaclient import client >>> auth = v3.Password(auth_url=AUTH_URL, username=USERNAME, user_domain_name=USER_DOMAIN_NAME, password=PASSWORD, project_name=PROJECT_ID, project_domain_name=PROJECT_DOMAIN_NAME) >>> sess = session.Session(auth=auth) >>> manila = client.Client(VERSION, session=sess) Then call methods on its managers:: >>> client.shares.list() ... """ @removals.removed_kwarg( 'use_keyring', message='This parameter is no longer supported and has no effect.', version='5.8.0', removal_version='6.0.0', ) @removals.removed_kwarg( 'force_new_token', message='This parameter is no longer supported and has no effect.', version='5.8.0', removal_version='6.0.0', ) @removals.removed_kwarg( 'cached_token_lifetime', message='This parameter is no longer supported and has no effect.', version='5.8.0', removal_version='6.0.0', ) 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 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: # Modern path - session provided by caller (e.g., OSC plugin) 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: # Legacy path - create auth plugin and session ourselves auth, ks_session = self._get_keystone_auth_and_session() self.keystone_client = adapter.LegacyJsonAdapter( session=ks_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) if not input_auth_token: raise RuntimeError("Not Authorized") if not service_catalog_url: # Use keystoneauth1 session endpoint discovery service_catalog_url = self.keystone_client.session.get_endpoint( self.keystone_client.auth, interface=endpoint_type, service_type=service_type, region_name=region_name, ) 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_auth_and_session(self): """Create keystoneauth1 auth plugin and session for authentication. Returns: tuple: (auth_plugin, session) for use with keystoneauth1 """ # Create session with SSL settings if self.insecure: verify = False else: verify = self.cacert or True ks_session = session.Session(verify=verify, cert=self.cert) # Discover Keystone v3 endpoint ks_discover = session.discover.Discover(ks_session, self.auth_url) auth_url = ks_discover.url_for('v3.0') if not auth_url: raise exceptions.CommandError( 'Unable to determine the Keystone version to authenticate ' 'with using the given auth_url.' ) # Create v3 Password auth plugin auth = identity.v3.Password( auth_url=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, ) return auth, ks_session ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0710907 python_manilaclient-6.2.0/manilaclient/v1/contrib/0000775000175000017500000000000015221154502021025 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/contrib/__init__.py0000664000175000017500000000000015221154414023126 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/contrib/list_extensions.py0000664000175000017500000000233715221154414024640 0ustar00zuulzuul# 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: 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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/limits.py0000664000175000017500000000225115221154414021242 0ustar00zuulzuul# 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: 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/quota_classes.py0000664000175000017500000000231415221154414022607 0ustar00zuulzuul# 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: 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/quotas.py0000664000175000017500000000225115221154414021255 0ustar00zuulzuul# 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: 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/scheduler_stats.py0000664000175000017500000000232515221154414023137 0ustar00zuulzuul# 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: 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/security_services.py0000664000175000017500000000234615221154414023520 0ustar00zuulzuul# 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: 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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/services.py0000664000175000017500000000226315221154414021567 0ustar00zuulzuul# 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: 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/share_networks.py0000664000175000017500000000232115221154414022775 0ustar00zuulzuul# 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: 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/share_servers.py0000664000175000017500000000231415221154414022614 0ustar00zuulzuul# 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: 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/share_snapshots.py0000664000175000017500000000231015221154414023141 0ustar00zuulzuul# 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: 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/share_type_access.py0000664000175000017500000000234615221154414023432 0ustar00zuulzuul# 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: 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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/share_types.py0000664000175000017500000000230415221154414022266 0ustar00zuulzuul# 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: 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v1/shares.py0000664000175000017500000000223315221154414021226 0ustar00zuulzuul# 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: 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) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0800912 python_manilaclient-6.2.0/manilaclient/v2/0000775000175000017500000000000015221154502017366 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/__init__.py0000664000175000017500000000124715221154414021505 0ustar00zuulzuul# 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/availability_zones.py0000664000175000017500000000245015221154414023633 0ustar00zuulzuul# 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 f"" class AvailabilityZoneManager(base.Manager): """Manage :class:`AvailabilityZone` 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/client.py0000664000175000017500000003102215221154414021216 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 identity from keystoneauth1 import session 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 qos_types 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: """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 keystoneauth1.identity import v3 >>> from keystoneauth1 import session >>> from manilaclient import client >>> auth = v3.Password(auth_url=AUTH_URL, username=USERNAME, user_domain_name=USER_DOMAIN_NAME, password=PASSWORD, project_name=PROJECT_ID, project_domain_name=PROJECT_DOMAIN_NAME) >>> sess = session.Session(auth=auth) >>> manila = client.Client(VERSION, session=sess) Then call methods on its managers:: >>> client.shares.list() ... """ @removals.removed_kwarg( 'use_keyring', message='This parameter is no longer supported and has no effect.', version='5.8.0', removal_version='6.0.0', ) @removals.removed_kwarg( 'force_new_token', message='This parameter is no longer supported and has no effect.', version='5.8.0', removal_version='6.0.0', ) @removals.removed_kwarg( 'cached_token_lifetime', message='This parameter is no longer supported and has no effect.', version='5.8.0', removal_version='6.0.0', ) 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 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: # Modern path - session provided by caller (e.g., OSC plugin) 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: # Legacy path - create auth plugin and session ourselves auth, ks_session = self._get_keystone_auth_and_session() self.keystone_client = adapter.LegacyJsonAdapter( session=ks_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) if not input_auth_token: raise RuntimeError("Not Authorized") if not service_catalog_url: # Use keystoneauth1 session endpoint discovery service_catalog_url = self.keystone_client.session.get_endpoint( self.keystone_client.auth, interface=endpoint_type, service_type=service_type, region_name=region_name, ) 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.qos_types = qos_types.QosTypeManager(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( # noqa: E501 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_auth_and_session(self): """Create keystoneauth1 auth plugin and session for authentication. Returns: tuple: (auth_plugin, session) for use with keystoneauth1 """ # Create session with SSL settings if self.insecure: verify = False else: verify = self.cacert or True ks_session = session.Session(verify=verify, cert=self.cert) # Discover Keystone v3 endpoint ks_discover = session.discover.Discover(ks_session, self.auth_url) auth_url = ks_discover.url_for('v3.0') if not auth_url: raise exceptions.CommandError( 'Unable to determine the Keystone version to authenticate ' 'with using the given auth_url.' ) # Create v3 Password auth plugin auth = identity.v3.Password( auth_url=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, ) return auth, ks_session ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0800912 python_manilaclient-6.2.0/manilaclient/v2/contrib/0000775000175000017500000000000015221154502021026 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/contrib/__init__.py0000664000175000017500000000000015221154414023127 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/contrib/list_extensions.py0000664000175000017500000000265015221154414024637 0ustar00zuulzuul# 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/limits.py0000664000175000017500000000555115221154414021251 0ustar00zuulzuul# 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: """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 f"" class AbsoluteLimit: """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 f"" 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") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/messages.py0000664000175000017500000000540015221154414021550 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 f"" 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: {}.'.format( ', '.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: {}.'.format( ', '.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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/qos_types.py0000664000175000017500000001314615221154414021775 0ustar00zuulzuul# Copyright (c) 2025 Cloudification GmbH. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. """ Qos Type interface. """ from manilaclient import api_versions from manilaclient import base from manilaclient.common import constants class QosType(base.Resource): """A Qos Type represents Quality of service of Manila resource.""" def __init__(self, manager, info, loaded=False): super().__init__(manager, info, loaded) self._specs = info.get('specs', {}) def __repr__(self): return f"" def get_keys(self, prefer_resource_data=True): """Get specs from a qos type. :param prefer_resource_data: By default specs are retrieved from resource data, but user can force this method to make API call. :return: dict with specs """ specs = getattr(self, 'specs', None) if prefer_resource_data and specs: return specs qos_type_id = base.getid(self) _resp, body = self.manager.api.client.get( f"/qos-types/{qos_type_id}/specs" ) self.specs = body["specs"] return body["specs"] def set_keys(self, metadata): """Set specs on a qos type. :param metadata: A dict of key/value pairs to be set """ body = {'specs': metadata} qos_type_id = base.getid(self) return self.manager._create( f"/qos-types/{qos_type_id}/specs", body, "specs", return_raw=True, ) def unset_keys(self, keys): """Unset specs on a qos type. :param keys: A list of keys to be unset """ qos_type_id = base.getid(self) for k in keys: self.manager._delete(f"/qos-types/{qos_type_id}/specs/{k}") def update(self, **kwargs): """Update this qos type.""" return self.manager.update(self, **kwargs) def delete(self): """Delete this qos type.""" return self.manager.delete(self) class QosTypeManager(base.ManagerWithFind): """Manage :class:`QosType` resources.""" resource_class = QosType @api_versions.wraps(constants.QOS_TYPE_VERSION) def list(self, search_opts=None, sort_key=None, sort_dir=None): """Get a list of all qos types. :param search_opts: Search options to filter out qos types. :param sort_key: Key to be sorted. :param sort_dir: Sort direction, should be 'desc' or 'asc'. :rtype: list of :class:`QosType`. """ search_opts = search_opts or {} if sort_key is not None: if sort_key in constants.QOS_TYPE_SORT_KEY_VALUES: search_opts['sort_key'] = sort_key else: raise ValueError( 'sort_key must be one of the following: {}.'.format( ', '.join(constants.QOS_TYPE_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: {}.'.format( ', '.join(constants.SORT_DIR_VALUES) ) ) query_string = self._build_query_string(search_opts) return self._list(f"/qos-types{query_string}", "qos_types") @api_versions.wraps(constants.QOS_TYPE_VERSION) def get(self, qos_type): """Get a specific qos type. :param qos_type: The ID of the :class:`QosType` to get. :rtype: :class:`QosType` """ qos_type_id = base.getid(qos_type) return self._get(f"/qos-types/{qos_type_id}", "qos_type") @api_versions.wraps(constants.QOS_TYPE_VERSION) def delete(self, qos_type): """Delete a specific qos_type. :param qos_type: The name or ID of the :class:`QosType` to get. """ qos_type_id = base.getid(qos_type) self._delete(f"/qos-types/{qos_type_id}") @api_versions.wraps(constants.QOS_TYPE_VERSION) def create(self, name, description=None, specs=None): """Create a qos type. :param name: Descriptive name of the qos type :param specs: Specs of the qos type :rtype: :class:`QosType` """ if specs is None: specs = {} body = { "qos_type": { "name": name, "specs": specs, } } if description: body["qos_type"]["description"] = description return self._create("/qos-types", body, "qos_type") @api_versions.wraps(constants.QOS_TYPE_VERSION) def update(self, qos_type, **kwargs): """Update the description for a qos type. :param qos_type: the ID of the :class: `QosType` to update. :param description: Description of the qos type. :rtype: :class:`QosType` """ if not kwargs: return body = { 'qos_type': kwargs, } qos_type_id = base.getid(qos_type) return self._update(f"/qos-types/{qos_type_id}", body, "qos_type") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/quota_classes.py0000664000175000017500000001423215221154414022612 0ustar00zuulzuul# 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( f"{RESOURCE_PATH_LEGACY}/{class_name}", "quota_class_set" ) @api_versions.wraps("2.7") # noqa def get(self, class_name): # noqa return self._get(f"{RESOURCE_PATH}/{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(f"{resource_path}/{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( # noqa self, class_name, shares=None, gigabytes=None, snapshots=None, snapshot_gigabytes=None, share_networks=None, ): return self._do_update( class_name, shares=shares, gigabytes=gigabytes, snapshots=snapshots, snapshot_gigabytes=snapshot_gigabytes, share_networks=share_networks, resource_path=RESOURCE_PATH, ) @api_versions.wraps("2.40", "2.52") # noqa def update( # noqa self, class_name, shares=None, gigabytes=None, 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( # noqa self, class_name, shares=None, gigabytes=None, 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( # noqa self, class_name, shares=None, gigabytes=None, 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, ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/quotas.py0000664000175000017500000003136015221154414021261 0ustar00zuulzuul# 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 = f'{query}?user_id={user_id}&share_type={share_type}' elif user_id: query = f'{query}?user_id={user_id}' elif share_type: query = f'{query}?share_type={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, encryption_keys=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, 'encryption_keys': encryption_keys, }, } 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}/{tenant_id}?user_id={user_id}'.format( **data ) elif share_type: url = '{resource_path}/{tenant_id}?share_type={st}'.format(**data) else: url = "{resource_path}/{tenant_id}".format(**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( # noqa self, tenant_id, shares=None, snapshots=None, gigabytes=None, snapshot_gigabytes=None, share_networks=None, force=None, user_id=None, ): return self._do_update( tenant_id, shares, snapshots, gigabytes, snapshot_gigabytes, share_networks, force, user_id, resource_path=RESOURCE_PATH, ) def _validate_st_and_sn_in_same_request(self, share_type, share_networks): if share_type and share_networks: raise ValueError( "'share_networks' quota can be set only for project or user, " "not share type." ) @api_versions.wraps("2.39", "2.39") # noqa def update( # noqa self, tenant_id, user_id=None, share_type=None, shares=None, snapshots=None, gigabytes=None, snapshot_gigabytes=None, share_networks=None, force=None, ): self._validate_st_and_sn_in_same_request(share_type, share_networks) return self._do_update( tenant_id, shares, snapshots, gigabytes, snapshot_gigabytes, share_networks, force, user_id, share_type=share_type, resource_path=RESOURCE_PATH, ) @api_versions.wraps("2.40", "2.52") # noqa def update( # noqa self, tenant_id, user_id=None, share_type=None, shares=None, snapshots=None, gigabytes=None, snapshot_gigabytes=None, share_networks=None, share_groups=None, share_group_snapshots=None, force=None, ): self._validate_st_and_sn_in_same_request(share_type, share_networks) return self._do_update( tenant_id, shares, snapshots, gigabytes, snapshot_gigabytes, share_networks, force, user_id, share_type=share_type, share_groups=share_groups, share_group_snapshots=share_group_snapshots, resource_path=RESOURCE_PATH, ) @api_versions.wraps(REPLICA_QUOTAS_MICROVERSION, "2.61") # noqa def update( # noqa self, tenant_id, user_id=None, share_type=None, shares=None, snapshots=None, gigabytes=None, snapshot_gigabytes=None, share_networks=None, share_groups=None, share_group_snapshots=None, share_replicas=None, replica_gigabytes=None, force=None, ): self._validate_st_and_sn_in_same_request(share_type, share_networks) return self._do_update( tenant_id, shares, snapshots, gigabytes, snapshot_gigabytes, share_networks, force, user_id, share_type=share_type, share_groups=share_groups, share_group_snapshots=share_group_snapshots, share_replicas=share_replicas, replica_gigabytes=replica_gigabytes, resource_path=RESOURCE_PATH, ) @api_versions.wraps("2.62", "2.89") # noqa def update( # noqa self, tenant_id, user_id=None, share_type=None, shares=None, snapshots=None, gigabytes=None, snapshot_gigabytes=None, share_networks=None, share_groups=None, share_group_snapshots=None, share_replicas=None, replica_gigabytes=None, force=None, 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("2.90") # noqa def update( # noqa self, tenant_id, user_id=None, share_type=None, shares=None, snapshots=None, gigabytes=None, snapshot_gigabytes=None, share_networks=None, share_groups=None, share_group_snapshots=None, share_replicas=None, replica_gigabytes=None, force=None, per_share_gigabytes=None, encryption_keys=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, encryption_keys=encryption_keys, resource_path=RESOURCE_PATH, ) @api_versions.wraps("1.0", "2.6") def defaults(self, tenant_id): return self._get( f"{RESOURCE_PATH_LEGACY}/{tenant_id}/defaults", "quota_set" ) @api_versions.wraps("2.7") # noqa def defaults(self, tenant_id): # noqa return self._get(f"{RESOURCE_PATH}/{tenant_id}/defaults", "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}/{tenant_id}?user_id={user_id}'.format( **data ) elif share_type: url = '{resource_path}/{tenant_id}?share_type={st}'.format(**data) else: url = '{resource_path}/{tenant_id}'.format(**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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/resource_locks.py0000664000175000017500000001132115221154414022762 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 f"" 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(f"/resource-locks/{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: {}.'.format( ', '.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: {}.'.format( ', '.join(constants.SORT_DIR_VALUES) ) ) query_string = self._build_query_string(search_opts) path = f"/resource-locks{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(f"/resource-locks/{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(f"/resource-locks/{base.getid(lock)}") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/scheduler_stats.py0000664000175000017500000000245215221154414023141 0ustar00zuulzuul# 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 f"" 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 = f'{RESOURCES_PATH}/detail{query_string}' else: path = f'{RESOURCES_PATH}{query_string}' return self._list(path, RESOURCES_NAME) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/security_services.py0000664000175000017500000002266415221154414023526 0ustar00zuulzuul# 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 f"" 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( # noqa self, type, dns_ip=None, ou=None, server=None, 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( # noqa self, security_service, dns_ip=None, ou=None, 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/services.py0000664000175000017500000000754215221154414021575 0ustar00zuulzuul# 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 f"" 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(f"{resource_path}/enable", 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(f"{resource_path}/disable", 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 ) @api_versions.wraps("2.86") def ensure_shares(self, host): # noqa resource_path = f'{RESOURCE_PATH}/ensure-shares' body = {"host": host} return self.api.client.post(resource_path, body=body) 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 [] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_access_rules.py0000664000175000017500000000671015221154414023603 0ustar00zuulzuul# 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 f"" 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 access: 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 access: either share access rule object or text with its ID. :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.88") def set_access_level(self, access, access_level): """Set or update access level for share access rule. :param access: either share access rule object or text with its ID. :param access_level: value of access_level (e.g. ro/rw) """ body = {'update_access': {'access_level': access_level}} access_id = base.getid(access) url = RESOURCE_PATH % access_id return self._update(url, body) @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') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_backups.py0000664000175000017500000001176515221154414022566 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 f"" 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: {}.'.format( ', '.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: {}.'.format( ', '.join(constants.SORT_DIR_VALUES) ) ) query_string = self._build_query_string(search_opts) if detailed: path = f"/share-backups/detail{query_string}" else: path = f"/share-backups{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", "2.90") @api_versions.experimental_api def restore(self, backup_id): return self._action('restore', backup_id) @api_versions.wraps("2.91") @api_versions.experimental_api def restore(self, backup_id, target_share_id=None): # noqa F811 return self._action('restore', backup_id, info=target_share_id) @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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_export_locations.py0000664000175000017500000000477615221154414024536 0ustar00zuulzuul# 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 f"" def __getitem__(self, key): return self._info[key] class ShareExportLocationManager(base.MetadataCapableManager): """Manage :class:`ShareExportLocation` resources.""" resource_class = ShareExportLocation resource_path = '/shares' subresource_path = '/export_locations' @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( f"/shares/{share_id}/export_locations", "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( f"/shares/{share_id}/export_locations/{export_location_id}", "export_location", ) @api_versions.wraps('2.87') def get_metadata(self, share, share_export_location): return super().get_metadata(share, subresource=share_export_location) @api_versions.wraps('2.87') def set_metadata(self, resource, metadata, subresource=None): return super().set_metadata( resource, metadata, subresource=subresource ) @api_versions.wraps('2.87') def delete_metadata(self, resource, keys, subresource=None): return super().delete_metadata(resource, keys, subresource=subresource) @api_versions.wraps('2.87') def update_all_metadata(self, resource, metadata, subresource=None): return super().update_all_metadata( resource, metadata, subresource=subresource ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_group_snapshots.py0000664000175000017500000002221415221154414024363 0ustar00zuulzuul# 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 f"" 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: {}.'.format( ', '.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( # noqa self, detailed=True, search_opts=None, sort_key=None, sort_dir=None, ): return self._list_share_group_snapshots( detailed=detailed, search_opts=search_opts, sort_key=sort_key, sort_dir=sort_dir, ) def _update_share_group_snapshot(self, share_group_snapshot, **kwargs): """Updates a share group snapshot. :param share_group_snapshot: either ShareGroupSnapshot object or text with its UUID :rtype: :class:`ShareGroupSnapshot` """ share_group_snapshot_id = 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_group_type_access.py0000664000175000017500000000703615221154414024650 0ustar00zuulzuul# 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 f"" 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_group_types.py0000664000175000017500000001622615221154414023513 0ustar00zuulzuul# 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().__init__(manager, info, loaded) self._group_specs = info.get(GROUP_SPECS_RESOURCES_NAME, {}) def __repr__(self): return f"" @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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_groups.py0000664000175000017500000002656415221154414022460 0ustar00zuulzuul# 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 f"" 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( # noqa self, share_group_type=None, share_types=None, share_network=None, name=None, description=None, source_share_group_snapshot=None, availability_zone=None, ): return self._create_share_group( share_group_type=share_group_type, share_types=share_types, share_network=share_network, name=name, description=description, source_share_group_snapshot=source_share_group_snapshot, availability_zone=availability_zone, ) def _get_share_group(self, share_group): """Get a share group. :param share_group: either ShareGroup object or text with its UUID :rtype: :class:`ShareGroup` """ share_group_id = 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: {}.'.format( ', '.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( # noqa self, detailed=True, search_opts=None, sort_key=None, sort_dir=None, ): return self._list_share_groups( detailed=detailed, search_opts=search_opts, sort_key=sort_key, sort_dir=sort_dir, ) def _update_share_group(self, share_group, **kwargs): """Updates a share group. :param share_group: either ShareGroup object or text with its UUID :rtype: :class:`ShareGroup` """ share_group_id = 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") @api_versions.experimental_api 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_instance_export_locations.py0000664000175000017500000000356415221154414026414 0ustar00zuulzuul# 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 f"" 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( f"/share_instances/{share_instance_id}/export_locations", "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( ( f"/share_instances/{share_instance_id}/export_locations/" f"{export_location_id}" ), "export_location", ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_instances.py0000664000175000017500000001017615221154414023120 0ustar00zuulzuul# 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 f"" 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:`ShareInstance` 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(f"/share_instances/{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 = f'/share_instances/{base.getid(instance)}/action' 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") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_network_subnets.py0000664000175000017500000001161215221154414024361 0ustar00zuulzuul# 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 f"" 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 = f'/share-networks/{share_network_id}/subnets' 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( # noqa self, neutron_net_id=None, neutron_subnet_id=None, 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 share_network_subnet: share network subnet to get. :rtype: :class:`ShareNetworkSubnet` """ share_network_id = base.getid(share_network) share_network_subnet_id = base.getid(share_network_subnet) url = ( f'/share-networks/{share_network_id}/subnets' f'/{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 = ( f'/share-networks/{base.getid(share_network)}/subnets' f'/{share_network_subnet}' ) self._delete(url) @api_versions.wraps('2.78') def get_metadata(self, share_network, share_network_subnet): return super().get_metadata( share_network, subresource=share_network_subnet ) @api_versions.wraps('2.78') def set_metadata(self, resource, metadata, subresource=None): return super().set_metadata( resource, metadata, subresource=subresource ) @api_versions.wraps('2.78') def delete_metadata(self, resource, keys, subresource=None): return super().delete_metadata(resource, keys, subresource=subresource) @api_versions.wraps('2.78') def update_all_metadata(self, resource, metadata, subresource=None): return super().update_all_metadata( resource, metadata, subresource=subresource ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_networks.py0000664000175000017500000003126415221154414023006 0ustar00zuulzuul# 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 f"" 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( # noqa self, neutron_net_id=None, neutron_subnet_id=None, name=None, description=None, ): """Create share network. :param neutron_net_id: ID of Neutron network :param neutron_subnet_id: ID of Neutron subnet :param name: share network name :param description: share network description :rtype: :class:`ShareNetwork` """ values = {} if neutron_net_id: values['neutron_net_id'] = neutron_net_id if neutron_subnet_id: values['neutron_subnet_id'] = neutron_subnet_id if name: values['name'] = name if description: values['description'] = description body = {RESOURCE_NAME: values} return self._create(RESOURCES_PATH, body, RESOURCE_NAME) @api_versions.wraps("2.51") # noqa def create( # noqa self, neutron_net_id=None, neutron_subnet_id=None, name=None, description=None, availability_zone=None, ): values = {} if neutron_net_id: values['neutron_net_id'] = neutron_net_id if neutron_subnet_id: values['neutron_subnet_id'] = neutron_subnet_id if name: values['name'] = name if description: values['description'] = description if availability_zone: values['availability_zone'] = availability_zone body = {RESOURCE_NAME: values} return self._create(RESOURCES_PATH, body, RESOURCE_NAME) def 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 share_network: share network to get. :rtype: :class:`ShareNetwork` """ 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( # noqa self, share_network, neutron_net_id=None, neutron_subnet_id=None, name=None, description=None, ): """Updates a share network. :param share_network: share network to update. :rtype: :class:`ShareNetwork` """ values = {} if neutron_net_id is not None: values['neutron_net_id'] = neutron_net_id if neutron_subnet_id is not None: values['neutron_subnet_id'] = neutron_subnet_id if name is not None: values['name'] = name if description is not None: values['description'] = description for k, v in values.items(): if v == '': values[k] = None if not values: msg = "Must specify fields to be updated" raise exceptions.CommandError(msg) body = {RESOURCE_NAME: values} return self._update( RESOURCE_PATH % 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:`ShareNetwork` """ 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 ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_replica_export_locations.py0000664000175000017500000000550415221154414026223 0ustar00zuulzuul# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 f"" def __getitem__(self, key): return self._info[key] class ShareReplicaExportLocationManager(base.ManagerWithFind): """Manage :class:`ShareReplicaExportLocation` 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( f"/share-replicas/{share_replica_id}/export-locations", "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( f"/share-replicas/{share_replica_id}/export-locations", "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( ( f"/share-replicas/{share_replica_id}/export-locations/" f"{export_location_id}" ), "export_location", ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_replicas.py0000664000175000017500000003050715221154414022733 0ustar00zuulzuul# 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.MetadataCapableResource): """A replica is 'mirror' instance of a share at some point in time.""" def __repr__(self): return f"" 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.MetadataCapableManager): """Manage :class:`ShareReplica` resources.""" resource_class = ShareReplica resource_path = '/share-replicas' @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: dict of search options (e.g., metadata filters) :rtype: list of :class:`ShareReplica` """ search_opts = search_opts or {} # This will turn {'metadata': {'foo': 'bar', 'baz': 'qux'}} # into ?metadata=foo:bar,baz:qux # For nested search options like metadata, # stringified inner dict is expected. # e.g., {'metadata': 'foo:bar,baz:qux'} query_string = self._build_query_string(search_opts) if share: share_id = '?share_id=' + base.getid(share) url = RESOURCES_PATH + '/detail' + share_id if query_string: url += '&' + query_string.lstrip('?') return self._list(url, RESOURCES_NAME) else: url = RESOURCES_PATH + '/detail' if query_string: url += query_string return self._list(url, 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", "2.94") # noqa def create( # noqa 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, ) @api_versions.wraps("2.95") # noqa def create( # noqa self, share, # pylint: disable=function-redefined # noqa F811 availability_zone=None, scheduler_hints=None, share_network=None, metadata=None, ): return self._create_share_replica( share, availability_zone=availability_zone, scheduler_hints=scheduler_hints, share_network=share_network, metadata=metadata, ) def _create_share_replica( self, share, availability_zone=None, scheduler_hints=None, share_network=None, metadata=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. :param metadata: dict - optional metadata to set on share replica creation """ 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) if metadata: body['metadata'] = metadata 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}) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_servers.py0000664000175000017500000002533015221154414022620 0ustar00zuulzuul# 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 f"" def __getattr__(self, attr): if attr == 'share_network': attr = 'share_network_name' return super().__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(f"{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[f"details:{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(f"{RESOURCES_PATH}/{server_id}/details", "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( # noqa self, host, share_network_id, identifier, share_network_subnet_id=None, driver_options=None, ): driver_options = driver_options or {} body = { 'host': host, 'share_network_id': share_network_id, 'identifier': identifier, 'share_network_subnet_id': share_network_subnet_id, 'driver_options': driver_options, } resource_path = RESOURCE_PATH % 'manage' return self._create( resource_path, {'share_server': body}, 'share_server' ) @api_versions.wraps("2.49") def unmanage(self, share_server, force=False): return self._action("unmanage", share_server, {'force': force}) @api_versions.wraps("2.49") def reset_state(self, share_server, state): """Update the provided share server with the provided state. :param share_server: either share_server object or text with its ID. :param state: text with new state to set for share. """ return self._action("reset_status", share_server, {"status": state}) def _action(self, action, share_server, info=None): """Perform a share server 'action'. :param action: text with action name. :param share_server: either share_server object or text with its ID. :param info: dict with data for specified 'action'. """ 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] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_snapshot_export_locations.py0000664000175000017500000000341715221154414026444 0ustar00zuulzuul# 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 f"" 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( f"/snapshots/{base.getid(snapshot)}/export-locations", '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}/export-locations/" "{export_location_id}".format(**params), "share_snapshot_export_location", ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_snapshot_instance_export_locations.py0000664000175000017500000000360115221154414030323 0ustar00zuulzuul# 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 f"" 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( f"/snapshot-instances/{base.getid(snapshot_instance)}/export-locations", '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}/" "export-locations/{export_location_id}".format(**params), "share_snapshot_export_location", ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_snapshot_instances.py0000664000175000017500000000516115221154414025035 0ustar00zuulzuul# 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 f"" 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( f"/snapshot-instances/{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 += f'?snapshot_id={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 = f'/snapshot-instances/{base.getid(instance)}/action' return self.api.client.post(url, body=body) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_snapshots.py0000664000175000017500000002230115221154414023144 0ustar00zuulzuul# 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 f"" 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( # noqa self, share, force=False, name=None, description=None, 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(f'/snapshots/{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: {}.'.format( ', '.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: {}.'.format( ', '.join(constants.SORT_DIR_VALUES) ) ) query_string = self._build_query_string(search_opts) if detailed: path = f"/snapshots/detail{query_string}" else: path = f"/snapshots{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(f"/snapshots/{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(f"/snapshots/{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( f"/snapshots/{snapshot_id}/access-list", '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 = f'/snapshots/{base.getid(snapshot)}/action' return self.api.client.post(url, body=body) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_transfers.py0000664000175000017500000001102515221154414023132 0ustar00zuulzuul# 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 f"" 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(f'/share-transfers/{transfer_id}/accept', 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(f"/share-transfers/{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: {}.'.format( ', '.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: {}.'.format( ', '.join(constants.SORT_DIR_VALUES) ) ) query_string = self._build_query_string(search_opts) if detailed: path = f"/share-transfers/detail{query_string}" else: path = f"/share-transfers{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(f"/share-transfers/{base.getid(transfer_id)}") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_type_access.py0000664000175000017500000000437015221154414023432 0ustar00zuulzuul# 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 f"" 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( f"/types/{base.getid(share_type)}/{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 = f'/types/{base.getid(share_type)}/action' return self.api.client.post(url, body=body) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/share_types.py0000664000175000017500000002447715221154414022306 0ustar00zuulzuul# 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().__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 f"" @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( f"/types/{base.getid(self)}/extra_specs" ) 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( f"/types/{base.getid(self)}/extra_specs", 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( f"/types/{base.getid(self)}/extra_specs/{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(f"/types{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(f"/types/{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(f"/types/{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(f"/types/{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( f"/types/{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( # noqa self, name, spec_driver_handles_share_servers, spec_snapshot_support=None, is_public=True, extra_specs=None, ): if extra_specs is None: extra_specs = {} self._handle_spec_driver_handles_share_servers( extra_specs, spec_driver_handles_share_servers ) self._handle_spec_snapshot_support( extra_specs, spec_snapshot_support, set_default=True ) return self._do_create(name, extra_specs, is_public) @api_versions.wraps("2.24", "2.40") # noqa def create( # noqa self, name, spec_driver_handles_share_servers, spec_snapshot_support=None, is_public=True, extra_specs=None, ): if extra_specs is None: extra_specs = {} self._handle_spec_driver_handles_share_servers( extra_specs, spec_driver_handles_share_servers ) self._handle_spec_snapshot_support(extra_specs, spec_snapshot_support) return self._do_create(name, extra_specs, is_public) @api_versions.wraps("2.41") # noqa def create( # noqa self, name, spec_driver_handles_share_servers, spec_snapshot_support=None, is_public=True, extra_specs=None, description=None, ): if extra_specs is None: extra_specs = {} self._handle_spec_driver_handles_share_servers( extra_specs, spec_driver_handles_share_servers ) self._handle_spec_snapshot_support(extra_specs, spec_snapshot_support) return self._do_create( name, extra_specs, is_public, description=description ) @api_versions.wraps("2.50") def update(self, share_type, name=None, is_public=None, description=None): return self._do_update( share_type, name, is_public, description=description ) def _handle_spec_driver_handles_share_servers( self, extra_specs, spec_driver_handles_share_servers ): """Validation and default for DHSS extra spec.""" if spec_driver_handles_share_servers is not None: if 'driver_handles_share_servers' in extra_specs: msg = ( "'driver_handles_share_servers' is already set via " "positional argument." ) raise exceptions.CommandError(msg) else: extra_specs['driver_handles_share_servers'] = ( spec_driver_handles_share_servers ) else: msg = ( "'driver_handles_share_servers' is not set via " "positional argument." ) raise exceptions.CommandError(msg) def _handle_spec_snapshot_support( self, extra_specs, spec_snapshot_support, set_default=False ): """Validation and default for snapshot extra spec.""" if spec_snapshot_support is not None: if 'snapshot_support' in extra_specs: msg = "'snapshot_support' extra spec is provided twice." raise exceptions.CommandError(msg) else: extra_specs['snapshot_support'] = spec_snapshot_support elif 'snapshot_support' not in extra_specs and set_default: extra_specs['snapshot_support'] = True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/manilaclient/v2/shares.py0000664000175000017500000011273115221154414021234 0ustar00zuulzuul# 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 f"" 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, mount_point_name=None, encryption_key_ref=None, ): """Create a share. :param share_proto: text - share protocol for new share available values are NFS, CIFS, CephFS, GlusterFS, HDFS and MAPRFS. :param size: int - size in GiB :param snapshot_id: text - ID of the snapshot :param name: text - name of new share :param description: text - description of a share :param metadata: dict - optional metadata to set on share creation :param share_network: either instance of ShareNetwork or text with ID :param share_type: either instance of ShareType or text with ID :param is_public: bool, whether to set share as public or not. :param share_group_id: text - ID of the share group to which the share should belong :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 :param mount_point_name: text - share human-readable mount point name. This name will be reflected in export location once share is created. :param encryption_key_ref: text - encryption key reference, i.e. UUID of the secret stored in the key manager. :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 if mount_point_name: body['mount_point_name'] = mount_point_name if encryption_key_ref: body['encryption_key_ref'] = encryption_key_ref return self._create( '/shares', {'share': body}, 'share', return_raw=return_raw ) @api_versions.wraps( "2.29", constants.SHARE_MIGRATION_PRE_GRADUATION_VERSION ) @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._migration_start( share, host, force_host_assisted_migration, preserve_metadata, writable, nondisruptive, preserve_snapshots, new_share_network_id, new_share_type_id, ) @api_versions.wraps(constants.SHARE_MIGRATION_GRADUATION_VERSION) # noqa def migration_start( # noqa F811 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._migration_start( share, host, force_host_assisted_migration, preserve_metadata, writable, nondisruptive, preserve_snapshots, new_share_network_id, new_share_type_id, ) 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", constants.SHARE_MIGRATION_PRE_GRADUATION_VERSION ) @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._reset_task_state(share, task_state) @api_versions.wraps(constants.SHARE_MIGRATION_GRADUATION_VERSION) # noqa def reset_task_state(self, share, task_state): # noqa F811 """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._reset_task_state(share, task_state) def _reset_task_state(self, share, task_state): return self._action( 'reset_task_state', share, {"task_state": task_state} ) @api_versions.wraps( "2.22", constants.SHARE_MIGRATION_PRE_GRADUATION_VERSION ) @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(constants.SHARE_MIGRATION_GRADUATION_VERSION) # noqa def migration_complete(self, share): # noqa F811 """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", constants.SHARE_MIGRATION_PRE_GRADUATION_VERSION ) @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(constants.SHARE_MIGRATION_GRADUATION_VERSION) # noqa def migration_cancel(self, share): # noqa F811 """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", constants.SHARE_MIGRATION_PRE_GRADUATION_VERSION ) @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) @api_versions.wraps(constants.SHARE_MIGRATION_GRADUATION_VERSION) # noqa def migration_get_progress(self, share): # noqa F811 """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, mount_point_name=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 :param mount_point_name: text - mount point name of 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 if mount_point_name is not None: body['mount_point_name'] = mount_point_name 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( # noqa self, service_host, protocol, export_path, driver_options=None, share_type=None, name=None, description=None, ): return self._do_manage( service_host, protocol, export_path, driver_options=driver_options, share_type=share_type, name=name, description=description, resource_path="/shares/manage", ) @api_versions.wraps("2.8", "2.48") # noqa def manage( # noqa self, service_host, protocol, export_path, driver_options=None, share_type=None, name=None, description=None, is_public=False, ): return self._do_manage( service_host, protocol, export_path, driver_options=driver_options, share_type=share_type, name=name, description=description, is_public=is_public, resource_path="/shares/manage", ) @api_versions.wraps("2.49", "2.91") # noqa def manage( # noqa self, service_host, protocol, export_path, driver_options=None, share_type=None, name=None, description=None, is_public=False, share_server_id=None, ): return self._do_manage( service_host, protocol, export_path, driver_options=driver_options, share_type=share_type, name=name, description=description, is_public=is_public, share_server_id=share_server_id, resource_path="/shares/manage", ) @api_versions.wraps("2.92") # noqa def manage( # noqa self, service_host, protocol, export_path, driver_options=None, share_type=None, name=None, description=None, is_public=False, share_server_id=None, mount_point_name=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, mount_point_name=mount_point_name, 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( f"/os-share-unmanage/{base.getid(share)}/unmanage" ) @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(f"/shares/{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(f"/shares/{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) search_opts.pop("encryption_key_ref", 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( # noqa self, detailed=True, search_opts=None, 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) search_opts.pop("encryption_key_ref", 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", "2.89") # noqa def list( # noqa 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("encryption_key_ref", 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.90") # noqa def list( # noqa self, detailed=True, search_opts=None, 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) - ('encryption_key_ref', text) Note, that member context will have restricted set of available search opts. For admin context filtering also available by each share attr from its Model. So, this list is not full for admin context. :param sort_key: Key to be sorted (i.e. 'created_at' or 'status'). :param sort_dir: Sort direction, should be 'desc' or 'asc'. :rtype: list of :class:`Share` """ if search_opts is None: search_opts = {} if sort_key is not None: if sort_key in constants.SHARE_SORT_KEY_VALUES: search_opts['sort_key'] = sort_key # NOTE(vponomaryov): Replace aliases with appropriate keys if sort_key == 'share_type': search_opts['sort_key'] = 'share_type_id' elif sort_key == 'snapshot': search_opts['sort_key'] = 'snapshot_id' elif sort_key == 'share_network': search_opts['sort_key'] = 'share_network_id' elif sort_key == 'availability_zone': search_opts['sort_key'] = 'availability_zone_id' else: raise ValueError( 'sort_key must be one of the following: {}.'.format( ', '.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: {}.'.format( ', '.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 = f"/shares/detail{query_string}" else: path = f"/shares{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 = f"/shares/{base.getid(share)}" if share_group_id: url += f"?share_group_id={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: {}'.format( ', '.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( # noqa 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 = f'/shares/{base.getid(share)}/action' 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( f'/shares/{base.getid(share)}/instances', 'share_instances', manager=share_instances.ShareInstanceManager(self), ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0800912 python_manilaclient-6.2.0/playbooks/0000775000175000017500000000000015221154502016402 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/playbooks/enable-fips.yaml0000664000175000017500000000005015221154414021450 0ustar00zuulzuul- hosts: all roles: - enable-fips ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0810912 python_manilaclient-6.2.0/playbooks/python-manilaclient-functional/0000775000175000017500000000000015221154502024521 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/playbooks/python-manilaclient-functional/post.yaml0000664000175000017500000000015415221154414026374 0ustar00zuulzuul- hosts: all vars: tox_envlist: functional roles: - fetch-tox-output - fetch-subunit-output ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/playbooks/python-manilaclient-functional/run.yaml0000664000175000017500000000045615221154414026220 0ustar00zuulzuul- 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/pyproject.toml0000664000175000017500000003341115221154414017317 0ustar00zuulzuul[build-system] requires = ["pbr>=6.1.1"] build-backend = "pbr.build" [project] name = "python-manilaclient" description = "Client library for OpenStack Shared File System Storage" authors = [ {name = "OpenStack", email = "openstack-discuss@lists.openstack.org"}, ] readme = {file = "README.rst", content-type = "text/x-rst"} license = {text = "Apache-2.0"} dynamic = ["version", "dependencies"] requires-python = ">=3.10" classifiers = [ "Environment :: OpenStack", "Intended Audience :: Information Technology", "Intended Audience :: System Administrators", "License :: OSI Approved :: Apache Software License", "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", ] [project.urls] Homepage = "https://docs.openstack.org/python-manilaclient/" Repository = "https://opendev.org/openstack/python-manilaclient/" [project.entry-points."oslo.config.opts"] "manilaclient.config" = "manilaclient.config:list_opts" [project.entry-points."openstack.cli.extension"] share = "manilaclient.osc.plugin" [project.entry-points."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_export_location_set = "manilaclient.osc.v2.share:ShareExportLocationSet" share_export_location_unset = "manilaclient.osc.v2.share:ShareExportLocationUnset" 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_unset = "manilaclient.osc.v2.share_replicas:UnsetShareReplica" 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_service_ensure_shares = "manilaclient.osc.v2.services:EnsureShareService" 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" share_qos_type_create = "manilaclient.osc.v2.qos_types:CreateQosType" share_qos_type_list = "manilaclient.osc.v2.qos_types:ListQosType" share_qos_type_show = "manilaclient.osc.v2.qos_types:ShowQosType" share_qos_type_set = "manilaclient.osc.v2.qos_types:SetQosType" share_qos_type_unset = "manilaclient.osc.v2.qos_types:UnsetQosType" share_qos_type_delete = "manilaclient.osc.v2.qos_types:DeleteQosType" [tool.setuptools] packages = [ "manilaclient" ] [tool.coverage.run] omit = [ "manilaclient/tests/*", ] branch = true [tool.ruff] line-length = 79 [tool.ruff.format] quote-style = "preserve" docstring-code-format = true [tool.ruff.lint] select = ["E4", "E5", "E7", "E9", "F", "S", "UP"] [tool.ruff.lint.per-file-ignores] "manilaclient/tests/*" = ["S"] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.1280942 python_manilaclient-6.2.0/python_manilaclient.egg-info/0000775000175000017500000000000015221154502022132 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896961.0 python_manilaclient-6.2.0/python_manilaclient.egg-info/PKG-INFO0000644000175000017500000001250115221154501023223 0ustar00zuulzuulMetadata-Version: 2.4 Name: python-manilaclient Version: 6.2.0 Summary: Client library for OpenStack Shared File System Storage Author-email: OpenStack License: Apache-2.0 Project-URL: Homepage, https://docs.openstack.org/python-manilaclient/ Project-URL: Repository, https://opendev.org/openstack/python-manilaclient/ Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Requires-Python: >=3.10 Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: pbr>=3.0.0 Requires-Dist: oslo.config>=5.2.0 Requires-Dist: oslo.log>=3.36.0 Requires-Dist: oslo.serialization>=2.20.0 Requires-Dist: oslo.utils>=3.33.0 Requires-Dist: PrettyTable>=0.7.1 Requires-Dist: requests>=2.14.2 Requires-Dist: osc-lib>=3.2.0 Requires-Dist: keystoneauth1>=3.0.0 Requires-Dist: debtcollector>=1.2.0 Dynamic: license-file Dynamic: requires-dist ======================== 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 the OpenStack Manila API. You'll need to provide your OpenStack username and password. You can do this with the ``--os-username``, ``--os-password`` and ``--os-project-name`` params, but it's easier to just set them as environment variables:: export OS_USERNAME=foouser export OS_PASSWORD=barpass export OS_PROJECT_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/v3/ 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 v3 auth with http://example.com:5000/v3/ >>> from manilaclient.v2 import client >>> nt = client.Client("2", USER, PASS, PROJECT, 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896962.0 python_manilaclient-6.2.0/python_manilaclient.egg-info/SOURCES.txt0000664000175000017500000005673115221154502024032 0ustar00zuulzuul.git-blame-ignore-revs .pre-commit-config.yaml .stestr.conf AUTHORS CONTRIBUTING.rst ChangeLog HACKING LICENSE README.rst bindep.txt pyproject.toml requirements.txt setup.cfg setup.py test-requirements.txt tox.ini 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/adding_release_notes.rst doc/source/contributor/contributing.rst doc/source/contributor/functional-tests.rst doc/source/contributor/index.rst doc/source/user/api.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/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/qos_types.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/exceptions.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_qos_types.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_instance_export_locations.py manilaclient/tests/functional/osc/test_share_instances.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/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_qos_types.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_qos_types.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_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/qos_types.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/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-encryption-key-ref-option-for-share-create-api-035aec5b31d7c37b.yaml releasenotes/notes/add-ensure-shares-command-6cd854408de1979b.yaml releasenotes/notes/add-ensuring-filter-for-services-eedbead009d3505f.yaml releasenotes/notes/add-export-location-filter-4cf3114doe40k598.yaml releasenotes/notes/add-export-variable-for-api-endpoint-override-c00ea54e1670cb90.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-properties-column-to-replica-list-a3f7c91d28e05b4c.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-replica-metadata-e6c1c2e00295d2dd.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-for-qos-type-and-specs-2b5ce0002721cdb3.yaml releasenotes/notes/add-support-of-api-endpoint-override-e36e75246d39a367.yaml releasenotes/notes/add-support-to-check-quota-usage-scfdg14dod40k71a.yaml releasenotes/notes/add-wait-flag-share-server-unmanage-819dd42abc2a4d2b.yaml releasenotes/notes/add_export_location_metadata-e023dccf4c28bd76.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-manage-with-mount-point-name-69be3a8aa4fcaa1f.yaml releasenotes/notes/bp-ocata-migration-improvements-f63c5d233856fbee.yaml releasenotes/notes/bp-pass-share-network-subnet-metadata-updates-to-backend-drivers-61135da17758d9f2.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-1782847-filter-list-resources-with-empty-metadata-key-value-cc6f858994f6e9ee.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-1898307-add-wait-to-create-revert-delete-a-snapshot-20271b8ebb60ade5.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-1898310-add-wait-to-create-promote-delete-share-replica-35f17e4a51b8f62e.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-1898319-add-wait-to-create-delete-a-share-group-snapshot-f789e324f66ad1b6.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-1989818-fix-share-network-output-format-93d997f0f4a33fab.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-2051394-fix-validate-neutron-info-ebe679675111eec9.yaml releasenotes/notes/bug-2051737-fix-share-force-delete-request-6d2578fb7da61e3f.yaml releasenotes/notes/bug-2057951-fix-client-region-name-67fed93b76429b63.yaml releasenotes/notes/bug-2065769-fix-share-network-create-fa76f38f1b640001.yaml releasenotes/notes/bug-2066871-allow-to-update-access-level-for-access-rule-adb5d572454c01e8.yaml releasenotes/notes/bug-2068631-add-filter-by-source-share-server-id-d40500dcf184c321.yaml releasenotes/notes/bug-2068732-fix-migration-get-progress-on-source-share-server-7ef327dcdedcfbd6.yaml releasenotes/notes/bug-2108991-fix-task-state-none-handling-in-osc-share-set-307f3f4a2b232dd8.yaml releasenotes/notes/bug-2109376-fix-ensure-shares-command-69feb1794fea418a.yaml releasenotes/notes/bug-2132937-update-share-group-fix-41558fef427c84a2.yaml releasenotes/notes/bug-913201-add-wait-to-manage-unmanage-snapshot-9cc017a15733120f.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-keystoneauth-params-ebbaaf6be4df4bb7.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-migration-feature-3b8fa72d4e91a517.yaml releasenotes/notes/graduate-share-replication-feature-49770e921b4338fb.yaml releasenotes/notes/handle-missing-api-minor-version-5a6d242f28883442.yaml releasenotes/notes/human-readable-export-location-share-support-d156f23669e458fa.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-identity-v2-f62956e7d54b8c53.yaml releasenotes/notes/remove-nova-net-id-arg-5b01f0077be171ed.yaml releasenotes/notes/remove-nova-net-id-option-for-share-nets-82e424b75221528b.yaml releasenotes/notes/remove-py38-0326744b43229e21.yaml releasenotes/notes/remove-py39-5278e896dc045854.yaml releasenotes/notes/remove-python-client-a142b99ccc89731c.yaml releasenotes/notes/share-backup-98e11c6a28897e94.yaml releasenotes/notes/share-backup-out-of-place-restore-98740badef59ba32.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/2024.1.rst releasenotes/source/2024.2.rst releasenotes/source/2025.1.rst releasenotes/source/2025.2.rst releasenotes/source/2026.1.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 zuul.d/project.yaml zuul.d/python-manilaclient-jobs.yaml././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896961.0 python_manilaclient-6.2.0/python_manilaclient.egg-info/dependency_links.txt0000664000175000017500000000000115221154501026177 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896961.0 python_manilaclient-6.2.0/python_manilaclient.egg-info/entry_points.txt0000664000175000017500000002776415221154501025447 0ustar00zuulzuul[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_set = manilaclient.osc.v2.share:ShareExportLocationSet share_export_location_show = manilaclient.osc.v2.share:ShareExportLocationShow share_export_location_unset = manilaclient.osc.v2.share:ShareExportLocationUnset 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_qos_type_create = manilaclient.osc.v2.qos_types:CreateQosType share_qos_type_delete = manilaclient.osc.v2.qos_types:DeleteQosType share_qos_type_list = manilaclient.osc.v2.qos_types:ListQosType share_qos_type_set = manilaclient.osc.v2.qos_types:SetQosType share_qos_type_show = manilaclient.osc.v2.qos_types:ShowQosType share_qos_type_unset = manilaclient.osc.v2.qos_types:UnsetQosType 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_replica_unset = manilaclient.osc.v2.share_replicas:UnsetShareReplica 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_ensure_shares = manilaclient.osc.v2.services:EnsureShareService 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896961.0 python_manilaclient-6.2.0/python_manilaclient.egg-info/not-zip-safe0000664000175000017500000000000115221154501024357 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896961.0 python_manilaclient-6.2.0/python_manilaclient.egg-info/pbr.json0000664000175000017500000000005615221154501023610 0ustar00zuulzuul{"git_version": "8b34b1b", "is_release": true}././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896961.0 python_manilaclient-6.2.0/python_manilaclient.egg-info/requires.txt0000664000175000017500000000027215221154501024532 0ustar00zuulzuulpbr>=3.0.0 oslo.config>=5.2.0 oslo.log>=3.36.0 oslo.serialization>=2.20.0 oslo.utils>=3.33.0 PrettyTable>=0.7.1 requests>=2.14.2 osc-lib>=3.2.0 keystoneauth1>=3.0.0 debtcollector>=1.2.0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896961.0 python_manilaclient-6.2.0/python_manilaclient.egg-info/top_level.txt0000664000175000017500000000001515221154501024657 0ustar00zuulzuulmanilaclient ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0830915 python_manilaclient-6.2.0/rally-jobs/0000775000175000017500000000000015221154502016455 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/rally-jobs/rally-manila-no-ss.yaml0000664000175000017500000000354215221154414022766 0ustar00zuulzuul--- 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/rally-jobs/rally-manila.yaml0000664000175000017500000001022715221154414021727 0ustar00zuulzuul--- 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 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1782896962.016087 python_manilaclient-6.2.0/releasenotes/0000775000175000017500000000000015221154502017070 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.1210938 python_manilaclient-6.2.0/releasenotes/notes/0000775000175000017500000000000015221154502020220 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/.placeholder0000664000175000017500000000000015221154414022473 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000022200000000000010211 xustar00124 path=python_manilaclient-6.2.0/releasenotes/notes/Bug-1990013-fix-share-grou-from-snapshot-create-fa3629cf1417ba20.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/Bug-1990013-fix-share-grou-from-snapshot-create-fa3629c0000664000175000017500000000020615221154414031454 0ustar00zuulzuul--- fixes: - | Create Share group from snapshot failed due to a bug in OSC. The issue is fixed and its working as expected. ././@PaxHeader0000000000000000000000000000021300000000000010211 xustar00117 path=python_manilaclient-6.2.0/releasenotes/notes/add-access-visibility-and-deletion-locks-69978f052e25334c.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-access-visibility-and-deletion-locks-69978f052e25330000664000175000017500000000055715221154414031565 0ustar00zuulzuul--- 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`. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-count-info-in-share-21a6b36c0f4c87b2.yaml0000664000175000017500000000012315221154414027622 0ustar00zuulzuul--- features: - Added ``with_count`` option in share's list commands since 2.42. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-count-info-in-snapshot-c9600adad648a486.yaml0000664000175000017500000000012715221154414030374 0ustar00zuulzuul--- features: - Added ``count`` option in share snapshot's list commands since 2.79. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-dash-dash-help-subcomand-ad4226454aa07bc6.yaml0000664000175000017500000000013215221154414030564 0ustar00zuulzuul--- fixes: - Allow --help to print subcomands info. e.g. ``$ manila create --help`` ././@PaxHeader0000000000000000000000000000021000000000000010206 xustar00114 path=python_manilaclient-6.2.0/releasenotes/notes/add-defaultadsite-to-security-service-33fd0a5d1b865b11.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-defaultadsite-to-security-service-33fd0a5d1b865b11.0000664000175000017500000000037215221154414031715 0ustar00zuulzuul--- 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 `_ ././@PaxHeader0000000000000000000000000000022500000000000010214 xustar00127 path=python_manilaclient-6.2.0/releasenotes/notes/add-encryption-key-ref-option-for-share-create-api-035aec5b31d7c37b.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-encryption-key-ref-option-for-share-create-api-035a0000664000175000017500000000115715221154414032301 0ustar00zuulzuul--- features: - | Starting microversion 2.90, Manila supports 'bring your own key' feature that allows user to pass encryption key reference in share creation. The encryption key reference must be the UUID of a secret encryption key that is stored on a key manager configured in the Shared File Systems service. Encryption key reference can belongs to either share or share-server which will be decided by storage driver capability and the share type. Added 'encryption_keys' per project quota which controls how many share and share server encryption keys will be created within project. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-ensure-shares-command-6cd854408de1979b.yaml0000664000175000017500000000025515221154414030212 0ustar00zuulzuul--- features: - | Starting from API version 2.86, it is possible to run ensure shares on a given manila-share host, in order to update the shares information. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-ensuring-filter-for-services-eedbead009d3505f.yaml0000664000175000017500000000023415221154414032004 0ustar00zuulzuul--- features: - | It is now possible to filter services that have their status set to `ensuring` with a new option in the `service list` command. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-export-location-filter-4cf3114doe40k598.yaml0000664000175000017500000000015615221154414030510 0ustar00zuulzuul--- features: - Shares and share instances can now be filtered with their export location IDs or paths. ././@PaxHeader0000000000000000000000000000022000000000000010207 xustar00122 path=python_manilaclient-6.2.0/releasenotes/notes/add-export-variable-for-api-endpoint-override-c00ea54e1670cb90.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-export-variable-for-api-endpoint-override-c00ea54e10000664000175000017500000000047315221154414032256 0ustar00zuulzuul--- fixes: - | In addition to existing "--os-endpoint-override" option to override API endpoint, user can also use export variable. For example, use "export OS_SHARED_FILE_SYSTEM_ENDPOINT_OVERRIDE=" and then use "openstack share list" command to refer different Manila API endpoint. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-ipv6-access-type-4dko90r1a9a1e0b8.yaml0000664000175000017500000000022315221154414027341 0ustar00zuulzuul--- features: - IPv6 ACL support has been added in manila. access-allow command now validates and allows access control to IPv6 clients. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-like-filter-591572762357ef4b.yaml0000664000175000017500000000047315221154414026060 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-message-list-and-delete-41b3323edd63d894.yaml0000664000175000017500000000013215221154414030363 0ustar00zuulzuul--- features: - Added new subcommands message-list, message-show and message-delete. ././@PaxHeader0000000000000000000000000000020500000000000010212 xustar00111 path=python_manilaclient-6.2.0/releasenotes/notes/add-metadata-for-share-access-rule-11a6b36c0f4c87c2.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-metadata-for-share-access-rule-11a6b36c0f4c87c2.yam0000664000175000017500000000034715221154414031541 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-per-share-gigabytes-quotas-40bc404bd3cbdd89.yaml0000664000175000017500000000030415221154414031352 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000021000000000000010206 xustar00114 path=python_manilaclient-6.2.0/releasenotes/notes/add-properties-column-to-replica-list-a3f7c91d28e05b4c.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-properties-column-to-replica-list-a3f7c91d28e05b4c.0000664000175000017500000000044615221154414031746 0ustar00zuulzuul--- features: - | Added a ``Properties`` column to the ``openstack share replica list`` command output when using Manila API version 2.95 or newer. The column displays the metadata key/value pairs associated with each share replica, for example ``replication_policy='Sync'``. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-query-params-pools-list-api-12cf1s14ddf40kdd.yaml0000664000175000017500000000017615221154414031607 0ustar00zuulzuul--- features: - Added ``--detail`` argument to pool list command. - Added ``--share_type`` argument to pool list command. ././@PaxHeader0000000000000000000000000000021400000000000010212 xustar00118 path=python_manilaclient-6.2.0/releasenotes/notes/add-quiesce-wait-time-for-replica-promote-30d9fa66afc854f2.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-quiesce-wait-time-for-replica-promote-30d9fa66afc850000664000175000017500000000047715221154414032112 0ustar00zuulzuul--- 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 `_ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-quota-per-share-type-support-3b2708ea232e69bc.yaml0000664000175000017500000000007315221154414031553 0ustar00zuulzuul--- features: - Added support for per-share-type quotas. ././@PaxHeader0000000000000000000000000000020600000000000010213 xustar00112 path=python_manilaclient-6.2.0/releasenotes/notes/add-scheduler-hints-to-share-create-70d429cb0aaf8f11.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-scheduler-hints-to-share-create-70d429cb0aaf8f11.ya0000664000175000017500000000054515221154414031655 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000021600000000000010214 xustar00120 path=python_manilaclient-6.2.0/releasenotes/notes/add-scheduler-hints-to-share-replica-create-d152a3934e5ad3a7.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-scheduler-hints-to-share-replica-create-d152a3934e50000664000175000017500000000065715221154414031771 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-share-group-quotas-support-b6563cec58209a1d.yaml0000664000175000017500000000040315221154414031325 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-share-group-support-a3166f6ca4d06a81.yaml0000664000175000017500000000033015221154414030004 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000023600000000000010216 xustar00136 path=python_manilaclient-6.2.0/releasenotes/notes/add-share-network-sec-service-add-update-to-in-use-networks-ec7a60d07ebceaf4.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-share-network-sec-service-add-update-to-in-use-netw0000664000175000017500000000140615221154414032472 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-share-replica-metadata-e6c1c2e00295d2dd.yaml0000664000175000017500000000017315221154414030416 0ustar00zuulzuul--- features: - | Adds support to create/update/delete replica metadata (properties) with set and unset command. ././@PaxHeader0000000000000000000000000000022200000000000010211 xustar00124 path=python_manilaclient-6.2.0/releasenotes/notes/add-share-replicas-and-replica-gigabytes-quotas-909436c2b2420f2c.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-share-replicas-and-replica-gigabytes-quotas-909436c0000664000175000017500000000051515221154414032160 0ustar00zuulzuul--- 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.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-snapshot-instances-admin-api-3cf3114doe40k598.yaml0000664000175000017500000000013215221154414031550 0ustar00zuulzuul--- features: - Add list, show, and reset-status admin commands for snapshot instances. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-snapshot-metadata-49b0288bf2f12bf0.yaml0000664000175000017500000000024615221154414027464 0ustar00zuulzuulfeatures: - | Adds support to set snapshot property on snapshot create, filter list on snapshot property, and snapshot property set and unset. Only in OSC. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-subnet-metadata-82426986431b0179.yaml0000664000175000017500000000026515221154414026562 0ustar00zuulzuul--- features: - | Adds support to create share network subnet with properties and update the subnet properties with set and unset command (only with the OpenStackClient). ././@PaxHeader0000000000000000000000000000021300000000000010211 xustar00117 path=python_manilaclient-6.2.0/releasenotes/notes/add-support-filter-search-for-share-type-fdbaaa9510cc59dd.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-support-filter-search-for-share-type-fdbaaa9510cc590000664000175000017500000000011015221154414032167 0ustar00zuulzuul--- features: - Share types can now be filtered with its extra_specs. ././@PaxHeader0000000000000000000000000000020500000000000010212 xustar00111 path=python_manilaclient-6.2.0/releasenotes/notes/add-support-for-qos-type-and-specs-2b5ce0002721cdb3.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-support-for-qos-type-and-specs-2b5ce0002721cdb3.yam0000664000175000017500000000034115221154414031576 0ustar00zuulzuul--- features: - | Added support for QoS types and specs via the Python SDK and the ``openstack share qos type`` CLI commands (create, delete, list, show, set, unset). Available from microversion 2.94 and above. ././@PaxHeader0000000000000000000000000000020700000000000010214 xustar00113 path=python_manilaclient-6.2.0/releasenotes/notes/add-support-of-api-endpoint-override-e36e75246d39a367.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-support-of-api-endpoint-override-e36e75246d39a367.y0000664000175000017500000000040615221154414031553 0ustar00zuulzuul--- features: - Manilaclient is updated to consider API endpoint override option `--os-endpoint-override`. For example, "openstack share list" command for different API endpoint will be "openstack --os-endpoint-override share list". ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-support-to-check-quota-usage-scfdg14dod40k71a.yaml0000664000175000017500000000011715221154414032043 0ustar00zuulzuul--- features: - Add support to check quota usage by '--detail' in quota-show ././@PaxHeader0000000000000000000000000000020600000000000010213 xustar00112 path=python_manilaclient-6.2.0/releasenotes/notes/add-wait-flag-share-server-unmanage-819dd42abc2a4d2b.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add-wait-flag-share-server-unmanage-819dd42abc2a4d2b.ya0000664000175000017500000000027715221154414031723 0ustar00zuulzuul--- features: - | The command "manila share-server-unmanage" now accepts an optional "--wait" flag that allows users to let the client poll for the completion of the operation. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add_export_location_metadata-e023dccf4c28bd76.yaml0000664000175000017500000000017415221154414031344 0ustar00zuulzuul--- features: - | Adds support to set and unset share export location properties (only with the OpenStackClient). ././@PaxHeader0000000000000000000000000000020500000000000010212 xustar00111 path=python_manilaclient-6.2.0/releasenotes/notes/add_support_multiple_subnet_per_az-46145c3e90e097be.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/add_support_multiple_subnet_per_az-46145c3e90e097be.yam0000664000175000017500000000046615221154414032260 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000021300000000000010211 xustar00117 path=python_manilaclient-6.2.0/releasenotes/notes/bp-allow-locking-shares-against-deletion-89e51e27368cda46.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bp-allow-locking-shares-against-deletion-89e51e27368cda0000664000175000017500000000022215221154414032012 0ustar00zuulzuul--- features: - | Added SDK and OSC commands to create, view, update and delete resource locks, alongside support for API version 2.81. ././@PaxHeader0000000000000000000000000000021200000000000010210 xustar00116 path=python_manilaclient-6.2.0/releasenotes/notes/bp-export-locations-replica-az-commands-03aa32c08f59c42d.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bp-export-locations-replica-az-commands-03aa32c08f59c420000664000175000017500000000030515221154414031737 0ustar00zuulzuul--- 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.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bp-integrate-os-profiler-4818066dc2fbf7b7.yaml0000664000175000017500000000015315221154414030152 0ustar00zuulzuul--- features: - | OS profiler is now supported within the v2 client and the manilaclient shell. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bp-manage-with-mount-point-name-69be3a8aa4fcaa1f.yaml0000664000175000017500000000016415221154414031623 0ustar00zuulzuul--- features: - Updated CLI command for managing shares to accept ``mount_point_name`` parameter as a option. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bp-ocata-migration-improvements-f63c5d233856fbee.yaml0000664000175000017500000000052315221154414031616 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000024300000000000010214 xustar00141 path=python_manilaclient-6.2.0/releasenotes/notes/bp-pass-share-network-subnet-metadata-updates-to-backend-drivers-61135da17758d9f2.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bp-pass-share-network-subnet-metadata-updates-to-backen0000664000175000017500000000027215221154414032662 0ustar00zuulzuul--- upgrade: - | With microversion 2.89, share network subnet metadata updates will be passed to storage backend driver for processing driver specific things if supported. ././@PaxHeader0000000000000000000000000000022400000000000010213 xustar00126 path=python_manilaclient-6.2.0/releasenotes/notes/bp-support-group-spec-search-share-group-type-api-d5d9a6096f084b91.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bp-support-group-spec-search-share-group-type-api-d5d9a0000664000175000017500000000013015221154414032463 0ustar00zuulzuul--- features: - | Share group types can now be filtered with their `group_specs`. ././@PaxHeader0000000000000000000000000000021600000000000010214 xustar00120 path=python_manilaclient-6.2.0/releasenotes/notes/bp-support-query-user-messages-by-timestamp-34b70bba2d1b4d13.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bp-support-query-user-messages-by-timestamp-34b70bba2d10000664000175000017500000000023315221154414032324 0ustar00zuulzuul--- features: - Added ``since`` and ``before`` to messages list API. User messages can be queried by stimestamp with API version ``2.52`` and beyond.././@PaxHeader0000000000000000000000000000021400000000000010212 xustar00118 path=python_manilaclient-6.2.0/releasenotes/notes/bp-support-share-transfer-between-project-faefead551380eca.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bp-support-share-transfer-between-project-faefead5513800000664000175000017500000000014215221154414032331 0ustar00zuulzuul--- features: - Support transferring shares between projects starting from API version ``2.77``.././@PaxHeader0000000000000000000000000000021300000000000010211 xustar00117 path=python_manilaclient-6.2.0/releasenotes/notes/bp-update-share-type-name-or-description-32d98b5a42cd8090.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bp-update-share-type-name-or-description-32d98b5a42cd800000664000175000017500000000026415221154414031750 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000021200000000000010210 xustar00116 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1558995-fix-endpoint-not-found.yaml-a06a3b0ba5006718.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1558995-fix-endpoint-not-found.yaml-a06a3b0ba5006710000664000175000017500000000014115221154414031057 0ustar00zuulzuul--- fixes: - | Fix endpoint not found error when only the manilav2 endpoint is configured. ././@PaxHeader0000000000000000000000000000022500000000000010214 xustar00127 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1608664-add-support-columns-share-replica-list-885bd8c8b4bfa8f1.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1608664-add-support-columns-share-replica-list-885b0000664000175000017500000000011115221154414031637 0ustar00zuulzuul--- fixes: - Added "--columns" support for share-replica-list command. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1611506-allow-deletion-multiple-resources0000664000175000017500000000034715221154414030325 0ustar00zuulzuul--- 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.././@PaxHeader0000000000000000000000000000021200000000000010210 xustar00116 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1622540-add-missing-az-list-command-f90265de5c2f261b.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1622540-add-missing-az-list-command-f90265de5c2f2610000664000175000017500000000014015221154414031003 0ustar00zuulzuul--- fixes: - Added support for missing command to list manila service availability zones. ././@PaxHeader0000000000000000000000000000021100000000000010207 xustar00115 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1650774-share-instance-reset-state-9c4b26f44b5da0e9.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1650774-share-instance-reset-state-9c4b26f44b5da0e90000664000175000017500000000026315221154414031215 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1664877-c462bfad92ce03e5.yaml0000664000175000017500000000057415221154414025106 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1674915-allow-user-access-fix-495b3e42bdc985ec.yaml0000664000175000017500000000024315221154414031141 0ustar00zuulzuul--- fixes: - Changed user access name limit from 32 to 255 characters since there are security services that allow user names longer than 32 characters. ././@PaxHeader0000000000000000000000000000023000000000000010210 xustar00130 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1682726-remove-sort-key-export-location-from-list-885des26bd5ea2de.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1682726-remove-sort-key-export-location-from-list-80000664000175000017500000000010415221154414032024 0ustar00zuulzuul--- fixes: - Remove unused sort key export_location from list CLI.././@PaxHeader0000000000000000000000000000023200000000000010212 xustar00132 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1707303-fix-list-command-when-not-given-search-opts-c06af7b344e9cb91.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1707303-fix-list-command-when-not-given-search-opts0000664000175000017500000000023515221154414032006 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000024100000000000010212 xustar00139 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1709746-add-group-specs-in-share-group-type-create-command-f91265de5c2f251b.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1709746-add-group-specs-in-share-group-type-create-0000664000175000017500000000010115221154414031677 0ustar00zuulzuul--- fixes: - Added group specs in share group type create CLI. ././@PaxHeader0000000000000000000000000000024000000000000010211 xustar00138 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1717940-dash-dash-column-reports-nothing-for-capabilities-db8c1234fae91af5.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1717940-dash-dash-column-reports-nothing-for-capabi0000664000175000017500000000017015221154414032040 0ustar00zuulzuul--- fixes: - Fix Manila pool-list --column reports nothing for capabilities, And also format the detail contents. ././@PaxHeader0000000000000000000000000000021100000000000010207 xustar00115 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1724183-add-share-type-description-8c4b26f44b5da1e9.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1724183-add-share-type-description-8c4b26f44b5da1e90000664000175000017500000000007715221154414031202 0ustar00zuulzuul--- fixes: - Added description in list/create share type CLI.././@PaxHeader0000000000000000000000000000023200000000000010212 xustar00132 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1733494-allow-user-group-name-with-blank-access-fix-885a3e42bdc985ec.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1733494-allow-user-group-name-with-blank-access-fix0000664000175000017500000000016315221154414031777 0ustar00zuulzuul--- fixes: - Allows the use of blank in user group name since the AD allow user group name to include blank. ././@PaxHeader0000000000000000000000000000023600000000000010216 xustar00136 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1738917-and-1738918-fix-access-share-group-type-by-name-3a8760522c147f28.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1738917-and-1738918-fix-access-share-group-type-by-0000664000175000017500000000016615221154414031111 0ustar00zuulzuul--- fixes: - Fixed bugs 1738917 and 1738918. Names can now be used in commands pertaining to share group types. ././@PaxHeader0000000000000000000000000000024200000000000010213 xustar00140 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1782847-filter-list-resources-with-empty-metadata-key-value-cc6f858994f6e9ee.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1782847-filter-list-resources-with-empty-metadata-k0000664000175000017500000000035315221154414032144 0ustar00zuulzuul--- fixes: - | The --metadata command should handle empty metadata in proper way. Introduced "None" metadata tag to filter list which have empty. Example: "list --metadata None" will return only elements with metadata={} ././@PaxHeader0000000000000000000000000000022400000000000010213 xustar00126 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1798229-fix-create-share-from-snap-using-name-44100b907ea6a040.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1798229-fix-create-share-from-snap-using-name-441000000664000175000017500000000040315221154414031316 0ustar00zuulzuul--- 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 `_ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1799934-add-ou-parameter-260f9aaf939d1919.yaml0000664000175000017500000000012315221154414030024 0ustar00zuulzuul--- features: - organizational unit (--ou) parameter support added in manila cli ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1802059-fix-is_default-a8d3d95ffa0aede9.yaml0000664000175000017500000000014615221154414030137 0ustar00zuulzuul--- fixes: - Fix share_group_type_create returning is_default as a function object instead of value ././@PaxHeader0000000000000000000000000000021000000000000010206 xustar00114 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1811516-bug-1811627-python3-fixes-4b26130027b2c076.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1811516-bug-1811627-python3-fixes-4b26130027b2c076.0000664000175000017500000000015615221154414027722 0ustar00zuulzuul--- fixes: - | The shell utility has been fixed to report errors correctly on python3 environments. ././@PaxHeader0000000000000000000000000000020700000000000010214 xustar00113 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1814094-fix-_get_base_url-method-168e12292aeec8f1.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1814094-fix-_get_base_url-method-168e12292aeec8f1.y0000664000175000017500000000035315221154414031072 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1830677-fix-13b30d6a89f43246.yaml0000664000175000017500000000032315221154414025355 0ustar00zuulzuul--- fixes: - | `Launchpad bug 1830677 `_ has been fixed and the client now correctly get description of share type instead of ``None``. ././@PaxHeader0000000000000000000000000000021100000000000010207 xustar00115 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1855391-support-force-extend-share-6b5ebcfe1de0ca7b.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1855391-support-force-extend-share-6b5ebcfe1de0ca7b0000664000175000017500000000027515221154414031544 0ustar00zuulzuul--- fixes: - | `Launchpad bug 1855391 `_ has been fixed by adding a "force" argument to the share extension commands. ././@PaxHeader0000000000000000000000000000026700000000000010222 xustar00161 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1871252-update-default-quotas-via-quota-class-share-groups-share-group-snapshots-20ec1dfcc0a7e81c.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1871252-update-default-quotas-via-quota-class-share0000664000175000017500000000061215221154414032073 0ustar00zuulzuul--- 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 `_. ././@PaxHeader0000000000000000000000000000021600000000000010214 xustar00120 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1898304-add-wait-to-share-create-delete-f121073f2f4402ff.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1898304-add-wait-to-share-create-delete-f121073f2f40000664000175000017500000000031215221154414031050 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000023200000000000010212 xustar00132 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1898307-add-wait-to-create-revert-delete-a-snapshot-20271b8ebb60ade5.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1898307-add-wait-to-create-revert-delete-a-snapshot0000664000175000017500000000033715221154414031757 0ustar00zuulzuul--- features: - | The commands "snapshot-create", "snapshot-delete" and "revert-to-snapshot" now accept an optional "--wait" option that allows users to let the client poll for the completion of the operation.././@PaxHeader0000000000000000000000000000021600000000000010214 xustar00120 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1898308-add-wait-to-share-extend-shrink-c9cc413c50d9832a.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1898308-add-wait-to-share-extend-shrink-c9cc413c50d0000664000175000017500000000031415221154414031276 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000022000000000000010207 xustar00122 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1898309-add-wait-to-share-manage-unmanage-d2060c61cc295bfd.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1898309-add-wait-to-share-manage-unmanage-d2060c61c0000664000175000017500000000031215221154414031216 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000023600000000000010216 xustar00136 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1898310-add-wait-to-create-promote-delete-share-replica-35f17e4a51b8f62e.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1898310-add-wait-to-create-promote-delete-share-rep0000664000175000017500000000035515221154414031740 0ustar00zuulzuul--- features: - | The commands "share-replica-create", "share-replica-promote", and "share-replica-delete" now accept an optional "--wait" option that allows users to let the client poll for the completion of the operation.././@PaxHeader0000000000000000000000000000023500000000000010215 xustar00135 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1898315-add-wait-flag-to-manage-share-server-operation-be6488c2a57536e1.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1898315-add-wait-flag-to-manage-share-server-operat0000664000175000017500000000027015221154414031722 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000023200000000000010212 xustar00132 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1898316-add-wait-option-for-deleting-a-share-server-e2228018585de5cb.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1898316-add-wait-option-for-deleting-a-share-server0000664000175000017500000000027215221154414031761 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000025700000000000010221 xustar00153 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1898317-add-wait-option-for-force-deleting-share-snapshot-share-instance-fb2531b6033f0ae5.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1898317-add-wait-option-for-force-deleting-share-sn0000664000175000017500000000041515221154414031751 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000024100000000000010212 xustar00139 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1898318-add-wait-flag-for-deleting-a-share-group-operation-c602ba9faad411be.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1898318-add-wait-flag-for-deleting-a-share-group-op0000664000175000017500000000026715221154414031632 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000023500000000000010215 xustar00135 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1898318-add-wait-flag-to-create-sharee-group-operation-cd8310b241d377b0.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1898318-add-wait-flag-to-create-sharee-group-operat0000664000175000017500000000026715221154414031741 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000023700000000000010217 xustar00137 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1898319-add-wait-to-create-delete-a-share-group-snapshot-f789e324f66ad1b6.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1898319-add-wait-to-create-delete-a-share-group-sna0000664000175000017500000000034515221154414031630 0ustar00zuulzuul--- features: - | The commands "share-group-snapshot-create" and "share-group-snapshot-delete" now accept an optional "--wait" option that allows users to let the client poll for the completion of the operation.././@PaxHeader0000000000000000000000000000024600000000000010217 xustar00144 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1899325-implement-usage-of-c-or-column-without-additional-logic-2970ee294f32bd31.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1899325-implement-usage-of-c-or-column-without-addi0000664000175000017500000000027415221154414032014 0ustar00zuulzuul--- fixes: - | The commands "openstack share type list" and "openstack share access list" no longer support the "--columns" option. Use "--column" to filter the output. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1902873-fix-py-raw-error-msg-a839fee2ac7b9d3d.yaml0000664000175000017500000000036615221154414031104 0ustar00zuulzuul--- fixes: - | `Bug #1902873 `_: Fixed raw Python error message when using ``manila`` without a subcommand while passing an optional argument, such as ``--os-cache``. ././@PaxHeader0000000000000000000000000000024300000000000010214 xustar00141 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1909477-fix-forbid-users-to-create-shares-with-the-name-none-cfb0a59baa597803.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1909477-fix-forbid-users-to-create-shares-with-the-0000664000175000017500000000033515221154414031723 0ustar00zuulzuul--- fixes: - | `Launchpad bug 1909477 `_ has been fixed by prevent sending the share creation request with any capitalization of the name "None". ././@PaxHeader0000000000000000000000000000025500000000000010217 xustar00151 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1920888-fix-error-in-cli-manila-list-with-sorting-key-availabilityzone-cea5a1f5d8a38fc3.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1920888-fix-error-in-cli-manila-list-with-sorting-k0000664000175000017500000000025215221154414031742 0ustar00zuulzuul--- fixes: - | `Launchpad bug 1920888 `_: Fix the bug in CLI 'manila list' with sorting key 'availability_zone'.././@PaxHeader0000000000000000000000000000023500000000000010215 xustar00135 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1925486-add-share-network-option-to-replica-create-api-7d2ff3628e93fc77.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1925486-add-share-network-option-to-replica-create-0000664000175000017500000000051715221154414031763 0ustar00zuulzuul--- 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'. ././@PaxHeader0000000000000000000000000000023300000000000010213 xustar00133 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1953670-fix-id-attr-for-share-group-type-access-repr-008338a53d7a6a50.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1953670-fix-id-attr-for-share-group-type-access-rep0000664000175000017500000000033415221154414031724 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000021400000000000010212 xustar00118 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1959329-fix-server-listing-by-subnets-fa6447fd43093cae.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1959329-fix-server-listing-by-subnets-fa6447fd430930000664000175000017500000000025215221154414031242 0ustar00zuulzuul--- fixes: - Launchpad `bug 1959329 `_ has been fixed for share server listing with network subnet id. ././@PaxHeader0000000000000000000000000000021000000000000010206 xustar00114 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1960422-fix-no-default-share-type-d6191ea0aa1e57fa.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1960422-fix-no-default-share-type-d6191ea0aa1e57fa.0000664000175000017500000000033215221154414031065 0ustar00zuulzuul--- fixes: - | `Launchpad bug 1960422 `_ has been fixed by prevent sending the share creation request and provide early feedback to CLI users. ././@PaxHeader0000000000000000000000000000021300000000000010211 xustar00117 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1960490-use-suitable-version-for-osc-b375a32273b56522.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1960490-use-suitable-version-for-osc-b375a32273b5650000664000175000017500000000050315221154414031025 0ustar00zuulzuul--- 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 `_ ././@PaxHeader0000000000000000000000000000022100000000000010210 xustar00123 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1962288-fixed-share-network-create-command-879dc3deca131ef9.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1962288-fixed-share-network-create-command-879dc3de0000664000175000017500000000017215221154414031554 0ustar00zuulzuulfixes: - | Fixed the use of the "--availability-zone" option with the "openstack share network create" command. ././@PaxHeader0000000000000000000000000000021600000000000010214 xustar00120 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1975488-skip-force-kwarg-if-unspecified-f98c717df1d6e364.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1975488-skip-force-kwarg-if-unspecified-f98c717df1d0000664000175000017500000000031115221154414031372 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000022400000000000010213 xustar00126 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1980985-dont-use-share-type-with-snapshot-ref-de0331c640afbbd3.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1980985-dont-use-share-type-with-snapshot-ref-de0330000664000175000017500000000034415221154414031577 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000021600000000000010214 xustar00120 path=python_manilaclient-6.2.0/releasenotes/notes/bug-1989818-fix-share-network-output-format-93d997f0f4a33fab.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1989818-fix-share-network-output-format-93d997f0f4a0000664000175000017500000000031515221154414031460 0ustar00zuulzuul--- fixes: - | The format of the osc share network subnet create command output has been updated to return a list of hosts instead of a dictionary, as we do for other commands in the client. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-1999775-add-os-key-0cfc95c7b480df05.yaml0000664000175000017500000000022515221154414026767 0ustar00zuulzuul--- fixes: - | Support --os-key option and OS_KEY environment variable which allows to provide client cert and its private key separately. ././@PaxHeader0000000000000000000000000000020700000000000010214 xustar00113 path=python_manilaclient-6.2.0/releasenotes/notes/bug-2030686-fix-default-share-lookup-cc7e592a0dc855e1.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-2030686-fix-default-share-lookup-cc7e592a0dc855e1.y0000664000175000017500000000051115221154414031130 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000022400000000000010213 xustar00126 path=python_manilaclient-6.2.0/releasenotes/notes/bug-2047249-fix-osc-quota-set-per-share-gigabytes-fcff7f8ce2cc3c75.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-2047249-fix-osc-quota-set-per-share-gigabytes-fcff70000664000175000017500000000035215221154414031672 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000021000000000000010206 xustar00114 path=python_manilaclient-6.2.0/releasenotes/notes/bug-2051394-fix-validate-neutron-info-ebe679675111eec9.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-2051394-fix-validate-neutron-info-ebe679675111eec9.0000664000175000017500000000072115221154414031053 0ustar00zuulzuulfixes: - | The "openstack share network create" and "openstack share network subnet create" commands now validate Neutron network information provided before sending the request. Previously, validation only occurred in the Manila service which could result in client-side errors when invalid network or subnet IDs were supplied. The client now performs proper validation and handles missing or invalid network data gracefully.././@PaxHeader0000000000000000000000000000021500000000000010213 xustar00119 path=python_manilaclient-6.2.0/releasenotes/notes/bug-2051737-fix-share-force-delete-request-6d2578fb7da61e3f.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-2051737-fix-share-force-delete-request-6d2578fb7da60000664000175000017500000000042315221154414031304 0ustar00zuulzuul--- 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 `_ ././@PaxHeader0000000000000000000000000000020500000000000010212 xustar00111 path=python_manilaclient-6.2.0/releasenotes/notes/bug-2057951-fix-client-region-name-67fed93b76429b63.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-2057951-fix-client-region-name-67fed93b76429b63.yam0000664000175000017500000000054215221154414030757 0ustar00zuulzuulfixes: - | Fixed Bug #2057951: The manila python client previously ignored the "region_name" parameter when initializing a client session. This caused the client to use wrong API endpoint in multi-region deployments. The client now correctly respects the "region_name" value ensuring requests are routed to the intended region ././@PaxHeader0000000000000000000000000000020700000000000010214 xustar00113 path=python_manilaclient-6.2.0/releasenotes/notes/bug-2065769-fix-share-network-create-fa76f38f1b640001.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-2065769-fix-share-network-create-fa76f38f1b640001.y0000664000175000017500000000044115221154414030772 0ustar00zuulzuul--- fixes: - | Share network create command failed with 2.32 version of api, due to set availability zone argument for old version of create function, which doesn't have this argument. The bug was fixed by adding a check that the user set the availability zone in query. ././@PaxHeader0000000000000000000000000000023300000000000010213 xustar00133 path=python_manilaclient-6.2.0/releasenotes/notes/bug-2066871-allow-to-update-access-level-for-access-rule-adb5d572454c01e8.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-2066871-allow-to-update-access-level-for-access-rul0000664000175000017500000000023615221154414031761 0ustar00zuulzuul--- features: - | The ``access_level`` attributes of share access rule (of access type ``ip``) can be updated with API version ``2.88`` and beyond. ././@PaxHeader0000000000000000000000000000022300000000000010212 xustar00125 path=python_manilaclient-6.2.0/releasenotes/notes/bug-2068631-add-filter-by-source-share-server-id-d40500dcf184c321.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-2068631-add-filter-by-source-share-server-id-d405000000664000175000017500000000040215221154414031274 0ustar00zuulzuul--- fixes: - | Share server list command now support filtering share servers by source share server IDs and identifiers. For more details, please refer to `Launchpad bug #1896949 `_. ././@PaxHeader0000000000000000000000000000024000000000000010211 xustar00138 path=python_manilaclient-6.2.0/releasenotes/notes/bug-2068732-fix-migration-get-progress-on-source-share-server-7ef327dcdedcfbd6.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-2068732-fix-migration-get-progress-on-source-share-0000664000175000017500000000021415221154414032027 0ustar00zuulzuul--- fixes: - | Fixed an issue while getting share server migration progress for source share server after migration is completed. ././@PaxHeader0000000000000000000000000000023400000000000010214 xustar00134 path=python_manilaclient-6.2.0/releasenotes/notes/bug-2108991-fix-task-state-none-handling-in-osc-share-set-307f3f4a2b232dd8.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-2108991-fix-task-state-none-handling-in-osc-share-s0000664000175000017500000000052715221154414031670 0ustar00zuulzuul--- fixes: - | The `openstack share set` and `openstack share server set` commands now properly handle the `--task-state` argument when given no value or "None", sending a proper null value to the API. For more details, please refer to `Launchpad bug #2108991 `_. ././@PaxHeader0000000000000000000000000000021000000000000010206 xustar00114 path=python_manilaclient-6.2.0/releasenotes/notes/bug-2109376-fix-ensure-shares-command-69feb1794fea418a.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-2109376-fix-ensure-shares-command-69feb1794fea418a.0000664000175000017500000000021415221154414031120 0ustar00zuulzuul--- fixes: - | Fixed an issue while running ensure shares and getting a not found response. The command should now work properly. ././@PaxHeader0000000000000000000000000000020500000000000010212 xustar00111 path=python_manilaclient-6.2.0/releasenotes/notes/bug-2132937-update-share-group-fix-41558fef427c84a2.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-2132937-update-share-group-fix-41558fef427c84a2.yam0000664000175000017500000000055515221154414031006 0ustar00zuulzuul--- fixes: - | Fixed an issue where the ``share group update`` command (and the underlying ``share_groups.update()`` python binding) was missing the experimental API flag. The client now correctly treats this action as experimental and sends the required headers. (Bug `2132937 `_) ././@PaxHeader0000000000000000000000000000022200000000000010211 xustar00124 path=python_manilaclient-6.2.0/releasenotes/notes/bug-913201-add-wait-to-manage-unmanage-snapshot-9cc017a15733120f.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-913201-add-wait-to-manage-unmanage-snapshot-9cc017a0000664000175000017500000000031315221154414031505 0ustar00zuulzuul--- features: - | The commands "snapshot-manage" and "snapshot-unmanage" now accept an optional "--wait" option that allows users to let the client poll for the completion of the operation.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug-share-access-list-3cf3114doe40k599.yaml0000664000175000017500000000024415221154414027433 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug_1570085_fix-905786b797379309.yaml0000664000175000017500000000025715221154414025336 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug_1603387_fix_env_variable_8ed5450aab41aa5f.yaml0000664000175000017500000000020415221154414030601 0ustar00zuulzuul--- fixes: - Use consistent environment variable naming. All old variables are still supported due to compatibility reasons. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug_1606168_fix-54d3c3bb78389f01.yaml0000664000175000017500000000007715221154414025611 0ustar00zuulzuul--- fixes: - Fix error handling for os-token and bypass-url. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug_1715769_fix-3ec701b0fb9d7910.yaml0000664000175000017500000000020615221154414025662 0ustar00zuulzuul--- fixes: - Usernames can contain the $ symbol to allow for windows computer authentication in an Active Directory environment ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug_1777849_1779935_fix-344cb8f09b7df502.yaml0000664000175000017500000000015415221154414026631 0ustar00zuulzuul--- fixes: - Fixed bugs 1777849 and 1779935. Name can be used for the sort option in the list command.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/bug_1782672-1954059b373f03de.yaml0000664000175000017500000000012315221154414024656 0ustar00zuulzuul--- fixes: - | Fixed bug 1782672. Name can now be used in type-show command. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/deprecate-keystoneauth-params-ebbaaf6be4df4bb7.yaml0000664000175000017500000000071015221154414031712 0ustar00zuulzuul--- deprecations: - | Legacy keystoneclient options "use_keyring", "force_new_token" and "cached_token_lifetime" were dropped. Using these options will have no effect with this release and will cause an error in a future release. security: - | The use of the keystoneclient library has been replaced with keystoneauth. The latter library is better maintained and has more auth options that are now usable with manilaclient. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/deprecate-manila-shell-0061cbcab5d3d75b.yaml0000664000175000017500000000124115221154414027741 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/deprecate-v1-a0cfa6fd723c2f46.yaml0000664000175000017500000000111515221154414025735 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/drop-py36-and-py37-85cf389b2842f045.yaml0000664000175000017500000000020615221154414026277 0ustar00zuulzuul--- deprecations: - | Support for Python versions 3.6 and 3.7 was dropped. The minimum supported version is now Python 3.8. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/drop-python2-support-71c7b9e1dcf8c890.yaml0000664000175000017500000000044515221154414027501 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000020700000000000010214 xustar00113 path=python_manilaclient-6.2.0/releasenotes/notes/expose-access-key-in-access-list-API-b57c386c9048ae55.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/expose-access-key-in-access-list-API-b57c386c9048ae55.y0000664000175000017500000000016615221154414031341 0ustar00zuulzuul--- features: - Returns ``access_key`` as part of ``access_list`` API response for API microversions >= '2.21'. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/fix-and-improve-access-rules-a118a7f8e22f65bb.yaml0000664000175000017500000000036615221154414031003 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/fix-is-default-empty-557844001e0401e2.yaml0000664000175000017500000000037115221154414026764 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/graduate-share-migration-feature-3b8fa72d4e91a517.yaml0000664000175000017500000000113715221154414031643 0ustar00zuulzuul--- prelude: > - | Share migration APIs have graduated from their `experimental feature state `_ from API version ``2.96``. The share migration actions (``migration_start``, ``migration_complete``, ``migration_cancel``, ``migration_get_progress`` and ``reset_task_state``) no longer require the inclusion of the ``X-OpenStack-Manila-API-Experimental`` header in API requests starting at version ``2.96``. Older microversions continue to send the experimental header for backward compatibility. ././@PaxHeader0000000000000000000000000000020500000000000010212 xustar00111 path=python_manilaclient-6.2.0/releasenotes/notes/graduate-share-replication-feature-49770e921b4338fb.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/graduate-share-replication-feature-49770e921b4338fb.yam0000664000175000017500000000076315221154414031661 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/handle-missing-api-minor-version-5a6d242f28883442.yaml0000664000175000017500000000012115221154414031360 0ustar00zuulzuul--- fixes: - Added proper error handling for missing API minor version number. ././@PaxHeader0000000000000000000000000000021700000000000010215 xustar00121 path=python_manilaclient-6.2.0/releasenotes/notes/human-readable-export-location-share-support-d156f23669e458fa.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/human-readable-export-location-share-support-d156f236690000664000175000017500000000227115221154414032210 0ustar00zuulzuul--- features: - In the 2.84 API version, a new option, ``mount_point_name``, has been introduced to the share creation command. This option allow users to specify a more intuitive ``mount_point_name`` that will be reflected in the share's export location. However, for this feature to be available to users, administrators must first enable an extra-spec in the share type. In addition, administrators need to set an extra-spec named ``provisioning:mount_point_prefix``. The Manila service will combine this prefix with the mount point name that user provides during share creation. If the ``provisioning:mount_point_prefix`` is not set for a share type, but ``mount_point_name_support`` is enabled, the share's export location will default to using the project_id as a prefix. Please note that shares created with a project_id prefix cannot be transferred. To move these shares to a different project, an admin must manually unmount them from the current project and mount them to the target project. A new capability, ``mount_point_name_support``, allows the driver to inform the scheduler about its support for the mount_point_name feature.././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/manage-unmanage-share-servers-8c7b27a1fe80e5fa.yaml0000664000175000017500000000024515221154414031277 0ustar00zuulzuul--- features: - Added CLI commands to manage and unmanage share servers. - Updated CLI command for managing shares to accept ``share_server_id`` parameter. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/manila-client-support-recycle-bin-4ecb5de770bd525f.yaml0000664000175000017500000000023315221154414032104 0ustar00zuulzuul--- features: - Added CLI commands to soft delete share. - Added CLI commands to restore share. - Added CLI commands to query shares in recycle bin. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/manila-openstackclient-bf61ceb270d3afb7.yaml0000664000175000017500000000410715221154414030163 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/migration-share-type-4fc3b7c6187f5201.yaml0000664000175000017500000000012215221154414027306 0ustar00zuulzuul--- features: - Added parameter to change share type when performing migration. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/mountable_snapshot-ced01da7dffc6d7e.yaml0000664000175000017500000000012615221154414027611 0ustar00zuulzuul--- features: - Added support for the mountable snapshots feature to manila client. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/newton-migration-improvements-166a03472948bdef.yaml0000664000175000017500000000146715221154414031306 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000022500000000000010214 xustar00127 path=python_manilaclient-6.2.0/releasenotes/notes/remove-experimental-flag-from-share-groups-feature-dcf2b0b67fe4cac4.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/remove-experimental-flag-from-share-groups-feature-dcf20000664000175000017500000000110615221154414032665 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/remove-identity-v2-f62956e7d54b8c53.yaml0000664000175000017500000000010615221154414026731 0ustar00zuulzuul--- features: - | Support for identity v2 API has been removed. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/remove-nova-net-id-arg-5b01f0077be171ed.yaml0000664000175000017500000000013615221154414027473 0ustar00zuulzuul--- upgrade: - | The ``--nova-net-id`` option has been removed from the ``manila`` CLI. ././@PaxHeader0000000000000000000000000000021300000000000010211 xustar00117 path=python_manilaclient-6.2.0/releasenotes/notes/remove-nova-net-id-option-for-share-nets-82e424b75221528b.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/remove-nova-net-id-option-for-share-nets-82e424b75221520000664000175000017500000000042015221154414031456 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/remove-py38-0326744b43229e21.yaml0000664000175000017500000000016615221154414025114 0ustar00zuulzuul--- upgrade: - | Support for Python 3.8 has been removed. Now the minimum python version supported is 3.9 . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/remove-py39-5278e896dc045854.yaml0000664000175000017500000000016615221154414025230 0ustar00zuulzuul--- upgrade: - | Support for Python 3.9 has been removed. Now Python 3.10 is the minimum version supported. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/remove-python-client-a142b99ccc89731c.yaml0000664000175000017500000000032015221154414027412 0ustar00zuulzuul--- upgrade: The "manila" CLI utility has been removed in this release. The mapping/decoder documentation still exists to provide transition to users that are unfamiliar with the "openstack" CLI. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/share-backup-98e11c6a28897e94.yaml0000664000175000017500000000050015221154414025551 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/share-backup-out-of-place-restore-98740badef59ba32.yaml0000664000175000017500000000050615221154414031727 0ustar00zuulzuul--- features: - | Added support for targeted share backup restores via the openstackclient plugin. You can use the openstack client to restore a share backup from one source share to another target share, given the backup or share driver provides support for the operation. Available from microversion 2.91. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/share-network-multiple-subnets-732309abfbf5987c.yaml0000664000175000017500000000104615221154414031440 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/share-revert-to-snapshot-e899a4b7e1126749.yaml0000664000175000017500000000010415221154414030062 0ustar00zuulzuul--- features: - Added support for the revert-to-snapshot feature. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/share_type-skip-format-119595e62900e571.yaml0000664000175000017500000000046015221154414027426 0ustar00zuulzuul--- 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`` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/start-using-reno-b744cd0259c7a88c.yaml0000664000175000017500000000012615221154414026551 0ustar00zuulzuul--- features: - Started using release notes to track changes to python-manilaclient.././@PaxHeader0000000000000000000000000000022000000000000010207 xustar00122 path=python_manilaclient-6.2.0/releasenotes/notes/support-create-share-from-snapshot-extra-spec-cdba92f179c1c5c6.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/support-create-share-from-snapshot-extra-spec-cdba92f170000664000175000017500000000023115221154414032445 0ustar00zuulzuul--- features: - Support the new optional create_share_from_snapshot_support extra spec, and handle the newly-optional snapshot_support extra spec. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/support-share-server-migration-9804752270c6b153.yaml0000664000175000017500000000050215221154414031127 0ustar00zuulzuul--- 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`. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/support-show-type-6380b7c539c95ba8.yaml0000664000175000017500000000006515221154414026727 0ustar00zuulzuul--- features: - Support show type details command. ././@PaxHeader0000000000000000000000000000021200000000000010210 xustar00116 path=python_manilaclient-6.2.0/releasenotes/notes/support_add_disabled_reason_to_services-08cb52e4711745c7.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/support_add_disabled_reason_to_services-08cb52e4711745c0000664000175000017500000000024115221154414032257 0ustar00zuulzuul--- features: - Support add disabled reason to services command. See `bug 2037700 `_ for more details. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/type-create-extra-specs-691572762357ef3b.yaml0000664000175000017500000000011115221154414027566 0ustar00zuulzuul--- fixes: - User can specify any number of extra-specs in type_create. ././@PaxHeader0000000000000000000000000000025000000000000010212 xustar00146 path=python_manilaclient-6.2.0/releasenotes/notes/update-api-version-create-share-from-snapshot-another-pool-or-backend-694cfda84a41c4ff.yaml 22 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/update-api-version-create-share-from-snapshot-another-p0000664000175000017500000000040615221154414032713 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/notes/v2-0-0-deprecated-opts-removal-863565618535733d.yaml0000664000175000017500000000061215221154414030325 0ustar00zuulzuul--- 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. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1782896962.126094 python_manilaclient-6.2.0/releasenotes/source/0000775000175000017500000000000015221154502020370 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/2023.1.rst0000664000175000017500000000021015221154414021642 0ustar00zuulzuul=========================== 2023.1 Series Release Notes =========================== .. release-notes:: :branch: unmaintained/2023.1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/2023.2.rst0000664000175000017500000000020215221154414021644 0ustar00zuulzuul=========================== 2023.2 Series Release Notes =========================== .. release-notes:: :branch: stable/2023.2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/2024.1.rst0000664000175000017500000000021015221154414021643 0ustar00zuulzuul=========================== 2024.1 Series Release Notes =========================== .. release-notes:: :branch: unmaintained/2024.1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/2024.2.rst0000664000175000017500000000020215221154414021645 0ustar00zuulzuul=========================== 2024.2 Series Release Notes =========================== .. release-notes:: :branch: stable/2024.2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/2025.1.rst0000664000175000017500000000020215221154414021645 0ustar00zuulzuul=========================== 2025.1 Series Release Notes =========================== .. release-notes:: :branch: stable/2025.1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/2025.2.rst0000664000175000017500000000020215221154414021646 0ustar00zuulzuul=========================== 2025.2 Series Release Notes =========================== .. release-notes:: :branch: stable/2025.2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/2026.1.rst0000664000175000017500000000020215221154414021646 0ustar00zuulzuul=========================== 2026.1 Series Release Notes =========================== .. release-notes:: :branch: stable/2026.1 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1782896962.126094 python_manilaclient-6.2.0/releasenotes/source/_static/0000775000175000017500000000000015221154502022016 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/_static/.placeholder0000664000175000017500000000000015221154414024271 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.1270943 python_manilaclient-6.2.0/releasenotes/source/_templates/0000775000175000017500000000000015221154502022525 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/_templates/.placeholder0000664000175000017500000000000015221154414025000 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/conf.py0000664000175000017500000002150215221154414021671 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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/'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/index.rst0000664000175000017500000000052215221154414022232 0ustar00zuulzuul ================================= python-manilaclient Release Notes ================================= .. toctree:: :maxdepth: 1 unreleased 2026.1 2025.2 2025.1 2024.2 2024.1 2023.2 2023.1 zed yoga xena wallaby victoria ussuri train stein rocky queens pike ocata newton ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/newton.rst0000664000175000017500000000023215221154414022433 0ustar00zuulzuul=================================== Newton Series Release Notes =================================== .. release-notes:: :branch: origin/stable/newton ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/ocata.rst0000664000175000017500000000023015221154414022206 0ustar00zuulzuul=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/pike.rst0000664000175000017500000000021715221154414022054 0ustar00zuulzuul=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/queens.rst0000664000175000017500000000022315221154414022421 0ustar00zuulzuul=================================== Queens Series Release Notes =================================== .. release-notes:: :branch: stable/queens ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/rocky.rst0000664000175000017500000000022115221154414022246 0ustar00zuulzuul=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/stein.rst0000664000175000017500000000022115221154414022241 0ustar00zuulzuul=================================== Stein Series Release Notes =================================== .. release-notes:: :branch: stable/stein ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/train.rst0000664000175000017500000000017615221154414022245 0ustar00zuulzuul========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/unreleased.rst0000664000175000017500000000015315221154414023252 0ustar00zuulzuul============================ Current Series Release Notes ============================ .. release-notes:: ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/ussuri.rst0000664000175000017500000000020215221154414022450 0ustar00zuulzuul=========================== Ussuri Series Release Notes =========================== .. release-notes:: :branch: stable/ussuri ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/victoria.rst0000664000175000017500000000021215221154414022737 0ustar00zuulzuul============================= Victoria Series Release Notes ============================= .. release-notes:: :branch: stable/victoria ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/wallaby.rst0000664000175000017500000000021415221154414022554 0ustar00zuulzuul============================ Wallaby Series Release Notes ============================ .. release-notes:: :branch: unmaintained/wallaby ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/xena.rst0000664000175000017500000000020015221154414022047 0ustar00zuulzuul========================= Xena Series Release Notes ========================= .. release-notes:: :branch: unmaintained/xena ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/yoga.rst0000664000175000017500000000020015221154414022053 0ustar00zuulzuul========================= Yoga Series Release Notes ========================= .. release-notes:: :branch: unmaintained/yoga ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/releasenotes/source/zed.rst0000664000175000017500000000017415221154414021710 0ustar00zuulzuul======================== Zed Series Release Notes ======================== .. release-notes:: :branch: unmaintained/zed ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/requirements.txt0000664000175000017500000000103115221154414017660 0ustar00zuulzuul# 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. pbr>=3.0.0 # Apache-2.0 oslo.config>=5.2.0 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 oslo.serialization>=2.20.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 PrettyTable>=0.7.1 # BSD requests>=2.14.2 # Apache-2.0 osc-lib>=3.2.0 # Apache-2.0 keystoneauth1>=3.0.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.0170872 python_manilaclient-6.2.0/roles/0000775000175000017500000000000015221154502015523 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.1270943 python_manilaclient-6.2.0/roles/populate-manilaclient-config/0000775000175000017500000000000015221154502023255 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/roles/populate-manilaclient-config/README.rst0000664000175000017500000000235115221154414024747 0ustar00zuulzuulpopulate-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. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.1270943 python_manilaclient-6.2.0/roles/populate-manilaclient-config/defaults/0000775000175000017500000000000015221154502025064 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/roles/populate-manilaclient-config/defaults/main.yaml0000664000175000017500000000030715221154414026676 0ustar00zuulzuul--- 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 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.1270943 python_manilaclient-6.2.0/roles/populate-manilaclient-config/tasks/0000775000175000017500000000000015221154502024402 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/roles/populate-manilaclient-config/tasks/main.yaml0000664000175000017500000000475615221154414026230 0ustar00zuulzuul- 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_PROJECT_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" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.1290944 python_manilaclient-6.2.0/setup.cfg0000664000175000017500000000011515221154502016215 0ustar00zuulzuul[metadata] name = python-manilaclient [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/setup.py0000664000175000017500000000126015221154414016112 0ustar00zuulzuul# 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/test-requirements.txt0000664000175000017500000000035715221154414020647 0ustar00zuulzuulcoverage!=4.4,>=4.0 # Apache-2.0 ddt>=1.0.1 # MIT fixtures>=3.0.0 # Apache-2.0/BSD requests-mock>=1.2.0 # Apache-2.0 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 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/tox.ini0000664000175000017500000000533515221154414015722 0ustar00zuulzuul[tox] envlist = py3,pep8 minversion = 4.11.0 [testenv] 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:newnote] deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/doc/requirements.txt commands = reno new {posargs} [testenv:pep8] skip_install = true deps = pre-commit commands = pre-commit run --all-files --show-diff-on-failure [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 [testenv:bandit] deps = bandit commands = bandit -r manilaclient -x manilaclient/tests/* -n5 -ll [flake8] show-source = true exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,releasenotes # We only enable the hacking (H) checks select = H # H301 Black will put commas after imports that can't fit on one line ignore = H301 # 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 application_import_names = manilaclient ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1782896962.1280942 python_manilaclient-6.2.0/zuul.d/0000775000175000017500000000000015221154502015620 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/zuul.d/project.yaml0000664000175000017500000000107115221154414020153 0ustar00zuulzuul- project: templates: - publish-openstack-docs-pti - openstack-cover-jobs - openstack-python3-jobs - check-requirements - openstackclient-plugin-jobs - release-notes-jobs-python3 check: jobs: - python-manilaclient-functional - python-manilaclient-tox-bandit - manila-rally-ss: voting: false - python-manilaclient-functional-fips: voting: false - manila-rally-no-ss: voting: false gate: jobs: - python-manilaclient-functional ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1782896908.0 python_manilaclient-6.2.0/zuul.d/python-manilaclient-jobs.yaml0000664000175000017500000001156415221154414023427 0ustar00zuulzuul- 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: 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-agent: true # openstack-cli-server causes problem with credential switch in ansible # task openstack-cli-server: false - 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' - job: name: python-manilaclient-tox-bandit parent: openstack-tox timeout: 2400 vars: tox_envlist: bandit