././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8590243 python_watcherclient-4.8.0/0000775000175000017500000000000000000000000016003 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/.coveragerc0000664000175000017500000000025300000000000020124 0ustar00zuulzuul00000000000000[run] branch = True source = watcherclient omit = watcherclient/tests/* [report] ignore_errors = True exclude_lines = @abc.abstract raise NotImplementedError ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/.stestr.conf0000664000175000017500000000007200000000000020253 0ustar00zuulzuul00000000000000[DEFAULT] test_path=./watcherclient/tests/unit top_dir=./ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/.zuul.yaml0000664000175000017500000000037200000000000017746 0ustar00zuulzuul00000000000000- project: templates: - openstack-cover-jobs - openstack-python3-jobs - publish-openstack-docs-pti - check-requirements - openstackclient-plugin-jobs check: jobs: - watcherclient-tempest-functional ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768124.0 python_watcherclient-4.8.0/AUTHORS0000664000175000017500000000704000000000000017054 0ustar00zuulzuul0000000000000098k <18552437190@163.com> Alexander Chadin Alexander Chadin Alexander Chadin Alexandr Stavitskiy Andreas Jaeger Antoine Cabot Cao Xuan Hoang Chihiro Yokoyama Chris Spencer Cyril Roelandt Daniel Pawlik Dantali0n Dao Cong Tien David TARDIVEL David.T DeepaJon Doug Hellmann Dougal Matthews Edwin Zhai Eric Fried Flavio Percoco Ghanshyam Mann Hangdong Zhang Hidekazu Nakamura James E. Blair Jean-Emile DARTOIS Jeremy Liu Jeremy Stanley Larry Rensing Lucky samadhiya Luong Anh Tuan M V P Nitesh Martin Kopec Matt Riedemann Mitya Eremeev Nam Nguyen Hoai Nguyen Hai OpenStack Release Bot PanFengyun Prashanth Hari Sean McGinnis Steve Martinelli Swapnil Kulkarni (coolsvap) Takashi Kajinami Taylor Peoples Thierry Carrez Thomas Goirand Tin Lam Tin Lam Tomasz Kaczynski Tony Xu Vieri <15050873171@163.com> Vincent Francoise Vladimir Ostroverkhov XinxinShen Yumeng Bao ZhijunWei ZhongShengping aditi aditi avnish blue55 caihui chenke chenker daohanli deepak_mourya digambar ericxiett gecong1973 gengchc2 hongzhezheng huang.zhiping jacky06 kangyufei kavithahr licanwei maaoyu melissaml qinchunhua qingszhao qiufossen rajat29 ricolin ricolin shu-mutou suzhengwei venkatamahesh wanghui wu.shiming zhangbailin zhangboye zhangguoqing zhangyangyang zhurong zhuzeyu zte-hanrong ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/CONTRIBUTING.rst0000664000175000017500000000105100000000000020441 0ustar00zuulzuul00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps in this page: https://docs.openstack.org/infra/manual/developers.html Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: https://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad, not GitHub: https://bugs.launchpad.net/python-watcherclient ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768124.0 python_watcherclient-4.8.0/ChangeLog0000664000175000017500000002731200000000000017562 0ustar00zuulzuul00000000000000CHANGES ======= 4.8.0 ----- 4.7.0 ----- * If endpoint ends with 1 client removes it * Update python versions, drop py3.8 * Drop unused tempest from test requirements * Get rid of distutils 4.6.0 ----- * Bump hacking * Python 3.12: do not use ssl.wrap\_socket 4.5.0 ----- 4.3.0 ----- * Remove six * Update python classifier in setup.cfg * Remove untested lower-constraints.txt 4.2.0 ----- * Fix tox error * Switch to 2023.1 Python3 unit tests and generic template name 4.0.0 ----- * Update to zed cycle testing runtime * Make watcherclient/shell.py reproducible 3.4.0 ----- * Add Python3 yoga unit tests 3.3.0 ----- * Changed minversion in tox to 3.18.0 * Switch testing to Xena testing runtime * Replace deprecated UPPER\_CONSTRAINTS\_FILE variable * setup.cfg: Replace dashes with underscores * Use py3 as the default runtime for tox * Drop lower constraints testing * remove unicode from code 3.2.0 ----- * Remove install unnecessary packages * Add Python3 wallaby unit tests 3.1.1 ----- * [goal] Migrate testing to ubuntu focal * remove mox3 3.1.0 ----- * Switch to newer openstackdocstheme version * Fix hacking min version to 3.0.1 * Remove Babel requirement * Remove future imports * Use unittest.mock instead of third party mock * Bump default tox env from py37 to py38 * Add py38 package metadata * Add Python3 victoria unit tests 3.0.0 ----- * Cleanup py27 support * Update hacking for Python3 * Support audit type event * Watcherclient supports list data model API * Drop python 2.7 support and testing * Fix details doc format error 2.5.0 ----- * Fix python-openstackclient plugin doc build * Switch to Ussuri jobs * Build pdf docs 2.4.0 ----- * Implement watcher datamodel list in watcherclient * Add datamodel doc in watcherclient 2.3.0 ----- * Fix \_make\_connection\_url * Add Python3 Train unit tests * Switch to the new canonical constraints URL on master * Remove unused readme file * remove unused cliutils.py file as osc\_lib is used * Add force option * Cleanup doc dependencies * Remove --test-path option from stestr run * Whitelist rm external usage in tox.ini * Use upper-constraints in tox runs * Add tempest voting * Replace git.openstack.org URLs with opendev.org URLs * OpenDev Migration Patch * Remove py35 * Increase actionplan\_shell.py unittest coverage from 70% to 97% * Update .gitignore to ignore cover and .coverage\* * Increase the unit test coverage of action\_plan.py from 79% to 95% * Remove functional in tox.ini * Add marker option for strategy in watcher-client * Remove the functional test * Add marker option for goal * Add openstack-cover-jobs for watcherclient * add python 3.7 unit test job * DEFAULT\_VER should be '1.latest' * Use openstack common CLI * Update hacking version * Add deletion of actionplan to osc plugin * [Doc] Fix OS\_AUTH\_URL value * update audit start/end time help * Pass API microversion to Client class * Change openstack-dev to openstack-discuss * Revert "Pass actual API version to Client class" * Fix obsolete version of osc * Change openstack-dev to openstack-discuss * Add Python 3.6 classifier to setup.cfg * Add continuous audit functional test * add python 3.6 unit test job * Refactor the getid method base.py * Pass actual API version to Client class * Replace latest with explicit version * Update min tox version to 2.0 * Update watcher api command argument * add start and end time for continuous audit * Prepare watcherclient for microversioning 2.2.0 ----- * Allow CLI to pass goal and strategy names * Use templates lower-constraints, remove cover * Switch to stestr * add python 3.6 unit test job * switch documentation job to new PTI * import zuul job settings from project-config * fix watcher actionplan show command * Update watcher strategy state in CLI * Add --marker help info * Organize the use of 'log' and replace 'log' with LOG * Add audit name to CLI help doc 2.1.0 ----- * Fix watherclient error in py3 * Add --marker help info * Add hostname field to support HA * Invalid doc for Client exceptions * Don't run voting jobs in gate * fix tox python3 overrides 2.0.0 ----- * Modify Watcher start actionplan command * Remove 'actionplan create' command 1.7.0 ----- * [WiP] functional jobs fix * ZuulV3 support for watcherclient * add lower-constraints job * Add tempest plugin * Delete the unnecessary '-' * Updated from global requirements * Updated from global requirements * Update links in README * Updated from global requirements * Fix global efficacy format * Zuul: Remove project name 1.6.0 ----- * Add strategy state command * Updated from global requirements * Updated from global requirements * Updated from global requirements * Audit Template Help Message * Fix test\_action\_plan functional tests * Updated from global requirements * Fix watcher actionplan list command 1.5.0 ----- * Migrate to Zuul v3 * Updated from global requirements * Update audit\_template create help message * marker when retrive audit * marker when retrive action * marker when retrive audit template * Add --marker for 'watcher actionplan list' * Updated from global requirements * Updated from global requirements * Multiple global efficacy * add name for audit, changes for python-watcherclient * Fix unnecessary retries during conflict * Use generic user for both zuul v2 and v3 1.4.0 ----- * Updated from global requirements * Update audit\_template create help message * Fix gate-watcherclient-dsvm-functional-ubuntu-xenial job * Updated from global requirements * Updated from global requirements * Updated from global requirements * Fix to use "." to source script files * import content from cli-reference in openstack-manuals * Updated from global requirements * Updated from global requirements * Add the filed of description to shell command for action 1.3.0 ----- * Update .gitignore because of doc migration * Fix Audit Update functional test * Update the documentation link for doc migration * Updated from global requirements * Update permissions for post\_test\_hook.sh * Update URLs in documents according to document migration * Updated from global requirements * Add support for cron syntax * Updated from global requirements * Fixed wrap from taking negative values * Add post\_test\_hook * Move existing content into the new standard structure * Fix for README.rst of tests * switch to openstackdocstheme * Updated from global requirements * Turn on warning-is-error in sphinx build * Enable some off-by-default checks * Updated from global requirements * Add CLI for Action Plan Cancel 1.2.0 ----- * Updated from global requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * Add 'rm -f .testrepository/times.dbm' command in testenv 1.1.0 ----- * Updated from global requirements * Optimize the link address * Remove log translations * Indicating the location tests directory in oslo\_debug\_helper * Using assert methods instead of assertTrue * Updated from global requirements * [Fix gate]Update test requirement * Remove RST files located in doc/source/api * Remove support for py34 * update help description about actionplan create * Using jsonutils instead of json * Updated from global requirements * Remove useless utf-8 coding * Add functional env to tox 1.0.0 ----- * update '--detail' help in goal list * Updated from global requirements * Add functional tests to watcherclient * Support parents field along with planner changes * changes to make consistent with other openstack component * Use keystoneauth instead of keystoneclient * Remove unused files * Enable coverage report in console output * Add param 'goal' and 'strategy' in list() * Removes unnecessary utf-8 encoding * update audit create '--interval' description * Add unit for continuous audit's interval * Fix a typo in audittemplate help * use 'auto' instead of None * Add auto\_trigger support * Fix TOKENID format which should without dashed * too many digits after the decimal point 0.27.0 ------ * Remove readme reference because of sphinx error * Replace six.iteritems() with .items() * Updated from global requirements * Show team and repo badges on README * Updated from global requirements * Use uuidutils instead of uuid.uuid4() 0.26.0 ------ * Updated from global requirements * Remove obsolete object attributes * Fix a typo error in a help message in the client * Updated from global requirements * Remove unnecessary ')' * Add Python 3.5 classifier and venv * Add \_\_ne\_\_ built-in function * Updated from global requirements * Add support for Audit Scope * Add parameters in Audit creation attributes list * Updated from global requirements * Added support for Client creation from KS session * Updated from global requirements * Add fixtures to test-requirements * Add constraint target to tox.ini and remove 1 dep * Add service support * python-openstackclient ClientManager interface changed * Update home page link in cfg file * Clean imports in code 0.25.0 ------ * Add again parameters as Audit creation attributes * Updated from global requirements * Add strategy name in action plan fields * Updated from global requirements * remove redundant ')' * Add goal\_id, strategy\_id and host\_aggregate CLI options to audit * Optimiz the help information for audit type parameter * Updated from global requirements * Add scoring engine commands * Remove discover from test-requirements * Updated from global requirements * Updated from global requirements * Fix for importing osc-lib instead openstackclient * Updated from global requirements 0.24.0 ------ * Fix for error importing of exception class * Add support continuously-optimization * Remove the blank space between the function name and the parenthesis * Prints '-' instead of 'None' when data is None * Enable strategy parameter * Fix field type to audit\_type * Add license file * Updated from global requirements * Remove tempest-lib * Updated from global requirements * Revert "Add support continuously-optimization" * Replaced UUID of goal with name * Remove useless index on root doc page * Updated CLI to display efficacy related fields * Updated from global requirements * Add support continuously-optimization 0.23.0 ------ * Update Watcher CLI documentation * Use goal name as strategy list filter * Replaced UUID of goal and strategy with name * Flatten the project structure * Switch Watcher CLI to an OSC-compatible version * OpenStackClient plugin for action * OpenStackClient plugin for action plan * OpenStackClient plugin for audit * OpenStackClient plugin for audit template * OpenStackClient plugin for strategy * OpenStackClient plugin for goal * Tidy up * Use the correct capitalization of OpenStack * Support for refactored /audit\_templates endpoint * Added Strategy support in Watcher CLI * Updated CLI for new /goals API * Add PrettyTable module * Fixed audit creation bug in CLI * Removed unused 'alarm' field * Add audit-template name checking in CLI * Updated from global requirements 0.22.0 ------ * Rename TRIGGERED state as PENDING * Updated from global requirements * Updated from global requirements * Replace deprecated LOG.warn with LOG.warning * Removed host\_aggregate filter for Audit Template * Removed useless '--name' in audit-template-list * Sync with openstack/requirements master branch 0.21.0 ------ * Updated STARTING to TRIGGERED * Remove argparse from requirements * Fix extraction of \_LI \_LW \_LE \_LC for translation * Update Watcher documentation in README * i18n - Make string translatable * Respect the import order template * Sync Action resource fields * Sync with oslo-incubator * use keystoneclient exceptions instead of oslo-incubator code * Drop py33 support 0.20.0 ------ * Remove py26 support * Update requirements from OS Global Requirements * bug and requirements fixes * Change stackforge to openstack * Update .gitreview for new namespace * update README.rst file * push initial version * Added .gitreview ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/HACKING.rst0000664000175000017500000000024200000000000017577 0ustar00zuulzuul00000000000000python-watcherclient Style Commandments ======================================= Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/LICENSE0000664000175000017500000002363700000000000017023 0ustar00zuulzuul00000000000000 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. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8590243 python_watcherclient-4.8.0/PKG-INFO0000644000175000017500000001206700000000000017104 0ustar00zuulzuul00000000000000Metadata-Version: 2.1 Name: python-watcherclient Version: 4.8.0 Summary: Python client library for Watcher API Home-page: https://docs.openstack.org/python-watcherclient/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org 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.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Requires-Python: >=3.9 License-File: LICENSE Requires-Dist: cliff!=2.9.0,>=2.11.0 Requires-Dist: osc-lib>=1.10.0 Requires-Dist: oslo.i18n>=3.20.0 Requires-Dist: oslo.serialization!=2.19.1,>=2.18.0 Requires-Dist: oslo.utils>=3.36.0 Requires-Dist: pbr!=2.1.0,>=3.1.1 Requires-Dist: keystoneauth1>=3.4.0 Requires-Dist: PyYAML>=3.13 ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/python-watcherclient.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on ==================== python-watcherclient ==================== Client for resource optimization service for OpenStack. OpenStack Watcher provides a flexible and scalable resource optimization service for multi-tenant OpenStack-based clouds. Watcher provides a complete optimization loop-including everything from a metrics receiver, complex event processor and profiler, optimization processor and an action plan applier. This provides a robust framework to realize a wide range of cloud optimization goals, including the reduction of data center operating costs, increased system performance via intelligent virtual machine migration, increased energy efficiency and more! * Free software: Apache license * Wiki: https://wiki.openstack.org/wiki/Watcher * Source: https://opendev.org/openstack/python-watcherclient * Bugs: https://bugs.launchpad.net/watcher Installation ============ Install the prerequisite packages --------------------------------- On Ubuntu (tested on 14.04-64) .. code:: sudo apt-get install python-dev libssl-dev python-pip git-core libmysqlclient-dev libffi-dev On Fedora-based distributions e.g., Fedora/RHEL/CentOS/Scientific Linux (tested on CentOS 6.5) .. code:: sudo yum install python-virtualenv openssl-devel python-pip git gcc libffi-devel mysql-devel postgresql-devel On openSUSE-based distributions (SLES 12, openSUSE 13.1, Factory or Tumbleweed) .. code:: sudo zypper install gcc git libmysqlclient-devel libopenssl-devel postgresql-devel python-devel python-pip Install the Watcher client -------------------------- You can install the Watcher CLI with the following command: .. code:: sudo pip install python-watcherclient You can also use the `OpenStack client `_ with Watcher (our watcher plugin for OpenStack client is included in the python-watcherclient package). To install it, you have just to run this command: .. code:: sudo pip install python-openstackclient Configuration ============= Create a **creds** file containing your OpenStack credentials: .. code:: export OS_IDENTITY_API_VERSION=3 export OS_AUTH_URL=http://:5000/v3 export OS_PROJECT_DOMAIN_ID=default export OS_USER_DOMAIN_ID=default export OS_USERNAME=admin export OS_PASSWORD= export OS_PROJECT_NAME= Source these credentials into your current shell session: .. code:: # source creds You should be able to launch the following command which gets the list of previously created Audit Templates: .. code:: # watcher audittemplate list or:: # openstack optimize audittemplate list +--------------------------------+------+----------------------+----------+ | UUID | Name | Goal | Strategy | +--------------------------------+------+----------------------+----------+ +--------------------------------+------+----------------------+----------+ You can view the entire list of available Watcher commands and options using this command: .. code:: # watcher help or:: # openstack help optimize Troubleshootings ================ If any watcher command fails, you can obtain more details with the **--debug** option : .. code:: # watcher --debug audittemplate list or:: # openstack --debug optimize audittemplate list Install the openstack CLI : .. code:: # pip install python-openstackclient Make sure that your Openstack credentials are correct. If so, you should be able to verify that the watcher user has been declared in your Openstack keystone : .. code:: # openstack user list and that the watcher endpoints have been declared as well : .. code:: # openstack endpoint list ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/README.rst0000664000175000017500000000771400000000000017503 0ustar00zuulzuul00000000000000======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/python-watcherclient.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on ==================== python-watcherclient ==================== Client for resource optimization service for OpenStack. OpenStack Watcher provides a flexible and scalable resource optimization service for multi-tenant OpenStack-based clouds. Watcher provides a complete optimization loop-including everything from a metrics receiver, complex event processor and profiler, optimization processor and an action plan applier. This provides a robust framework to realize a wide range of cloud optimization goals, including the reduction of data center operating costs, increased system performance via intelligent virtual machine migration, increased energy efficiency and more! * Free software: Apache license * Wiki: https://wiki.openstack.org/wiki/Watcher * Source: https://opendev.org/openstack/python-watcherclient * Bugs: https://bugs.launchpad.net/watcher Installation ============ Install the prerequisite packages --------------------------------- On Ubuntu (tested on 14.04-64) .. code:: sudo apt-get install python-dev libssl-dev python-pip git-core libmysqlclient-dev libffi-dev On Fedora-based distributions e.g., Fedora/RHEL/CentOS/Scientific Linux (tested on CentOS 6.5) .. code:: sudo yum install python-virtualenv openssl-devel python-pip git gcc libffi-devel mysql-devel postgresql-devel On openSUSE-based distributions (SLES 12, openSUSE 13.1, Factory or Tumbleweed) .. code:: sudo zypper install gcc git libmysqlclient-devel libopenssl-devel postgresql-devel python-devel python-pip Install the Watcher client -------------------------- You can install the Watcher CLI with the following command: .. code:: sudo pip install python-watcherclient You can also use the `OpenStack client `_ with Watcher (our watcher plugin for OpenStack client is included in the python-watcherclient package). To install it, you have just to run this command: .. code:: sudo pip install python-openstackclient Configuration ============= Create a **creds** file containing your OpenStack credentials: .. code:: export OS_IDENTITY_API_VERSION=3 export OS_AUTH_URL=http://:5000/v3 export OS_PROJECT_DOMAIN_ID=default export OS_USER_DOMAIN_ID=default export OS_USERNAME=admin export OS_PASSWORD= export OS_PROJECT_NAME= Source these credentials into your current shell session: .. code:: # source creds You should be able to launch the following command which gets the list of previously created Audit Templates: .. code:: # watcher audittemplate list or:: # openstack optimize audittemplate list +--------------------------------+------+----------------------+----------+ | UUID | Name | Goal | Strategy | +--------------------------------+------+----------------------+----------+ +--------------------------------+------+----------------------+----------+ You can view the entire list of available Watcher commands and options using this command: .. code:: # watcher help or:: # openstack help optimize Troubleshootings ================ If any watcher command fails, you can obtain more details with the **--debug** option : .. code:: # watcher --debug audittemplate list or:: # openstack --debug optimize audittemplate list Install the openstack CLI : .. code:: # pip install python-openstackclient Make sure that your Openstack credentials are correct. If so, you should be able to verify that the watcher user has been declared in your Openstack keystone : .. code:: # openstack user list and that the watcher endpoints have been declared as well : .. code:: # openstack endpoint list ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8390236 python_watcherclient-4.8.0/doc/0000775000175000017500000000000000000000000016550 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/doc/requirements.txt0000664000175000017500000000047100000000000022036 0ustar00zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. openstackdocstheme>=2.2.1 # Apache-2.0 sphinx>=2.0.0,!=2.1.0 # BSD sphinxcontrib-apidoc>=0.2.0 # BSD ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8430238 python_watcherclient-4.8.0/doc/source/0000775000175000017500000000000000000000000020050 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8430238 python_watcherclient-4.8.0/doc/source/cli/0000775000175000017500000000000000000000000020617 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/doc/source/cli/details.rst0000664000175000017500000007322300000000000023005 0ustar00zuulzuul00000000000000.. ################################################### .. ## WARNING ###################################### .. ############## WARNING ########################## .. ########################## WARNING ############## .. ###################################### WARNING ## .. ################################################### .. ################################################### .. ## .. This file is tool-generated. Do not edit manually. .. http://docs.openstack.org/contributor-guide/ .. doc-tools/cli-reference.html .. ## .. ## WARNING ###################################### .. ############## WARNING ########################## .. ########################## WARNING ############## .. ###################################### WARNING ## .. ################################################### ================================================================= Infrastructure Optimization service (watcher) command-line client ================================================================= The watcher client is the command-line interface (CLI) for the Infrastructure Optimization service (watcher) API and its extensions. This chapter documents :command:`watcher` version ``1.3.0``. For help on a specific :command:`watcher` command, enter: .. code-block:: console $ watcher help COMMAND .. _watcher_command_usage: watcher usage ~~~~~~~~~~~~~ .. code-block:: console usage: watcher [--version] [-v | -q] [--log-file LOG_FILE] [-h] [--debug] [--no-auth] [--os-identity-api-version ] [--os-auth-url ] [--os-region-name ] [--os-username ] [--os-user-id ] [--os-password ] [--os-user-domain-id ] [--os-user-domain-name ] [--os-tenant-name ] [--os-tenant-id ] [--os-project-id ] [--os-project-name ] [--os-project-domain-id ] [--os-project-domain-name ] [--os-auth-token ] [--os-watcher-api-version ] [--os-endpoint-type OS_ENDPOINT_TYPE] [--os-endpoint-override ] [--insecure] [--os-cacert ] [--os-cert ] [--os-key ] [--timeout ] .. _watcher_command_options: watcher optional arguments ~~~~~~~~~~~~~~~~~~~~~~~~~~ ``--version`` show program's version number and exit ``-v, --verbose`` Increase verbosity of output. Can be repeated. ``-q, --quiet`` Suppress output except warnings and errors. ``--log-file LOG_FILE`` Specify a file to log output. Disabled by default. ``-h, --help`` Show help message and exit. ``--debug`` Show tracebacks on errors. ``--no-auth, -N`` Do not use authentication. ``--os-identity-api-version `` Specify Identity API version to use. Defaults to ``env[OS_IDENTITY_API_VERSION]`` or 3. ``--os-auth-url , -A `` Defaults to ``env[OS_AUTH_URL]``. ``--os-region-name , -R `` Defaults to ``env[OS_REGION_NAME]``. ``--os-username , -U `` Defaults to ``env[OS_USERNAME]``. ``--os-user-id `` Defaults to ``env[OS_USER_ID]``. ``--os-password , -P `` Defaults to ``env[OS_PASSWORD]``. ``--os-user-domain-id `` Defaults to ``env[OS_USER_DOMAIN_ID]``. ``--os-user-domain-name `` Defaults to ``env[OS_USER_DOMAIN_NAME]``. ``--os-tenant-name , -T `` Defaults to ``env[OS_TENANT_NAME]``. ``--os-tenant-id , -I `` Defaults to ``env[OS_TENANT_ID]``. ``--os-project-id `` Another way to specify tenant ID. This option is mutually exclusive with --os-tenant-id. Defaults to ``env[OS_PROJECT_ID]``. ``--os-project-name `` Another way to specify tenant name. This option is mutually exclusive with --os-tenant-name. Defaults to ``env[OS_PROJECT_NAME]``. ``--os-project-domain-id `` Defaults to ``env[OS_PROJECT_DOMAIN_ID]``. ``--os-project-domain-name `` Defaults to ``env[OS_PROJECT_DOMAIN_NAME]``. ``--os-auth-token `` Defaults to ``env[OS_AUTH_TOKEN]``. ``--os-watcher-api-version `` Defaults to ``env[OS_INFRA_OPTIM_API_VERSION]``. ``--os-endpoint-type OS_ENDPOINT_TYPE`` Defaults to ``env[OS_ENDPOINT_TYPE]`` or "publicURL" ``--os-endpoint-override `` Use this API endpoint instead of the Service Catalog. .. _watcher_action_list: watcher action list ------------------- .. code-block:: console usage: watcher action list [-h] [-f {csv,html,json,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--action-plan ] [--audit ] [--detail] [--limit ] [--sort-key ] [--sort-dir ] [--marker ] List information on retrieved actions. **Optional arguments:** ``-h, --help`` show this help message and exit ``--action-plan `` UUID of the action plan used for filtering. ``--audit `` UUID of the audit used for filtering. ``--detail`` Show detailed information about actions. ``--limit `` Maximum number of actions to return per request, 0 for no limit. Default is the maximum number used by the Watcher API Service. ``--sort-key `` Action field that will be used for sorting. ``--sort-dir `` Sort direction: "asc" (the default) or "desc". ``--marker `` UUID of the last action in the previous page; displays list of actions after "marker". .. _watcher_action_show: watcher action show ------------------- .. code-block:: console usage: watcher action show [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] Show detailed information about a given action. **Positional arguments:** ```` UUID of the action **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_actionplan_cancel: watcher actionplan cancel ------------------------- .. code-block:: console usage: watcher actionplan cancel [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] Cancel action plan command. **Positional arguments:** ```` UUID of the action_plan. **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_actionplan_create: watcher actionplan create ------------------------- .. code-block:: console usage: watcher actionplan create [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] -a [-t ] Create new audit. **Optional arguments:** ``-h, --help`` show this help message and exit ``-a , --audit-template `` Audit template used for this audit (name or uuid). ``-t , --audit_type `` Audit type. It must be ONESHOT or CONTINUOUS. Default is ONESHOT. .. _watcher_actionplan_delete: watcher actionplan delete ------------------------- .. code-block:: console usage: watcher actionplan delete [-h] [ ...] Delete action plan command. **Positional arguments:** ```` UUID of the action plan **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_actionplan_list: watcher actionplan list ----------------------- .. code-block:: console usage: watcher actionplan list [-h] [-f {csv,html,json,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--audit ] [--detail] [--limit ] [--marker ] [--sort-key ] [--sort-dir ] List information on retrieved action plans. **Optional arguments:** ``-h, --help`` show this help message and exit ``--audit `` UUID of an audit used for filtering. ``--detail`` Show detailed information about action plans. ``--limit `` Maximum number of action plans to return per request, 0 for no limit. Default is the maximum number used by the Watcher API Service. ``--marker `` The last actionplan UUID of the previous page; displays list of actionplans after "marker". ``--sort-key `` Action Plan field that will be used for sorting. ``--sort-dir `` Sort direction: "asc" (the default) or "desc". .. _watcher_actionplan_show: watcher actionplan show ----------------------- .. code-block:: console usage: watcher actionplan show [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] Show detailed information about a given action plan. **Positional arguments:** ```` UUID of the action plan **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_actionplan_start: watcher actionplan start ------------------------ .. code-block:: console usage: watcher actionplan start [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] Start action plan command. **Positional arguments:** ```` UUID of the action_plan. **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_actionplan_update: watcher actionplan update ------------------------- .. code-block:: console usage: watcher actionplan update [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] [ ...] Update action plan command. **Positional arguments:** ```` UUID of the action_plan. ```` Operation: 'add', 'replace', or 'remove'. ```` Attribute to add, replace, or remove. Can be specified multiple times. For 'remove', only is necessary. **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_audit_create: watcher audit create -------------------- .. code-block:: console usage: watcher audit create [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] [-t ] [-p ] [-i ] [-g ] [-s ] [-a ] [--auto-trigger] Create new audit. **Optional arguments:** ``-h, --help`` show this help message and exit ``-t , --audit_type `` Audit type. It must be ONESHOT or CONTINUOUS. Default is ONESHOT. ``-p , --parameter `` Record strategy parameter/value metadata. Can be specified multiple times. ``-i , --interval `` Audit interval (in seconds or cron format). Cron inteval can be used like: "\*/5 \* \* \* \*". Only used if the audit is CONTINUOUS. ``-g , --goal `` Goal UUID or name associated to this audit. ``-s , --strategy `` Strategy UUID or name associated to this audit. ``-a , --audit-template `` Audit template used for this audit (name or uuid). ``--auto-trigger`` Trigger automatically action plan once audit is succeeded. .. _watcher_audit_delete: watcher audit delete -------------------- .. code-block:: console usage: watcher audit delete [-h] [ ...] Delete audit command. **Positional arguments:** ```` UUID or name of the audit **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_audit_list: watcher audit list ------------------ .. code-block:: console usage: watcher audit list [-h] [-f {csv,html,json,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--detail] [--goal ] [--strategy ] [--limit ] [--sort-key ] [--sort-dir ] List information on retrieved audits. **Optional arguments:** ``-h, --help`` show this help message and exit ``--detail`` Show detailed information about audits. ``--goal `` UUID or name of the goal used for filtering. ``--strategy `` UUID or name of the strategy used for filtering. ``--limit `` Maximum number of audits to return per request, 0 for no limit. Default is the maximum number used by the Watcher API Service. ``--sort-key `` Audit field that will be used for sorting. ``--sort-dir `` Sort direction: "asc" (the default) or "desc". .. _watcher_audit_show: watcher audit show ------------------ .. code-block:: console usage: watcher audit show [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] Show detailed information about a given audit. **Positional arguments:** ```` UUID or name of the audit **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_audit_update: watcher audit update -------------------- .. code-block:: console usage: watcher audit update [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] [ ...] Update audit command. **Positional arguments:** ```` UUID or name of the audit. ```` Operation: 'add', 'replace', or 'remove'. ```` Attribute to add, replace, or remove. Can be specified multiple times. For 'remove', only is necessary. **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_audittemplate_create: watcher audittemplate create ---------------------------- .. code-block:: console usage: watcher audittemplate create [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] [-s ] [-d ] [--scope ] Create new audit template. **Positional arguments:** ```` Name for this audit template. ```` Goal UUID or name associated to this audit template. **Optional arguments:** ``-h, --help`` show this help message and exit ``-s , --strategy `` Strategy UUID or name associated to this audit template. ``-d , --description `` Description of the audit template. ``--scope `` Part of the cluster on which an audit will be done. Can be provided either in yaml or json file. YAML example: :: --- - host_aggregates: - id: 1 - id: 2 - id: 3 - availability_zones: - name: AZ1 - name: AZ2 - exclude: - instances: - uuid: UUID1 - uuid: UUID2 - compute_nodes: - name: compute1 JSON example: :: [ {'host_aggregates': [ {'id': 1}, {'id': 2}, {'id': 3}]}, {'availability_zones': [ {'name': 'AZ1'}, {'name': 'AZ2'}]}, {'exclude': [ {'instances': [ {'uuid': 'UUID1'}, {'uuid': 'UUID2'}]}, {'compute_nodes': [ {'name': 'compute1'}]}]} ] .. _watcher_audittemplate_delete: watcher audittemplate delete ---------------------------- .. code-block:: console usage: watcher audittemplate delete [-h] [ ...] Delete audit template command. **Positional arguments:** ```` UUID or name of the audit template **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_audittemplate_list: watcher audittemplate list -------------------------- .. code-block:: console usage: watcher audittemplate list [-h] [-f {csv,html,json,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--detail] [--goal ] [--strategy ] [--limit ] [--sort-key ] [--sort-dir ][--marker ] List information on retrieved audit templates. **Optional arguments:** ``-h, --help`` show this help message and exit ``--detail`` Show detailed information about audit templates. ``--goal `` UUID or name of the goal used for filtering. ``--strategy `` UUID or name of the strategy used for filtering. ``--limit `` Maximum number of audit templates to return per request, 0 for no limit. Default is the maximum number used by the Watcher API Service. ``--sort-key `` Audit template field that will be used for sorting. ``--sort-dir `` Sort direction: "asc" (the default) or "desc". ``--marker `` UUID of the last audittemplate in the previous page; displays list of audittemplates after "marker". .. _watcher_audittemplate_show: watcher audittemplate show -------------------------- .. code-block:: console usage: watcher audittemplate show [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] Show detailed information about a given audit template. **Positional arguments:** ```` UUID or name of the audit template **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_audittemplate_update: watcher audittemplate update ---------------------------- .. code-block:: console usage: watcher audittemplate update [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] [ ...] Update audit template command. **Positional arguments:** ```` UUID or name of the audit_template. ```` Operation: 'add', 'replace', or 'remove'. ```` Attribute to add, replace, or remove. Can be specified multiple times. For 'remove', only is necessary. **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_goal_list: watcher goal list ----------------- .. code-block:: console usage: watcher goal list [-h] [-f {csv,html,json,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--detail] [--limit ] [--sort-key ] [--sort-dir ][--marker ] List information on retrieved goals. **Optional arguments:** ``-h, --help`` show this help message and exit ``--detail`` Show detailed information about each goal. ``--limit `` Maximum number of goals to return per request, 0 for no limit. Default is the maximum number used by the Watcher API Service. ``--sort-key `` Goal field that will be used for sorting. ``--sort-dir `` Sort direction: "asc" (the default) or "desc". ``--marker `` UUID of the last goal in the previous page; displays list of goals after "marker". .. _watcher_goal_show: watcher goal show ----------------- .. code-block:: console usage: watcher goal show [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] Show detailed information about a given goal. **Positional arguments:** ```` UUID or name of the goal **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_scoringengine_list: watcher scoringengine list -------------------------- .. code-block:: console usage: watcher scoringengine list [-h] [-f {csv,html,json,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--detail] [--limit ] [--sort-key ] [--sort-dir ][--marker ] List information on retrieved scoring engines. **Optional arguments:** ``-h, --help`` show this help message and exit ``--detail`` Show detailed information about scoring engines. ``--limit `` Maximum number of actions to return per request, 0 for no limit. Default is the maximum number used by the Watcher API Service. ``--sort-key `` Action field that will be used for sorting. ``--sort-dir `` Sort direction: "asc" (the default) or "desc". ``--marker `` UUID of the last scoringengine in the previous page; displays list of scoringengines after "marker". .. _watcher_scoringengine_show: watcher scoringengine show -------------------------- .. code-block:: console usage: watcher scoringengine show [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] Show detailed information about a given scoring engine. **Positional arguments:** ```` Name of the scoring engine **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_service_list: watcher service list -------------------- .. code-block:: console usage: watcher service list [-h] [-f {csv,html,json,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--detail] [--limit ] [--sort-key ] [--sort-dir ] List information on retrieved services. **Optional arguments:** ``-h, --help`` show this help message and exit ``--detail`` Show detailed information about each service. ``--limit `` Maximum number of services to return per request, 0 for no limit. Default is the maximum number used by the Watcher API Service. ``--sort-key `` Goal field that will be used for sorting. ``--sort-dir `` Sort direction: "asc" (the default) or "desc". .. _watcher_service_show: watcher service show -------------------- .. code-block:: console usage: watcher service show [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] Show detailed information about a given service. **Positional arguments:** ```` ID or name of the service **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_datamodel_list: watcher datamodel list ---------------------- .. code-block:: console usage: watcher datamodel list [-h] [-f {csv,json,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--sort-column SORT_COLUMN] [--type ] [--audit ] [--detail] List information on retrieved data model. **Optional arguments:** ``-h, --help`` show this help message and exit ``--type `` Type of Datamodel user want to list. Supported values: compute. Future support values: storage, baremetal. Default type is compute. ``--audit `` UUID of the audit. used to filter data model by the scope in audit. ``--detail`` Show detailed information about data model. .. _watcher_strategy_list: watcher strategy list --------------------- .. code-block:: console usage: watcher strategy list [-h] [-f {csv,html,json,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--goal ] [--detail] [--limit ] [--sort-key ] [--sort-dir ] [--marker ] List information on retrieved strategies. **Optional arguments:** ``-h, --help`` show this help message and exit ``--goal `` UUID or name of the goal ``--detail`` Show detailed information about each strategy. ``--limit `` Maximum number of strategies to return per request, 0 for no limit. Default is the maximum number used by the Watcher API Service. ``--sort-key `` Goal field that will be used for sorting. ``--sort-dir `` Sort direction: "asc" (the default) or "desc". ``--marker `` UUID of the last strategy in the previous page; displays list of strategies after "marker". .. _watcher_strategy_show: watcher strategy show --------------------- .. code-block:: console usage: watcher strategy show [-h] [-f {html,json,shell,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--prefix PREFIX] Show detailed information about a given strategy. **Positional arguments:** ```` UUID or name of the strategy **Optional arguments:** ``-h, --help`` show this help message and exit .. _watcher_strategy_state: watcher strategy state ---------------------- .. code-block:: console usage: watcher strategy state [-h] [-f {csv,html,json,table,value,yaml}] [-c COLUMN] [--max-width ] [--fit-width] [--print-empty] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--sort-column SORT_COLUMN] Retrieve information about strategy requirements. **Positional arguments:** ```` Name of the strategy **Optional arguments:** ``-h, --help`` show this help message and exit ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/doc/source/cli/index.rst0000664000175000017500000000213300000000000022457 0ustar00zuulzuul00000000000000============================= Command-line Tool Reference ============================= In order to use the CLI, you must provide your OpenStack username, password, tenant, and auth endpoint. Use the corresponding configuration options (``--os-username``, ``--os-password``, ``--os-tenant-id``, and ``--os-auth-url``) or set them in environment variables:: export OS_USERNAME=user export OS_PASSWORD=pass export OS_TENANT_ID=b363706f891f48019483f8bd6503c54b export OS_AUTH_URL=http://auth.example.com:5000/v3/ The command line tool will attempt to reauthenticate using your provided credentials for every request. You can override this behavior by manually supplying an auth token using ``--os-watcher-url`` and ``--os-auth-token``. You can alternatively set these environment variables:: export OS_WATCHER_URL=http://watcher.example.org:9322/ export OS_AUTH_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155 Once you've configured your authentication parameters, you can run ``watcher help`` to see a complete listing of available commands. .. toctree:: watcher openstack_cli details ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/doc/source/cli/openstack_cli.rst0000664000175000017500000000456700000000000024203 0ustar00zuulzuul00000000000000===================================================================== :program:`openstack` Command-Line Interface (CLI) with Watcher plugin ===================================================================== .. program:: openstack .. highlight:: bash SYNOPSIS ======== :program:`openstack` [options] :program:`optimize` [command-options] :program:`openstack help optimize` :program:`openstack help optimize` DESCRIPTION =========== The :program:`openstack` command-line interface (CLI) can interact with the OpenStack infra-optim Service (Watcher), by using our additional plugin (included into the python-watcherclient package). In order to use the CLI, you must provide your OpenStack username, password, project (historically called tenant), and auth endpoint. You can use configuration options :option:``--os-username``, :option:``--os-password``, :option:``--os-tenant-id`` (or :option:``--os-tenant-name``), and :option:``--os-auth-url``, or set the corresponding environment variables:: $ export OS_USERNAME=user $ export OS_PASSWORD=password $ export OS_TENANT_ID=b363706f891f48019483f8bd6503c54b # or OS_TENANT_NAME $ export OS_TENANT_NAME=project # or OS_TENANT_ID $ export OS_AUTH_URL=http://auth.example.com:5000/v3/ The command-line tool will attempt to reauthenticate using the provided credentials for every request. You can override this behavior by manually supplying an auth token using :option:``--watcher-url`` and :option:``--os-auth-token``, or by setting the corresponding environment variables:: export WATCHER_URL=http://watcher.example.org:9322/ export OS_AUTH_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155 Since Keystone can return multiple regions in the Service Catalog, you can specify the one you want with :option:``--os-region-name`` or set the following environment variable. (It defaults to the first in the list returned.) :: $ export OS_REGION_NAME=region OPTIONS ======= To get a list of available (sub)commands and options, run:: $ openstack help optimize To get usage and options of a command, run:: $ openstack help optimize EXAMPLES ======== Get information about the audit-create command:: $ openstack help optimize audit create Get a list of available goal:: $ openstack optimize goal list Get a list of audits:: $ openstack optimize audit list ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/doc/source/cli/watcher.rst0000664000175000017500000000522300000000000023010 0ustar00zuulzuul00000000000000=============================================== :program:`watcher` Command-Line Interface (CLI) =============================================== .. program:: watcher .. highlight:: bash SYNOPSIS ======== :program:`watcher` [options] [command-options] :program:`watcher help` :program:`watcher help` DESCRIPTION =========== The :program:`watcher` command-line interface (CLI) interacts with the OpenStack infra-optim Service (Watcher). In order to use the CLI, you must provide your OpenStack username, password, project (historically called tenant), and auth endpoint. You can use configuration options :option:``--os-username``, :option:``--os-password``, :option:``--os-tenant-id`` (or :option:``--os-tenant-name``), and :option:``--os-auth-url``, or set the corresponding environment variables:: $ export OS_USERNAME=user $ export OS_PASSWORD=password $ export OS_TENANT_ID=b363706f891f48019483f8bd6503c54b # or OS_TENANT_NAME $ export OS_TENANT_NAME=project # or OS_TENANT_ID $ export OS_AUTH_URL=http://auth.example.com:5000/v3/ The command-line tool will attempt to reauthenticate using the provided credentials for every request. You can override this behavior by manually supplying an auth token using :option:``--watcher-url`` and :option:``--os-auth-token``, or by setting the corresponding environment variables:: $ export WATCHER_URL=http://watcher.example.org:9322/ $ export OS_AUTH_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155 Since Keystone can return multiple regions in the Service Catalog, you can specify the one you want with :option:``--os-region-name`` or set the following environment variable. (It defaults to the first in the list returned.) :: $ export OS_REGION_NAME=region Watcher CLI supports bash completion. The command-line tool can automatically fill partially typed commands. To use this feature, source the below file (available at https://opendev.org/openstack/python-watcherclient/src/branch/master/tools/watcher.bash_completion) to your terminal and then bash completion should work:: $ . watcher.bash_completion To avoid doing this every time, add this to your ``.bashrc`` or copy the watcher.bash_completion file to the default bash completion scripts directory on your linux distribution. OPTIONS ======= To get a list of available (sub)commands and options, run:: $ watcher help To get usage and options of a command, run:: $ watcher help EXAMPLES ======== Get information about the audit-create command:: $ watcher help audit create Get a list of available goal:: $ watcher goal list Get a list of audits:: $ watcher audit list ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/doc/source/conf.py0000664000175000017500000000634400000000000021356 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # -- 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', 'sphinx.ext.viewcode', 'sphinxcontrib.apidoc', 'openstackdocstheme', ] # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable # sphinxcontrib.apidoc options apidoc_module_dir = '../../watcherclient' apidoc_output_dir = 'reference/api' apidoc_excluded_paths = [ 'tests/*'] apidoc_separate_modules = True # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'python-watcherclient' copyright = 'OpenStack Foundation' # A list of ignored prefixes for module index sorting. modindex_common_prefix = ['watcherclient.'] # 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 -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ["."] # html_theme = '_theme' # html_static_path = ['_static'] html_theme = 'openstackdocs' # html_theme_path = [openstackdocstheme.get_html_theme_path()] # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ( 'index', '%s.tex' % project, '%s Documentation' % project, 'OpenStack Foundation', 'manual' ), ] # Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664 latex_use_xindy = False latex_domain_indices = False latex_elements = { 'makeindex': '', 'printindex': '', 'preamble': r'\setcounter{tocdepth}{3}', } # openstackdocstheme options openstackdocs_repo_name = 'openstack/python-watcherclient' openstackdocs_pdf_link = True openstackdocs_bug_project = 'python-watcherclient' openstackdocs_bug_tag = '' #html_theme_options = {"show_other_versions": "True"} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/doc/source/contributing.rst0000664000175000017500000000260600000000000023315 0ustar00zuulzuul00000000000000.. _contributing: ==================================== Contributing to python-watcherclient ==================================== If you're interested in contributing to the python-watcherclient project, the following will help get you started. Contributor License Agreement ----------------------------- .. index:: single: license; agreement In order to contribute to the python-watcherclient project, you need to have signed OpenStack's contributor's agreement. .. seealso:: * https://docs.openstack.org/infra/manual/developers.html * https://wiki.openstack.org/CLA LaunchPad Project ----------------- Most of the tools used for OpenStack depend on a launchpad.net ID for authentication. After signing up for a launchpad account, join the "openstack" team to have access to the mailing list and receive notifications of important events. .. seealso:: * http://launchpad.net * http://launchpad.net/python-watcherclient * http://launchpad.net/~openstack Project Hosting Details ------------------------- Bug tracker https://launchpad.net/python-watcherclient Mailing list (prefix subjects with ``[watcher]`` for faster responses) http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss Code Hosting https://opendev.org/openstack/python-watcherclient Code Review https://review.opendev.org/#/q/status:open+project:openstack/python-watcherclient,n,z ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/doc/source/index.rst0000664000175000017500000000061600000000000021714 0ustar00zuulzuul00000000000000Python bindings to the OpenStack Watcher API ============================================ This is a client for OpenStack Watcher API. There's a Python API (the :mod:`watcherclient` modules), and a command-line script (installed as :program:`watcher`). Each implements the entire OpenStack Watcher API. .. toctree:: :maxdepth: 2 cli/index reference/index installation contributing ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/doc/source/installation.rst0000664000175000017500000000054700000000000023311 0ustar00zuulzuul00000000000000============ Installation ============ If you have `virtualenvwrapper `_ installed:: $ mkvirtualenv python-watcherclient $ git clone https://opendev.org/openstack/python-watcherclient $ cd python-watcherclient && python setup.py install $ pip install -r ./requirements.txt ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8430238 python_watcherclient-4.8.0/doc/source/reference/0000775000175000017500000000000000000000000022006 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/doc/source/reference/api_v1.rst0000664000175000017500000000575600000000000023734 0ustar00zuulzuul00000000000000.. _api_v1: ======================== watcherclient Python API ======================== The watcherclient python API lets you access watcher, the OpenStack TODEFINE Service. For example, to manipulate audits, you interact with an `watcherclient.v1.audit`_ object. You obtain access to audits via attributes of the `watcherclient.v1.client.Client`_ object. Usage ===== Get a Client object ------------------- First, create an `watcherclient.v1.client.Client`_ instance by passing your credentials to `watcherclient.client.get_client()`_. By default, the Watcher system is configured so that only administrators (users with 'admin' role) have access. There are two different sets of credentials that can be used:: * watcher endpoint and auth token * Identity Service (keystone) credentials Using watcher endpoint and auth token ..................................... An auth token and the watcher endpoint can be used to authenticate:: * os_auth_token: authentication token (from Identity Service) * watcher_url: watcher API endpoint, eg http://watcher.example.org:9322/v1 To create the client, you can use the API like so:: >>> from watcherclient import client >>> >>> kwargs = {'os_auth_token': '3bcc3d3a03f44e3d8377f9247b0ad155' >>> 'watcher_url': 'http://watcher.example.org:9322/'} >>> watcher = client.get_client(1, **kwargs) Using Identity Service (keystone) credentials ............................................. These Identity Service credentials can be used to authenticate:: * os_username: name of user * os_password: user's password * os_auth_url: Identity Service endpoint for authorization * os_tenant_{name|id}: name or ID of tenant To create a client, you can use the API like so:: >>> from watcherclient import client >>> >>> kwargs = {'os_username': 'name', >>> 'os_password': 'password', >>> 'os_auth_url': 'http://keystone.example.org:5000/', >>> 'os_tenant_name': 'tenant'} >>> watcher = client.get_client(1, **kwargs) Perform watcher operations -------------------------- Once you have an watcher `Client`_, you can perform various tasks:: >>> watcher.action.list() # list of actions >>> watcher.action_plan.list() # list of action_plan >>> watcher.audit.get(audit_uuid_or_name) # information about a particular audit When the `Client`_ needs to propagate an exception, it will usually raise an instance listed in `watcherclient.exceptions`_. Refer to the modules themselves, for more details. ===================== watcherclient Modules ===================== .. _watcherclient.v1.audit: api/watcherclient.v1.audit.html#watcherclient.v1.audit.Audit .. _watcherclient.v1.client.Client: api/watcherclient.v1.client.html#watcherclient.v1.client.Client .. _Client: api/watcherclient.v1.client.html#watcherclient.v1.client.Client .. _watcherclient.client.get_client(): api/watcherclient.client.html#watcherclient.client.get_client .. _watcherclient.exceptions: api/watcherclient.exceptions.html ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/doc/source/reference/index.rst0000664000175000017500000000047500000000000023655 0ustar00zuulzuul00000000000000========================== Python Library Reference ========================== 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. .. toctree:: :maxdepth: 2 api/modules api_v1 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8590243 python_watcherclient-4.8.0/python_watcherclient.egg-info/0000775000175000017500000000000000000000000023732 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768124.0 python_watcherclient-4.8.0/python_watcherclient.egg-info/PKG-INFO0000644000175000017500000001206700000000000025033 0ustar00zuulzuul00000000000000Metadata-Version: 2.1 Name: python-watcherclient Version: 4.8.0 Summary: Python client library for Watcher API Home-page: https://docs.openstack.org/python-watcherclient/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org 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.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Requires-Python: >=3.9 License-File: LICENSE Requires-Dist: cliff!=2.9.0,>=2.11.0 Requires-Dist: osc-lib>=1.10.0 Requires-Dist: oslo.i18n>=3.20.0 Requires-Dist: oslo.serialization!=2.19.1,>=2.18.0 Requires-Dist: oslo.utils>=3.36.0 Requires-Dist: pbr!=2.1.0,>=3.1.1 Requires-Dist: keystoneauth1>=3.4.0 Requires-Dist: PyYAML>=3.13 ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/python-watcherclient.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on ==================== python-watcherclient ==================== Client for resource optimization service for OpenStack. OpenStack Watcher provides a flexible and scalable resource optimization service for multi-tenant OpenStack-based clouds. Watcher provides a complete optimization loop-including everything from a metrics receiver, complex event processor and profiler, optimization processor and an action plan applier. This provides a robust framework to realize a wide range of cloud optimization goals, including the reduction of data center operating costs, increased system performance via intelligent virtual machine migration, increased energy efficiency and more! * Free software: Apache license * Wiki: https://wiki.openstack.org/wiki/Watcher * Source: https://opendev.org/openstack/python-watcherclient * Bugs: https://bugs.launchpad.net/watcher Installation ============ Install the prerequisite packages --------------------------------- On Ubuntu (tested on 14.04-64) .. code:: sudo apt-get install python-dev libssl-dev python-pip git-core libmysqlclient-dev libffi-dev On Fedora-based distributions e.g., Fedora/RHEL/CentOS/Scientific Linux (tested on CentOS 6.5) .. code:: sudo yum install python-virtualenv openssl-devel python-pip git gcc libffi-devel mysql-devel postgresql-devel On openSUSE-based distributions (SLES 12, openSUSE 13.1, Factory or Tumbleweed) .. code:: sudo zypper install gcc git libmysqlclient-devel libopenssl-devel postgresql-devel python-devel python-pip Install the Watcher client -------------------------- You can install the Watcher CLI with the following command: .. code:: sudo pip install python-watcherclient You can also use the `OpenStack client `_ with Watcher (our watcher plugin for OpenStack client is included in the python-watcherclient package). To install it, you have just to run this command: .. code:: sudo pip install python-openstackclient Configuration ============= Create a **creds** file containing your OpenStack credentials: .. code:: export OS_IDENTITY_API_VERSION=3 export OS_AUTH_URL=http://:5000/v3 export OS_PROJECT_DOMAIN_ID=default export OS_USER_DOMAIN_ID=default export OS_USERNAME=admin export OS_PASSWORD= export OS_PROJECT_NAME= Source these credentials into your current shell session: .. code:: # source creds You should be able to launch the following command which gets the list of previously created Audit Templates: .. code:: # watcher audittemplate list or:: # openstack optimize audittemplate list +--------------------------------+------+----------------------+----------+ | UUID | Name | Goal | Strategy | +--------------------------------+------+----------------------+----------+ +--------------------------------+------+----------------------+----------+ You can view the entire list of available Watcher commands and options using this command: .. code:: # watcher help or:: # openstack help optimize Troubleshootings ================ If any watcher command fails, you can obtain more details with the **--debug** option : .. code:: # watcher --debug audittemplate list or:: # openstack --debug optimize audittemplate list Install the openstack CLI : .. code:: # pip install python-openstackclient Make sure that your Openstack credentials are correct. If so, you should be able to verify that the watcher user has been declared in your Openstack keystone : .. code:: # openstack user list and that the watcher endpoints have been declared as well : .. code:: # openstack endpoint list ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768124.0 python_watcherclient-4.8.0/python_watcherclient.egg-info/SOURCES.txt0000664000175000017500000000670400000000000025625 0ustar00zuulzuul00000000000000.coveragerc .stestr.conf .zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst requirements.txt setup.cfg setup.py test-requirements.txt tox.ini doc/requirements.txt doc/source/conf.py doc/source/contributing.rst doc/source/index.rst doc/source/installation.rst doc/source/cli/details.rst doc/source/cli/index.rst doc/source/cli/openstack_cli.rst doc/source/cli/watcher.rst doc/source/reference/api_v1.rst doc/source/reference/index.rst python_watcherclient.egg-info/PKG-INFO python_watcherclient.egg-info/SOURCES.txt python_watcherclient.egg-info/dependency_links.txt python_watcherclient.egg-info/entry_points.txt python_watcherclient.egg-info/not-zip-safe python_watcherclient.egg-info/pbr.json python_watcherclient.egg-info/requires.txt python_watcherclient.egg-info/top_level.txt releasenotes/notes/drop-py-2-7-f7078b44cf99cae1.yaml tools/watcher.bash_completion watcherclient/__init__.py watcherclient/_i18n.py watcherclient/client.py watcherclient/exceptions.py watcherclient/shell.py watcherclient/version.py watcherclient/common/__init__.py watcherclient/common/api_versioning.py watcherclient/common/base.py watcherclient/common/command.py watcherclient/common/httpclient.py watcherclient/common/utils.py watcherclient/common/apiclient/__init__.py watcherclient/common/apiclient/base.py watcherclient/common/apiclient/exceptions.py watcherclient/locale/watcherclient.pot watcherclient/locale/fr/LC_MESSAGES/watcherclient.po watcherclient/osc/__init__.py watcherclient/osc/plugin.py watcherclient/tests/__init__.py watcherclient/tests/unit/__init__.py watcherclient/tests/unit/keystone_client_fixtures.py watcherclient/tests/unit/test_client.py watcherclient/tests/unit/test_import.py watcherclient/tests/unit/test_utils.py watcherclient/tests/unit/utils.py watcherclient/tests/unit/common/__init__.py watcherclient/tests/unit/common/test_api_versioning.py watcherclient/tests/unit/v1/__init__.py watcherclient/tests/unit/v1/base.py watcherclient/tests/unit/v1/test_action.py watcherclient/tests/unit/v1/test_action_plan.py watcherclient/tests/unit/v1/test_action_plan_shell.py watcherclient/tests/unit/v1/test_action_shell.py watcherclient/tests/unit/v1/test_audit.py watcherclient/tests/unit/v1/test_audit_shell.py watcherclient/tests/unit/v1/test_audit_template.py watcherclient/tests/unit/v1/test_audit_template_shell.py watcherclient/tests/unit/v1/test_data_model.py watcherclient/tests/unit/v1/test_data_model_shell.py watcherclient/tests/unit/v1/test_goal.py watcherclient/tests/unit/v1/test_goal_shell.py watcherclient/tests/unit/v1/test_scoring_engine.py watcherclient/tests/unit/v1/test_scoring_engine_shell.py watcherclient/tests/unit/v1/test_service.py watcherclient/tests/unit/v1/test_service_shell.py watcherclient/tests/unit/v1/test_strategy.py watcherclient/tests/unit/v1/test_strategy_shell.py watcherclient/v1/__init__.py watcherclient/v1/action.py watcherclient/v1/action_plan.py watcherclient/v1/action_plan_shell.py watcherclient/v1/action_shell.py watcherclient/v1/audit.py watcherclient/v1/audit_shell.py watcherclient/v1/audit_template.py watcherclient/v1/audit_template_shell.py watcherclient/v1/client.py watcherclient/v1/data_model.py watcherclient/v1/data_model_shell.py watcherclient/v1/goal.py watcherclient/v1/goal_shell.py watcherclient/v1/resource_fields.py watcherclient/v1/scoring_engine.py watcherclient/v1/scoring_engine_shell.py watcherclient/v1/service.py watcherclient/v1/service_shell.py watcherclient/v1/strategy.py watcherclient/v1/strategy_shell.py././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768124.0 python_watcherclient-4.8.0/python_watcherclient.egg-info/dependency_links.txt0000664000175000017500000000000100000000000030000 0ustar00zuulzuul00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768124.0 python_watcherclient-4.8.0/python_watcherclient.egg-info/entry_points.txt0000664000175000017500000000773100000000000027240 0ustar00zuulzuul00000000000000[console_scripts] watcher = watcherclient.shell:main [openstack.cli.extension] infra_optim = watcherclient.osc.plugin [openstack.infra_optim.v1] optimize_action_list = watcherclient.v1.action_shell:ListAction optimize_action_show = watcherclient.v1.action_shell:ShowAction optimize_actionplan_cancel = watcherclient.v1.action_plan_shell:CancelActionPlan optimize_actionplan_delete = watcherclient.v1.action_plan_shell:DeleteActionPlan optimize_actionplan_list = watcherclient.v1.action_plan_shell:ListActionPlan optimize_actionplan_show = watcherclient.v1.action_plan_shell:ShowActionPlan optimize_actionplan_start = watcherclient.v1.action_plan_shell:StartActionPlan optimize_actionplan_update = watcherclient.v1.action_plan_shell:UpdateActionPlan optimize_audit_create = watcherclient.v1.audit_shell:CreateAudit optimize_audit_delete = watcherclient.v1.audit_shell:DeleteAudit optimize_audit_list = watcherclient.v1.audit_shell:ListAudit optimize_audit_show = watcherclient.v1.audit_shell:ShowAudit optimize_audit_update = watcherclient.v1.audit_shell:UpdateAudit optimize_audittemplate_create = watcherclient.v1.audit_template_shell:CreateAuditTemplate optimize_audittemplate_delete = watcherclient.v1.audit_template_shell:DeleteAuditTemplate optimize_audittemplate_list = watcherclient.v1.audit_template_shell:ListAuditTemplate optimize_audittemplate_show = watcherclient.v1.audit_template_shell:ShowAuditTemplate optimize_audittemplate_update = watcherclient.v1.audit_template_shell:UpdateAuditTemplate optimize_datamodel_list = watcherclient.v1.data_model_shell:ListDataModel optimize_goal_list = watcherclient.v1.goal_shell:ListGoal optimize_goal_show = watcherclient.v1.goal_shell:ShowGoal optimize_scoringengine_list = watcherclient.v1.scoring_engine_shell:ListScoringEngine optimize_scoringengine_show = watcherclient.v1.scoring_engine_shell:ShowScoringEngine optimize_service_list = watcherclient.v1.service_shell:ListService optimize_service_show = watcherclient.v1.service_shell:ShowService optimize_strategy_list = watcherclient.v1.strategy_shell:ListStrategy optimize_strategy_show = watcherclient.v1.strategy_shell:ShowStrategy optimize_strategy_state = watcherclient.v1.strategy_shell:StateStrategy [watcherclient.v1] action_list = watcherclient.v1.action_shell:ListAction action_show = watcherclient.v1.action_shell:ShowAction actionplan_cancel = watcherclient.v1.action_plan_shell:CancelActionPlan actionplan_delete = watcherclient.v1.action_plan_shell:DeleteActionPlan actionplan_list = watcherclient.v1.action_plan_shell:ListActionPlan actionplan_show = watcherclient.v1.action_plan_shell:ShowActionPlan actionplan_start = watcherclient.v1.action_plan_shell:StartActionPlan actionplan_update = watcherclient.v1.action_plan_shell:UpdateActionPlan audit_create = watcherclient.v1.audit_shell:CreateAudit audit_delete = watcherclient.v1.audit_shell:DeleteAudit audit_list = watcherclient.v1.audit_shell:ListAudit audit_show = watcherclient.v1.audit_shell:ShowAudit audit_update = watcherclient.v1.audit_shell:UpdateAudit audittemplate_create = watcherclient.v1.audit_template_shell:CreateAuditTemplate audittemplate_delete = watcherclient.v1.audit_template_shell:DeleteAuditTemplate audittemplate_list = watcherclient.v1.audit_template_shell:ListAuditTemplate audittemplate_show = watcherclient.v1.audit_template_shell:ShowAuditTemplate audittemplate_update = watcherclient.v1.audit_template_shell:UpdateAuditTemplate datamodel_list = watcherclient.v1.data_model_shell:ListDataModel goal_list = watcherclient.v1.goal_shell:ListGoal goal_show = watcherclient.v1.goal_shell:ShowGoal scoringengine_list = watcherclient.v1.scoring_engine_shell:ListScoringEngine scoringengine_show = watcherclient.v1.scoring_engine_shell:ShowScoringEngine service_list = watcherclient.v1.service_shell:ListService service_show = watcherclient.v1.service_shell:ShowService strategy_list = watcherclient.v1.strategy_shell:ListStrategy strategy_show = watcherclient.v1.strategy_shell:ShowStrategy strategy_state = watcherclient.v1.strategy_shell:StateStrategy ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768124.0 python_watcherclient-4.8.0/python_watcherclient.egg-info/not-zip-safe0000664000175000017500000000000100000000000026160 0ustar00zuulzuul00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768124.0 python_watcherclient-4.8.0/python_watcherclient.egg-info/pbr.json0000664000175000017500000000005600000000000025411 0ustar00zuulzuul00000000000000{"git_version": "edbc62c", "is_release": true}././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768124.0 python_watcherclient-4.8.0/python_watcherclient.egg-info/requires.txt0000664000175000017500000000024400000000000026332 0ustar00zuulzuul00000000000000cliff!=2.9.0,>=2.11.0 osc-lib>=1.10.0 oslo.i18n>=3.20.0 oslo.serialization!=2.19.1,>=2.18.0 oslo.utils>=3.36.0 pbr!=2.1.0,>=3.1.1 keystoneauth1>=3.4.0 PyYAML>=3.13 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768124.0 python_watcherclient-4.8.0/python_watcherclient.egg-info/top_level.txt0000664000175000017500000000001600000000000026461 0ustar00zuulzuul00000000000000watcherclient ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8350234 python_watcherclient-4.8.0/releasenotes/0000775000175000017500000000000000000000000020474 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8430238 python_watcherclient-4.8.0/releasenotes/notes/0000775000175000017500000000000000000000000021624 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/releasenotes/notes/drop-py-2-7-f7078b44cf99cae1.yaml0000664000175000017500000000034100000000000026713 0ustar00zuulzuul00000000000000--- upgrade: - | Python 2.7 support has been dropped. Last release of python-watcherclient to support py2.7 is OpenStack Train. The minimum version of Python now supported by python-watcherclient is Python 3.6. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/requirements.txt0000664000175000017500000000040500000000000021266 0ustar00zuulzuul00000000000000cliff!=2.9.0,>=2.11.0 # Apache-2.0 osc-lib>=1.10.0 # Apache-2.0 oslo.i18n>=3.20.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 oslo.utils>=3.36.0 # Apache-2.0 pbr!=2.1.0,>=3.1.1 # Apache-2.0 keystoneauth1>=3.4.0 # Apache-2.0 PyYAML>=3.13 # MIT ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8590243 python_watcherclient-4.8.0/setup.cfg0000664000175000017500000001173100000000000017627 0ustar00zuulzuul00000000000000[metadata] name = python-watcherclient summary = Python client library for Watcher API description_file = README.rst author = OpenStack author_email = openstack-discuss@lists.openstack.org home_page = https://docs.openstack.org/python-watcherclient/latest/ python_requires = >=3.9 classifier = 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.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 [files] packages = watcherclient [entry_points] console_scripts = watcher = watcherclient.shell:main openstack.cli.extension = infra_optim = watcherclient.osc.plugin openstack.infra_optim.v1 = optimize_goal_show = watcherclient.v1.goal_shell:ShowGoal optimize_goal_list = watcherclient.v1.goal_shell:ListGoal optimize_strategy_show = watcherclient.v1.strategy_shell:ShowStrategy optimize_strategy_list = watcherclient.v1.strategy_shell:ListStrategy optimize_strategy_state = watcherclient.v1.strategy_shell:StateStrategy optimize_audittemplate_show = watcherclient.v1.audit_template_shell:ShowAuditTemplate optimize_audittemplate_list = watcherclient.v1.audit_template_shell:ListAuditTemplate optimize_audittemplate_create = watcherclient.v1.audit_template_shell:CreateAuditTemplate optimize_audittemplate_update = watcherclient.v1.audit_template_shell:UpdateAuditTemplate optimize_audittemplate_delete = watcherclient.v1.audit_template_shell:DeleteAuditTemplate optimize_audit_show = watcherclient.v1.audit_shell:ShowAudit optimize_audit_list = watcherclient.v1.audit_shell:ListAudit optimize_audit_create = watcherclient.v1.audit_shell:CreateAudit optimize_audit_update = watcherclient.v1.audit_shell:UpdateAudit optimize_audit_delete = watcherclient.v1.audit_shell:DeleteAudit optimize_actionplan_show = watcherclient.v1.action_plan_shell:ShowActionPlan optimize_actionplan_delete = watcherclient.v1.action_plan_shell:DeleteActionPlan optimize_actionplan_list = watcherclient.v1.action_plan_shell:ListActionPlan optimize_actionplan_update = watcherclient.v1.action_plan_shell:UpdateActionPlan optimize_actionplan_start = watcherclient.v1.action_plan_shell:StartActionPlan optimize_actionplan_cancel = watcherclient.v1.action_plan_shell:CancelActionPlan optimize_action_show = watcherclient.v1.action_shell:ShowAction optimize_action_list = watcherclient.v1.action_shell:ListAction optimize_scoringengine_show = watcherclient.v1.scoring_engine_shell:ShowScoringEngine optimize_scoringengine_list = watcherclient.v1.scoring_engine_shell:ListScoringEngine optimize_service_show = watcherclient.v1.service_shell:ShowService optimize_service_list = watcherclient.v1.service_shell:ListService optimize_datamodel_list = watcherclient.v1.data_model_shell:ListDataModel watcherclient.v1 = goal_show = watcherclient.v1.goal_shell:ShowGoal goal_list = watcherclient.v1.goal_shell:ListGoal strategy_show = watcherclient.v1.strategy_shell:ShowStrategy strategy_list = watcherclient.v1.strategy_shell:ListStrategy strategy_state = watcherclient.v1.strategy_shell:StateStrategy audittemplate_show = watcherclient.v1.audit_template_shell:ShowAuditTemplate audittemplate_list = watcherclient.v1.audit_template_shell:ListAuditTemplate audittemplate_create = watcherclient.v1.audit_template_shell:CreateAuditTemplate audittemplate_update = watcherclient.v1.audit_template_shell:UpdateAuditTemplate audittemplate_delete = watcherclient.v1.audit_template_shell:DeleteAuditTemplate audit_show = watcherclient.v1.audit_shell:ShowAudit audit_list = watcherclient.v1.audit_shell:ListAudit audit_create = watcherclient.v1.audit_shell:CreateAudit audit_update = watcherclient.v1.audit_shell:UpdateAudit audit_delete = watcherclient.v1.audit_shell:DeleteAudit actionplan_show = watcherclient.v1.action_plan_shell:ShowActionPlan actionplan_list = watcherclient.v1.action_plan_shell:ListActionPlan actionplan_update = watcherclient.v1.action_plan_shell:UpdateActionPlan actionplan_start = watcherclient.v1.action_plan_shell:StartActionPlan actionplan_delete = watcherclient.v1.action_plan_shell:DeleteActionPlan actionplan_cancel = watcherclient.v1.action_plan_shell:CancelActionPlan action_show = watcherclient.v1.action_shell:ShowAction action_list = watcherclient.v1.action_shell:ListAction scoringengine_show = watcherclient.v1.scoring_engine_shell:ShowScoringEngine scoringengine_list = watcherclient.v1.scoring_engine_shell:ListScoringEngine service_show = watcherclient.v1.service_shell:ShowService service_list = watcherclient.v1.service_shell:ListService datamodel_list = watcherclient.v1.data_model_shell:ListDataModel [pbr] autodoc_index_modules = True autodoc_exclude_modules = watcherclient.tests.* api_doc_dir = reference/api [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/setup.py0000664000175000017500000000127100000000000017516 0ustar00zuulzuul00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import setuptools setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/test-requirements.txt0000664000175000017500000000037700000000000022253 0ustar00zuulzuul00000000000000coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD hacking>=7.0.0,<7.1.0 # Apache-2.0 oslotest>=3.2.0 # Apache-2.0 python-subunit>=1.0.0 # Apache-2.0/BSD stestr>=2.0.0 # Apache-2.0 testscenarios>=0.4 # Apache-2.0/BSD testtools>=2.2.0 # MIT ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8430238 python_watcherclient-4.8.0/tools/0000775000175000017500000000000000000000000017143 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/tools/watcher.bash_completion0000664000175000017500000000200400000000000023664 0ustar00zuulzuul00000000000000_watcher_opts="" # lazy init _watcher_flags="" # lazy init _watcher_opts_exp="" # lazy init _watcher() { local cur prev nbc cflags COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" if [ "x$_watcher_opts" == "x" ] ; then nbc="`watcher bash-completion | sed -e "s/ *-h */ /" -e "s/ *-i */ /"`" _watcher_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/ */ /g"`" _watcher_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/ */ /g"`" _watcher_opts_exp="`echo "$_watcher_opts" | tr ' ' '|'`" fi if [[ " ${COMP_WORDS[@]} " =~ " "($_watcher_opts_exp)" " && "$prev" != "help" ]] ; then COMPLETION_CACHE=$HOME/.cache/python-watcherclient/*/*-cache cflags="$_watcher_flags "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ') COMPREPLY=($(compgen -W "${cflags}" -- ${cur})) else COMPREPLY=($(compgen -W "${_watcher_opts}" -- ${cur})) fi return 0 } complete -F _watcher watcher ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/tox.ini0000664000175000017500000000345300000000000017323 0ustar00zuulzuul00000000000000[tox] minversion = 3.18.0 envlist = py3,pep8 [testenv] usedevelop = True passenv = ZUUL_CACHE_DIR REQUIREMENTS_PIP_LOCATION install_command = pip install {opts} {packages} setenv = VIRTUAL_ENV={envdir} deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt allowlist_externals = rm commands = rm -f .testrepository/times.dbm # The --test-path is defined in .stestr.conf stestr run {posargs} stestr slowest [testenv:pep8] basepython = python3 commands = flake8 [testenv:venv] basepython = python3 commands = {posargs} [testenv:cover] basepython = python3 setenv = PYTHON=coverage run --source watcherclient --parallel-mode commands = stestr run {posargs} coverage combine coverage html -d cover coverage xml -o cover/coverage.xml coverage report [testenv:docs] basepython = python3 deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/doc/requirements.txt commands = sphinx-build -W -b html doc/source doc/build/html [testenv:pdf-docs] basepython = python3 envdir = {toxworkdir}/docs deps = {[testenv:docs]deps} allowlist_externals = rm make commands = rm -rf doc/build/pdf sphinx-build -W -b latex doc/source doc/build/pdf make -C doc/build/pdf [testenv:debug] basepython = python3 commands = oslo_debug_helper -t watcherclient/tests/unit {posargs} [flake8] # E123, E125 skipped as they are invalid PEP-8. show-source = True enable-extensions = H203,H106 ignore = E123,E125,W504 builtins = _ exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build [testenv:wheel] basepython = python3 commands = python setup.py bdist_wheel [hacking] import_exceptions = watcherclient._i18n ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1740768124.847024 python_watcherclient-4.8.0/watcherclient/0000775000175000017500000000000000000000000020637 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/__init__.py0000664000175000017500000000246600000000000022760 0ustar00zuulzuul00000000000000# # Copyright 2013 Hewlett-Packard Development Company, L.P. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import pbr.version from watcherclient import client from watcherclient.common import api_versioning from watcherclient import exceptions __version__ = pbr.version.VersionInfo( 'python-watcherclient').version_string() __all__ = ['client', 'exceptions', ] API_MIN_VERSION = api_versioning.APIVersion("1.0") # The max version should be the latest version that is supported in the client, # not necessarily the latest that the server can provide. This is only bumped # when client supported the max version, and bumped sequentially, otherwise # the client may break due to server side new version may include some # backward incompatible change. API_MAX_VERSION = api_versioning.APIVersion("1.1") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/_i18n.py0000664000175000017500000000217500000000000022134 0ustar00zuulzuul00000000000000# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import oslo_i18n DOMAIN = "watcherclient" _translators = oslo_i18n.TranslatorFactory(domain=DOMAIN) # The primary translation function using the well-known name "_" _ = _translators.primary # The contextual translation function using the name "_C" # requires oslo.i18n >=2.1.0 _C = _translators.contextual_form # The plural translation function using the name "_P" # requires oslo.i18n >=2.1.0 _P = _translators.plural_form def get_available_languages(): return oslo_i18n.get_available_languages(DOMAIN) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/client.py0000664000175000017500000002010400000000000022464 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneauth1 import loading as kaloading from oslo_utils import importutils from watcherclient._i18n import _ from watcherclient.common import api_versioning from watcherclient import exceptions def get_client(api_version, os_auth_token=None, watcher_url=None, os_username=None, os_password=None, os_auth_url=None, os_project_id=None, os_project_name=None, os_tenant_id=None, os_tenant_name=None, os_region_name=None, os_user_domain_id=None, os_user_domain_name=None, os_project_domain_id=None, os_project_domain_name=None, os_service_type=None, os_endpoint_type=None, insecure=None, timeout=None, os_cacert=None, ca_file=None, os_cert=None, cert_file=None, os_key=None, key_file=None, os_infra_optim_api_version=None, max_retries=None, retry_interval=None, session=None, os_endpoint_override=None, **ignored_kwargs): """Get an authenticated client, based on the credentials. :param api_version: the API version to use. Valid value: '1'. :param os_auth_token: pre-existing token to re-use :param watcher_url: watcher API endpoint :param os_username: name of a user :param os_password: user's password :param os_auth_url: endpoint to authenticate against :param os_project_id: ID of a project :param os_project_name: name of a project :param os_tenant_id: ID of a tenant (deprecated in favour of os_project_id) :param os_tenant_name: name of a tenant (deprecated in favour of os_project_name) :param os_region_name: name of a keystone region :param os_user_domain_id: ID of a domain the user belongs to :param os_user_domain_name: name of a domain the user belongs to :param os_project_domain_id: ID of a domain the project belongs to :param os_project_domain_name: name of a domain the project belongs to :param os_service_type: the type of service to lookup the endpoint for :param os_endpoint_type: the type (exposure) of the endpoint :param insecure: allow insecure SSL (no cert verification) :param timeout: allows customization of the timeout for client HTTP requests :param os_cacert: path to cacert file :param ca_file: path to cacert file, deprecated in favour of os_cacert :param os_cert: path to cert file :param cert_file: path to cert file, deprecated in favour of os_cert :param os_key: path to key file :param key_file: path to key file, deprecated in favour of os_key :param os_infra_optim_api_version: watcher API version to use :param max_retries: Maximum number of retries in case of conflict error :param retry_interval: Amount of time (in seconds) between retries in case of conflict error :param session: Keystone session to use :param os_endpoint_override: watcher API endpoint :param ignored_kwargs: all the other params that are passed. Left for backwards compatibility. They are ignored. """ os_service_type = os_service_type or 'infra-optim' os_endpoint_type = os_endpoint_type or 'publicURL' project_id = (os_project_id or os_tenant_id) project_name = (os_project_name or os_tenant_name) kwargs = { 'os_infra_optim_api_version': os_infra_optim_api_version, 'max_retries': max_retries, 'retry_interval': retry_interval, } endpoint = watcher_url or os_endpoint_override cacert = os_cacert or ca_file cert = os_cert or cert_file key = os_key or key_file if os_auth_token and endpoint: kwargs.update({ 'token': os_auth_token, 'insecure': insecure, 'ca_file': cacert, 'cert_file': cert, 'key_file': key, 'timeout': timeout, }) elif os_auth_url: auth_type = 'password' auth_kwargs = { 'auth_url': os_auth_url, 'project_id': project_id, 'project_name': project_name, 'user_domain_id': os_user_domain_id, 'user_domain_name': os_user_domain_name, 'project_domain_id': os_project_domain_id, 'project_domain_name': os_project_domain_name, } if os_username and os_password: auth_kwargs.update({ 'username': os_username, 'password': os_password, }) elif os_auth_token: auth_type = 'token' auth_kwargs.update({ 'token': os_auth_token, }) # Create new session only if it was not passed in if not session: loader = kaloading.get_plugin_loader(auth_type) auth_plugin = loader.load_from_options(**auth_kwargs) # Let keystoneauth do the necessary parameter conversions session = kaloading.session.Session().load_from_options( auth=auth_plugin, insecure=insecure, cacert=cacert, cert=cert, key=key, timeout=timeout, ) exception_msg = _('Must provide Keystone credentials or user-defined ' 'endpoint and token') if not endpoint: if session: try: # Pass the endpoint, it will be used to get hostname # and port that will be used for API version caching. It will # be also set as endpoint_override. endpoint = session.get_endpoint( service_type=os_service_type, interface=os_endpoint_type, region_name=os_region_name ) except Exception as e: raise exceptions.AmbiguousAuthSystem( exception_msg + _(', error was: %s') % e) else: # Neither session, nor valid auth parameters provided raise exceptions.AmbiguousAuthSystem(exception_msg) # Always pass the session kwargs['session'] = session return Client(api_version, endpoint, **kwargs) def _get_client_class_and_version(version): if not isinstance(version, api_versioning.APIVersion): version = api_versioning.get_api_version(version) else: api_versioning.check_major_version(version) if version.is_latest(): raise exceptions.UnsupportedVersion( _("The version should be explicit, not latest.")) return version, importutils.import_class( "watcherclient.v%s.client.Client" % version.ver_major) def Client(version, *args, **kwargs): """Initialize client object based on given version. HOW-TO: The simplest way to create a client instance is initialization with your credentials:: >>> from watcherclient import client >>> watcher = client.Client(VERSION, USERNAME, PASSWORD, ... PROJECT_ID, AUTH_URL) Here ``VERSION`` can be a string or ``watcherclient.api_versions.APIVersion`` obj. If you prefer string value, you can use ``1`` or ``1.X`` (where X is a microversion). Alternatively, you can create a client instance using the keystoneauth session API. See "The watcherclient Python API" page at python-watcherclient's doc. """ api_version, client_class = _get_client_class_and_version(version) kw_api = kwargs.get('os_infra_optim_api_version') endpoint = kwargs.get('endpoint') # If both os_infra_optim_api_version and endpoint are not provided, get # API version from arg. if not kw_api and not endpoint: kwargs['os_infra_optim_api_version'] = api_version.get_string() return client_class(*args, **kwargs) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1740768124.847024 python_watcherclient-4.8.0/watcherclient/common/0000775000175000017500000000000000000000000022127 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/common/__init__.py0000664000175000017500000000000000000000000024226 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/common/api_versioning.py0000664000175000017500000002066000000000000025521 0ustar00zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging import os import pkgutil import re from oslo_utils import strutils from watcherclient._i18n import _ from watcherclient import exceptions LOG = logging.getLogger(__name__) if not LOG.handlers: LOG.addHandler(logging.StreamHandler()) MINOR_1_START_END_TIMING = '1.1' MINOR_2_FORCE_AUDIT = '1.2' HEADER_NAME = "OpenStack-API-Version" # key is a deprecated version and value is an alternative version. DEPRECATED_VERSIONS = {} _type_error_msg = _("'%(other)s' should be an instance of '%(cls)s'") def allow_start_end_audit_time(requested_version): """Check if we should support optional start/end attributes for Audit. Version 1.1 of the API added support for start and end time of continuous audits. """ return (APIVersion(requested_version) >= APIVersion(MINOR_1_START_END_TIMING)) def launch_audit_forced(requested_version): """Check if we should support force option for Audit. Version 1.2 of the API added support for force option. """ return (APIVersion(requested_version) >= APIVersion(MINOR_2_FORCE_AUDIT)) class APIVersion(object): """This class represents an API Version Request. This class provides convenience methods for manipulation and comparison of version numbers that we need to do to implement microversions. """ def __init__(self, version_str=None): """Create an API version object. :param version_str: String representation of APIVersionRequest. Correct format is 'X.Y', where 'X' and 'Y' are int values. None value should be used to create Null APIVersionRequest, which is equal to 0.0 """ 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|latest)$", version_str) if match: self.ver_major = int(match.group(1)) if match.group(2) == "latest": # NOTE(andreykurilin): Infinity allows to easily determine # latest version and doesn't require any additional checks # in comparison methods. self.ver_minor = float("inf") else: 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.""" if self.is_latest(): return "Latest API Version Major: %s" % self.ver_major return ("API Version Major: %s, Minor: %s" % (self.ver_major, self.ver_minor)) def __repr__(self): if self.is_null(): return "" else: return "" % self.get_string() def is_null(self): return self.ver_major == 0 and self.ver_minor == 0 def is_latest(self): return self.ver_minor == float("inf") def __lt__(self, other): if not isinstance(other, APIVersion): raise TypeError(_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(_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(_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 matches(self, min_version, max_version): """Matches the version object. 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): """Version string representation. Converts object to string representation which if used to create an APIVersion object results in the same version. """ if self.is_null(): raise ValueError( _("Null APIVersion cannot be converted to string.")) elif self.is_latest(): return "%s.%s" % (self.ver_major, "latest") return "%s.%s" % (self.ver_major, self.ver_minor) def get_available_major_versions(): # NOTE(andreykurilin): available clients version should not be # hardcoded, so let's discover them. matcher = re.compile(r"v[0-9]*$") submodules = pkgutil.iter_modules( [os.path.dirname(os.path.dirname(__file__))]) available_versions = [name[1:] for loader, name, ispkg in submodules if matcher.search(name)] return available_versions def check_major_version(api_version): """Checks major part of ``APIVersion`` obj is supported. :raises watcherclient.exceptions.UnsupportedVersion: if major part is not supported """ available_versions = get_available_major_versions() if (not api_version.is_null() and str(api_version.ver_major) not in available_versions): if len(available_versions) == 1: msg = _("Invalid client version '%(version)s'. " "Major part should be '%(major)s'") % { "version": api_version.get_string(), "major": available_versions[0]} else: msg = _("Invalid client version '%(version)s'. " "Major part must be one of: '%(major)s'") % { "version": api_version.get_string(), "major": ", ".join(available_versions)} raise exceptions.UnsupportedVersion(msg) def get_api_version(version_string): """Returns checked APIVersion object""" version_string = str(version_string) if version_string in DEPRECATED_VERSIONS: LOG.warning( "Version %(deprecated_version)s is deprecated, using " "alternative version %(alternative)s instead.", {"deprecated_version": version_string, "alternative": DEPRECATED_VERSIONS[version_string]}) version_string = DEPRECATED_VERSIONS[version_string] if strutils.is_int_like(version_string): version_string = "%s.0" % version_string api_version = APIVersion(version_string) check_major_version(api_version) return api_version ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1740768124.847024 python_watcherclient-4.8.0/watcherclient/common/apiclient/0000775000175000017500000000000000000000000024077 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/common/apiclient/__init__.py0000664000175000017500000000000000000000000026176 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/common/apiclient/base.py0000664000175000017500000004206200000000000025367 0ustar00zuulzuul00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2012 Grid Dynamics # Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Base utilities to build API operation managers and objects on top of. """ ######################################################################## # # THIS MODULE IS DEPRECATED # # Please refer to # https://etherpad.openstack.org/p/kilo-watcherclient-library-proposals for # the discussion leading to this deprecation. # # We recommend checking out the python-openstacksdk project # (https://launchpad.net/python-openstacksdk) instead. # ######################################################################## # E1102: %s is not callable # pylint: disable=E1102 import abc import copy from urllib import parse from oslo_utils import strutils from watcherclient._i18n import _ from watcherclient.common.apiclient import exceptions def getid(obj): """Return id if argument is a Resource. Abstracts the common pattern of allowing both an object or an object's ID (UUID) as a parameter when dealing with relationships. """ try: if obj.uuid: return obj.uuid except AttributeError: pass try: return obj.id except AttributeError: return obj # TODO(aababilov): call run_hooks() in HookableMixin's child classes class HookableMixin(object): """Mixin so classes can register and run hooks.""" _hooks_map = {} @classmethod def add_hook(cls, hook_type, hook_func): """Add a new hook of specified type. :param cls: class that registers hooks :param hook_type: hook type, e.g., '__pre_parse_args__' :param hook_func: hook function """ if hook_type not in cls._hooks_map: cls._hooks_map[hook_type] = [] cls._hooks_map[hook_type].append(hook_func) @classmethod def run_hooks(cls, hook_type, *args, **kwargs): """Run all hooks of specified type. :param cls: class that registers hooks :param hook_type: hook type, e.g., '__pre_parse_args__' :param args: args to be passed to every hook function :param kwargs: kwargs to be passed to every hook function """ hook_funcs = cls._hooks_map.get(hook_type) or [] for hook_func in hook_funcs: hook_func(*args, **kwargs) class BaseManager(HookableMixin): """Basic manager type providing common operations. Managers interact with a particular type of API (servers, flavors, images, etc.) and provide CRUD operations for them. """ resource_class = None def __init__(self, client): """Initializes BaseManager with `client`. :param client: instance of BaseClient descendant for HTTP requests """ super(BaseManager, self).__init__() self.client = client def _list(self, url, response_key=None, obj_class=None, json=None): """List the collection. :param url: a partial URL, e.g., '/servers' :param response_key: the key to be looked up in response dictionary, e.g., 'servers'. If response_key is None - all response body will be used. :param obj_class: class for constructing the returned objects (self.resource_class will be used by default) :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) """ if json: body = self.client.post(url, json=json).json() else: body = self.client.get(url).json() if obj_class is None: obj_class = self.resource_class data = body[response_key] if response_key is not None else body # NOTE(ja): keystone returns values as list as {'values': [ ... ]} # unlike other services which just return the list... try: data = data['values'] except (KeyError, TypeError): pass return [obj_class(self, res, loaded=True) for res in data if res] def _get(self, url, response_key=None): """Get an object from collection. :param url: a partial URL, e.g., '/servers' :param response_key: the key to be looked up in response dictionary, e.g., 'server'. If response_key is None - all response body will be used. """ body = self.client.get(url).json() data = body[response_key] if response_key is not None else body return self.resource_class(self, data, loaded=True) def _head(self, url): """Retrieve request headers for an object. :param url: a partial URL, e.g., '/servers' """ resp = self.client.head(url) return resp.status_code == 204 def _post(self, url, json, response_key=None, return_raw=False): """Create an object. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'server'. If response_key is None - all response body will be used. :param return_raw: flag to force returning raw JSON instead of Python object of self.resource_class """ body = self.client.post(url, json=json).json() data = body[response_key] if response_key is not None else body if return_raw: return data return self.resource_class(self, data) def _put(self, url, json=None, response_key=None): """Update an object with PUT method. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers'. If response_key is None - all response body will be used. """ resp = self.client.put(url, json=json) # PUT requests may not return a body if resp.content: body = resp.json() if response_key is not None: return self.resource_class(self, body[response_key]) else: return self.resource_class(self, body) def _patch(self, url, json=None, response_key=None): """Update an object with PATCH method. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers'. If response_key is None - all response body will be used. """ body = self.client.patch(url, json=json).json() if response_key is not None: return self.resource_class(self, body[response_key]) else: return self.resource_class(self, body) def _delete(self, url): """Delete an object. :param url: a partial URL, e.g., '/servers/my-server' """ return self.client.delete(url) class ManagerWithFind(BaseManager, metaclass=abc.ABCMeta): """Manager with additional `find()`/`findall()` methods.""" @abc.abstractmethod def list(self): pass def find(self, **kwargs): """Find a single item with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ matches = self.findall(**kwargs) num_matches = len(matches) if num_matches == 0: msg = _("No %(name)s matching %(args)s.") % { 'name': self.resource_class.__name__, 'args': kwargs } raise exceptions.NotFound(msg) elif num_matches > 1: raise exceptions.NoUniqueMatch() else: return matches[0] def findall(self, **kwargs): """Find all items with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ found = [] searches = kwargs.items() for obj in self.list(): try: if all(getattr(obj, attr) == value for (attr, value) in searches): found.append(obj) except AttributeError: continue return found class CrudManager(BaseManager): """Base manager class for manipulating entities. Children of this class are expected to define a `collection_key` and `key`. - `collection_key`: Usually a plural noun by convention (e.g. `entities`); used to refer collections in both URL's (e.g. `/v3/entities`) and JSON objects containing a list of member resources (e.g. `{'entities': [{}, {}, {}]}`). - `key`: Usually a singular noun by convention (e.g. `entity`); used to refer to an individual member of the collection. """ collection_key = None key = None def build_url(self, base_url=None, **kwargs): """Builds a resource URL for the given kwargs. Given an example collection where `collection_key = 'entities'` and `key = 'entity'`, the following URL's could be generated. By default, the URL will represent a collection of entities, e.g.:: /entities If kwargs contains an `entity_id`, then the URL will represent a specific member, e.g.:: /entities/{entity_id} :param base_url: if provided, the generated URL will be appended to it """ url = base_url if base_url is not None else '' url += '/%s' % self.collection_key # do we have a specific entity? entity_id = kwargs.get('%s_id' % self.key) if entity_id is not None: url += '/%s' % entity_id return url def _filter_kwargs(self, kwargs): """Drop null values and handle ids.""" for key, ref in kwargs.copy().items(): if ref is None: kwargs.pop(key) else: if isinstance(ref, Resource): kwargs.pop(key) kwargs['%s_id' % key] = getid(ref) return kwargs def create(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._post( self.build_url(**kwargs), {self.key: kwargs}, self.key) def get(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._get( self.build_url(**kwargs), self.key) def head(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._head(self.build_url(**kwargs)) def list(self, base_url=None, **kwargs): """List the collection. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) return self._list( '%(base_url)s%(query)s' % { 'base_url': self.build_url(base_url=base_url, **kwargs), 'query': '?%s' % parse.urlencode(kwargs) if kwargs else '', }, self.collection_key) def put(self, base_url=None, **kwargs): """Update an element. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) return self._put(self.build_url(base_url=base_url, **kwargs)) def update(self, **kwargs): kwargs = self._filter_kwargs(kwargs) params = kwargs.copy() params.pop('%s_id' % self.key) return self._patch( self.build_url(**kwargs), {self.key: params}, self.key) def delete(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._delete( self.build_url(**kwargs)) def find(self, base_url=None, **kwargs): """Find a single item with attributes matching ``**kwargs``. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) rl = self._list( '%(base_url)s%(query)s' % { 'base_url': self.build_url(base_url=base_url, **kwargs), 'query': '?%s' % parse.urlencode(kwargs) if kwargs else '', }, self.collection_key) num = len(rl) if num == 0: msg = _("No %(name)s matching %(args)s.") % { 'name': self.resource_class.__name__, 'args': kwargs } raise exceptions.NotFound(404, msg) elif num > 1: raise exceptions.NoUniqueMatch else: return rl[0] class Extension(HookableMixin): """Extension descriptor.""" SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__') manager_class = None def __init__(self, name, module): super(Extension, self).__init__() self.name = name self.module = module self._parse_extension_module() def _parse_extension_module(self): self.manager_class = None for attr_name, attr_value in self.module.__dict__.items(): if attr_name in self.SUPPORTED_HOOKS: self.add_hook(attr_name, attr_value) else: try: if issubclass(attr_value, BaseManager): self.manager_class = attr_value except TypeError: pass def __repr__(self): return "" % self.name class Resource(object): """Base class for OpenStack resources (tenant, user, etc.). This is pretty much just a bag for attributes. """ HUMAN_ID = False NAME_ATTR = 'name' def __init__(self, manager, info, loaded=False): """Populate and bind to a manager. :param manager: BaseManager object :param info: dictionary representing resource attributes :param loaded: prevent lazy-loading if set to True """ self.manager = manager self._info = info self._add_details(info) self._loaded = loaded def __repr__(self): reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and k != 'manager') info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) return "<%s %s>" % (self.__class__.__name__, info) @property def human_id(self): """Human-readable ID which can be used for bash completion.""" if self.HUMAN_ID: name = getattr(self, self.NAME_ATTR, None) if name is not None: return strutils.to_slug(name) return None def _add_details(self, info): for (k, v) in info.items(): try: setattr(self, k, v) self._info[k] = v except AttributeError: # In this case we already defined the attribute on the class pass def __getattr__(self, k): if k not in self.__dict__: # NOTE(bcwaldon): disallow lazy-loading if already loaded once if not self.is_loaded(): self.get() return self.__getattr__(k) raise AttributeError(k) else: return self.__dict__[k] def get(self): """Support for lazy loading details. Some clients, such as watcherclient 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) self._add_details( {'x_request_id': self.manager.client.last_request_id}) 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.__eq__(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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/common/apiclient/exceptions.py0000664000175000017500000003077000000000000026641 0ustar00zuulzuul00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 Nebula, Inc. # Copyright 2013 Alessio Ababilov # Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Exception definitions. """ ######################################################################## # # THIS MODULE IS DEPRECATED # # Please refer to # https://etherpad.openstack.org/p/kilo-watcherclient-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 watcherclient._i18n import _ class ClientException(Exception): """The base exception class for all exceptions this library raises.""" pass class ValidationError(ClientException): """Error in validation on API client side.""" pass class UnsupportedVersion(ClientException): """User is trying to use an unsupported version of the API.""" pass class CommandError(ClientException): """Error in CLI tool.""" pass class AuthorizationFailure(ClientException): """Cannot authorize API client.""" pass class ConnectionError(ClientException): """Cannot connect to API service.""" pass class ConnectionRefused(ConnectionError): """Connection refused while trying to connect to API service.""" pass class AuthPluginOptionsMissing(AuthorizationFailure): """Auth plugin misses some options.""" def __init__(self, opt_names): super(AuthPluginOptionsMissing, self).__init__( _("Authentication failed. Missing options: %s") % ", ".join(opt_names)) self.opt_names = opt_names class AuthSystemNotFound(AuthorizationFailure): """User has specified an AuthSystem that is not installed.""" def __init__(self, auth_system): super(AuthSystemNotFound, self).__init__( _("AuthSystemNotFound: %r") % auth_system) self.auth_system = auth_system class NoUniqueMatch(ClientException): """Multiple entities found instead of one.""" pass class EndpointException(ClientException): """Something is rotten in Service Catalog.""" pass class EndpointNotFound(EndpointException): """Could not find requested endpoint in Service Catalog.""" pass class AmbiguousEndpoints(EndpointException): """Found more than one matching endpoint in Service Catalog.""" def __init__(self, endpoints=None): super(AmbiguousEndpoints, self).__init__( _("AmbiguousEndpoints: %r") % endpoints) self.endpoints = endpoints class HttpError(ClientException): """The base exception class for all HTTP exceptions.""" http_status = 0 message = _("HTTP Error") def __init__(self, message=None, details=None, response=None, request_id=None, url=None, method=None, http_status=None): self.http_status = http_status or self.http_status self.message = message or self.message self.details = details self.request_id = request_id self.response = response self.url = url self.method = method formatted_string = "%s (HTTP %s)" % (self.message, self.http_status) if request_id: formatted_string += " (Request-ID: %s)" % request_id super(HttpError, self).__init__(formatted_string) class HTTPRedirection(HttpError): """HTTP Redirection.""" message = _("HTTP Redirection") class HTTPClientError(HttpError): """Client-side HTTP error. Exception for cases in which the client seems to have erred. """ message = _("HTTP Client Error") class HttpServerError(HttpError): """Server-side HTTP error. Exception for cases in which the server is aware that it has erred or is incapable of performing the request. """ message = _("HTTP Server Error") class MultipleChoices(HTTPRedirection): """HTTP 300 - Multiple Choices. Indicates multiple options for the resource that the client may follow. """ http_status = 300 message = _("Multiple Choices") class BadRequest(HTTPClientError): """HTTP 400 - Bad Request. The request cannot be fulfilled due to bad syntax. """ http_status = 400 message = _("Bad Request") class Unauthorized(HTTPClientError): """HTTP 401 - Unauthorized. Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. """ http_status = 401 message = _("Unauthorized") class PaymentRequired(HTTPClientError): """HTTP 402 - Payment Required. Reserved for future use. """ http_status = 402 message = _("Payment Required") class Forbidden(HTTPClientError): """HTTP 403 - Forbidden. The request was a valid request, but the server is refusing to respond to it. """ http_status = 403 message = _("Forbidden") class NotFound(HTTPClientError): """HTTP 404 - Not Found. The requested resource could not be found but may be available again in the future. """ http_status = 404 message = _("Not Found") class MethodNotAllowed(HTTPClientError): """HTTP 405 - Method Not Allowed. A request was made of a resource using a request method not supported by that resource. """ http_status = 405 message = _("Method Not Allowed") class NotAcceptable(HTTPClientError): """HTTP 406 - Not Acceptable. The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request. """ http_status = 406 message = _("Not Acceptable") class ProxyAuthenticationRequired(HTTPClientError): """HTTP 407 - Proxy Authentication Required. The client must first authenticate itself with the proxy. """ http_status = 407 message = _("Proxy Authentication Required") class RequestTimeout(HTTPClientError): """HTTP 408 - Request Timeout. The server timed out waiting for the request. """ http_status = 408 message = _("Request Timeout") class Conflict(HTTPClientError): """HTTP 409 - Conflict. Indicates that the request could not be processed because of conflict in the request, such as an edit conflict. """ http_status = 409 message = _("Conflict") class Gone(HTTPClientError): """HTTP 410 - Gone. Indicates that the resource requested is no longer available and will not be available again. """ http_status = 410 message = _("Gone") class LengthRequired(HTTPClientError): """HTTP 411 - Length Required. The request did not specify the length of its content, which is required by the requested resource. """ http_status = 411 message = _("Length Required") class PreconditionFailed(HTTPClientError): """HTTP 412 - Precondition Failed. The server does not meet one of the preconditions that the requester put on the request. """ http_status = 412 message = _("Precondition Failed") class RequestEntityTooLarge(HTTPClientError): """HTTP 413 - Request Entity Too Large. The request is larger than the server is willing or able to process. """ http_status = 413 message = _("Request Entity Too Large") def __init__(self, *args, **kwargs): try: self.retry_after = int(kwargs.pop('retry_after')) except (KeyError, ValueError): self.retry_after = 0 super(RequestEntityTooLarge, self).__init__(*args, **kwargs) class RequestUriTooLong(HTTPClientError): """HTTP 414 - Request-URI Too Long. The URI provided was too long for the server to process. """ http_status = 414 message = _("Request-URI Too Long") class UnsupportedMediaType(HTTPClientError): """HTTP 415 - Unsupported Media Type. The request entity has a media type which the server or resource does not support. """ http_status = 415 message = _("Unsupported Media Type") class RequestedRangeNotSatisfiable(HTTPClientError): """HTTP 416 - Requested Range Not Satisfiable. The client has asked for a portion of the file, but the server cannot supply that portion. """ http_status = 416 message = _("Requested Range Not Satisfiable") class ExpectationFailed(HTTPClientError): """HTTP 417 - Expectation Failed. The server cannot meet the requirements of the Expect request-header field. """ http_status = 417 message = _("Expectation Failed") class UnprocessableEntity(HTTPClientError): """HTTP 422 - Unprocessable Entity. The request was well-formed but was unable to be followed due to semantic errors. """ http_status = 422 message = _("Unprocessable Entity") class InternalServerError(HttpServerError): """HTTP 500 - Internal Server Error. A generic error message, given when no more specific message is suitable. """ http_status = 500 message = _("Internal Server Error") # NotImplemented is a python keyword. class HttpNotImplemented(HttpServerError): """HTTP 501 - Not Implemented. The server either does not recognize the request method, or it lacks the ability to fulfill the request. """ http_status = 501 message = _("Not Implemented") class BadGateway(HttpServerError): """HTTP 502 - Bad Gateway. The server was acting as a gateway or proxy and received an invalid response from the upstream server. """ http_status = 502 message = _("Bad Gateway") class ServiceUnavailable(HttpServerError): """HTTP 503 - Service Unavailable. The server is currently unavailable. """ http_status = 503 message = _("Service Unavailable") class GatewayTimeout(HttpServerError): """HTTP 504 - Gateway Timeout. The server was acting as a gateway or proxy and did not receive a timely response from the upstream server. """ http_status = 504 message = _("Gateway Timeout") class HttpVersionNotSupported(HttpServerError): """HTTP 505 - HttpVersion Not Supported. The server does not support the HTTP protocol version used in the request. """ http_status = 505 message = _("HTTP Version Not Supported") # _code_map contains all the classes that have http_status attribute. _code_map = dict( (getattr(obj, 'http_status', None), obj) for name, obj in vars(sys.modules[__name__]).items() if inspect.isclass(obj) and getattr(obj, 'http_status', False) ) def from_response(response, method, url): """Returns an instance of :class:`HttpError` or subclass based on response. :param response: instance of `requests.Response` class :param method: HTTP method used for request :param url: URL used for request """ req_id = response.headers.get("x-openstack-request-id") kwargs = { "http_status": response.status_code, "response": response, "method": method, "url": url, "request_id": req_id, } if "retry-after" in response.headers: kwargs["retry_after"] = response.headers["retry-after"] content_type = response.headers.get("Content-Type", "") if content_type.startswith("application/json"): try: body = response.json() except ValueError: pass else: if isinstance(body, dict): error = body.get(list(body)[0]) if isinstance(error, dict): kwargs["message"] = (error.get("message") or error.get("faultstring")) kwargs["details"] = (error.get("details") or str(body)) elif content_type.startswith("text/"): kwargs["details"] = response.text try: cls = _code_map[response.status_code] except KeyError: if 500 <= response.status_code < 600: cls = HttpServerError elif 400 <= response.status_code < 500: cls = HTTPClientError else: cls = HttpError return cls(**kwargs) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/common/base.py0000664000175000017500000001121400000000000023412 0ustar00zuulzuul00000000000000# # Copyright 2012 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 copy from urllib import parse as urlparse from watcherclient.common.apiclient import base def getid(obj): """Wrapper to get object's ID. Abstracts the common pattern of allowing both an object or an object's ID (UUID) as a parameter when dealing with relationships. """ return getattr(obj, 'id', obj) class Manager(object): """Provides CRUD operations with a particular API.""" resource_class = None def __init__(self, api): self.api = api def _create(self, url, body): resp, body = self.api.json_request('POST', url, body=body) if body: return self.resource_class(self, body) def _format_body_data(self, body, response_key): if response_key: try: data = body[response_key] except KeyError: return [] else: data = body if not isinstance(data, list): data = [data] return data def _list_pagination(self, url, response_key=None, obj_class=None, limit=None): """Retrieve a list of items. The Watcher API is configured to return a maximum number of items per request, (see Watcher's api.max_limit option). This iterates over the 'next' link (pagination) in the responses, to get the number of items specified by 'limit'. If 'limit' is None this function will continue pagination until there are no more values to be returned. :param url: a partial URL, e.g. '/nodes' :param response_key: the key to be looked up in response dictionary, e.g. 'nodes' :param obj_class: class for constructing the returned objects. :param limit: maximum number of items to return. If None returns everything. """ if obj_class is None: obj_class = self.resource_class if limit is not None: limit = int(limit) object_list = [] object_count = 0 limit_reached = False while url: resp, body = self.api.json_request('GET', url) data = self._format_body_data(body, response_key) for obj in data: object_list.append(obj_class(self, obj, loaded=True)) object_count += 1 if limit and object_count >= limit: # break the for loop limit_reached = True break # break the while loop and return if limit_reached: break url = body.get('next') if url: # NOTE(lucasagomes): We need to edit the URL to remove # the scheme and netloc url_parts = list(urlparse.urlparse(url)) url_parts[0] = url_parts[1] = '' url = urlparse.urlunparse(url_parts) return object_list def _list(self, url, response_key=None, obj_class=None, body=None): resp, body = self.api.json_request('GET', url) if obj_class is None: obj_class = self.resource_class data = self._format_body_data(body, response_key) return [obj_class(self, res, loaded=True) for res in data if res] def _update(self, url, body, method='PATCH', response_key=None): resp, body = self.api.json_request(method, url, body=body) # PATCH/PUT requests may not return a body if body: return self.resource_class(self, body) def _delete(self, url): self.api.raw_request('DELETE', url) def _start(self, url, body=None, method='POST'): resp, body = self.api.json_request(method, url, body={}) if body: return self.resource_class(self, body) class Resource(base.Resource): """Represents a particular instance of an object (tenant, user, etc). This is pretty much just a bag for attributes. """ def to_dict(self): return copy.deepcopy(self._info) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/common/command.py0000664000175000017500000000272600000000000024126 0ustar00zuulzuul00000000000000# Copyright 2016 NEC Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import logging from cliff import command from cliff import lister from cliff import show class CommandMeta(abc.ABCMeta): def __new__(mcs, name, bases, cls_dict): if 'log' not in cls_dict: cls_dict['log'] = logging.getLogger( cls_dict['__module__'] + '.' + name) return super(CommandMeta, mcs).__new__(mcs, name, bases, cls_dict) class Command(command.Command, metaclass=CommandMeta): def run(self, parsed_args): self.log.debug('run(%s)', parsed_args) return super(Command, self).run(parsed_args) class Lister(Command, lister.Lister): pass class ShowOne(Command, show.ShowOne): def get_parser(self, prog_name, formatter_class=None): parser = super(ShowOne, self).get_parser(prog_name) if formatter_class: parser.formatter_class = formatter_class return parser ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/common/httpclient.py0000664000175000017500000006177000000000000024672 0ustar00zuulzuul00000000000000# Copyright 2012 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES 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 functools import hashlib import http.client import io import logging import os import re import socket import ssl import textwrap import time from urllib import parse as urlparse from keystoneauth1 import adapter from keystoneauth1 import exceptions as kexceptions from oslo_serialization import jsonutils from oslo_utils import strutils import requests from watcherclient._i18n import _ from watcherclient.common import api_versioning from watcherclient import exceptions # Record the latest version that this client was tested with. DEFAULT_VER = '1.latest' # Minor version 4 for adding webhook API LAST_KNOWN_API_VERSION = 4 LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION) LOG = logging.getLogger(__name__) USER_AGENT = 'python-watcherclient' CHUNKSIZE = 1024 * 64 # 64kB API_VERSION = '/v1' API_VERSION_SELECTED_STATES = ('user', 'negotiated', 'cached', 'default') DEFAULT_MAX_RETRIES = 5 DEFAULT_RETRY_INTERVAL = 2 SENSITIVE_HEADERS = ('X-Auth-Token',) SUPPORTED_ENDPOINT_SCHEME = ('http', 'https') def _trim_endpoint_api_version(url): """Trim API version and trailing slash from endpoint.""" return re.sub(f'{API_VERSION}$', '', url.rstrip('/')) def _extract_error_json(body): """Return error_message from the HTTP response body.""" error_json = {} try: body_json = jsonutils.loads(body) if 'error_message' in body_json: raw_msg = body_json['error_message'] error_json = jsonutils.loads(raw_msg) except ValueError: pass return error_json def get_server(endpoint): """Extract and return the server & port that we're connecting to.""" if endpoint is None: return None, None parts = urlparse.urlparse(endpoint) return parts.hostname, str(parts.port) class VersionNegotiationMixin(object): def negotiate_version(self, conn, resp): """Negotiate the server version Assumption: Called after receiving a 406 error when doing a request. param conn: A connection object param resp: The response object from http request """ if self.api_version_select_state not in API_VERSION_SELECTED_STATES: raise RuntimeError( _('Error: self.api_version_select_state should be one of the ' 'values in: "%(valid)s" but had the value: "%(value)s"') % {'valid': ', '.join(API_VERSION_SELECTED_STATES), 'value': self.api_version_select_state}) min_ver, max_ver = self._parse_version_headers(resp) # If the user requested an explicit version or we have negotiated a # version and still failing then error now. The server could # support the version requested but the requested operation may not # be supported by the requested version. if self.api_version_select_state == 'user': raise exceptions.UnsupportedVersion(textwrap.fill( _("Requested API version %(req)s is not supported by the " "server or the requested operation is not supported by the " "requested version. Supported version range is %(min)s to " "%(max)s") % {'req': self.os_infra_optim_api_version, 'min': min_ver, 'max': max_ver})) if self.api_version_select_state == 'negotiated': raise exceptions.UnsupportedVersion(textwrap.fill( _("No API version was specified and the requested operation " "was not supported by the client's negotiated API version " "%(req)s. Supported version range is: %(min)s to %(max)s") % {'req': self.os_infra_optim_api_version, 'min': min_ver, 'max': max_ver})) negotiated_ver = api_versioning.APIVersion( self.os_infra_optim_api_version) min_ver = api_versioning.APIVersion(min_ver) max_ver = api_versioning.APIVersion(max_ver) if negotiated_ver > max_ver: negotiated_ver = max_ver if negotiated_ver < min_ver: negotiated_ver = min_ver # server handles microversions, but doesn't support # the requested version, so try a negotiated version self.api_version_select_state = 'negotiated' self.os_infra_optim_api_version = negotiated_ver.get_string() LOG.debug('Negotiated API version is %s', negotiated_ver.get_string()) return negotiated_ver def _generic_parse_version_headers(self, accessor_func): min_ver = accessor_func('OpenStack-API-Minimum-Version', None) max_ver = accessor_func('OpenStack-API-Maximum-Version', None) return min_ver, max_ver def _parse_version_headers(self, accessor_func): # NOTE(jlvillal): Declared for unit testing purposes raise NotImplementedError() def _make_simple_request(self, conn, method, url): # NOTE(jlvillal): Declared for unit testing purposes raise NotImplementedError() _RETRY_EXCEPTIONS = (exceptions.ServiceUnavailable, exceptions.ConnectionRefused, kexceptions.RetriableConnectionFailure) def with_retries(func): """Wrapper for _http_request adding support for retries.""" @functools.wraps(func) def wrapper(self, url, method, **kwargs): if self.conflict_max_retries is None: self.conflict_max_retries = DEFAULT_MAX_RETRIES if self.conflict_retry_interval is None: self.conflict_retry_interval = DEFAULT_RETRY_INTERVAL num_attempts = self.conflict_max_retries + 1 for attempt in range(1, num_attempts + 1): try: return func(self, url, method, **kwargs) except _RETRY_EXCEPTIONS as error: msg = ("Error contacting Watcher server: %(error)s. " "Attempt %(attempt)d of %(total)d" % {'attempt': attempt, 'total': num_attempts, 'error': error}) if attempt == num_attempts: LOG.error(msg) raise else: LOG.debug(msg) time.sleep(self.conflict_retry_interval) return wrapper class HTTPClient(VersionNegotiationMixin): def __init__(self, endpoint, **kwargs): self.endpoint = endpoint self.endpoint_trimmed = _trim_endpoint_api_version(endpoint) self.auth_token = kwargs.get('token') self.auth_ref = kwargs.get('auth_ref') self.os_infra_optim_api_version = kwargs.get( 'os_infra_optim_api_version', DEFAULT_VER) self.api_version_select_state = kwargs.get( 'api_version_select_state', 'default') self.conflict_max_retries = kwargs.pop('max_retries', DEFAULT_MAX_RETRIES) self.conflict_retry_interval = kwargs.pop('retry_interval', DEFAULT_RETRY_INTERVAL) self.session = requests.Session() parts = urlparse.urlparse(endpoint) if parts.scheme not in SUPPORTED_ENDPOINT_SCHEME: msg = _('Unsupported scheme: %s') % parts.scheme raise exceptions.EndpointException(msg) if parts.scheme == 'https': if kwargs.get('insecure') is True: self.session.verify = False elif kwargs.get('ca_file'): self.session.verify = kwargs['ca_file'] self.session.cert = (kwargs.get('cert_file'), kwargs.get('key_file')) def _process_header(self, name, value): """Redacts any sensitive header Redact a header that contains sensitive information, by returning an updated header with the sha1 hash of that value. The redacted value is prefixed by '{SHA1}' because that's the convention used within OpenStack. :returns: A tuple of (name, value) name: the safe encoding format of name value: the redacted value if name is x-auth-token, or the safe encoding format of name """ if name in SENSITIVE_HEADERS: v = value.encode('utf-8') h = hashlib.sha1(v) d = h.hexdigest() return (name, "{SHA1}%s" % d) else: return (name, value) def log_curl_request(self, method, url, kwargs): curl = ['curl -i -X %s' % method] for (key, value) in kwargs['headers'].items(): header = '-H \'%s: %s\'' % self._process_header(key, value) curl.append(header) if not self.session.verify: curl.append('-k') elif isinstance(self.session.verify, str): curl.append('--cacert %s' % self.session.verify) if self.session.cert: curl.append('--cert %s' % self.session.cert[0]) curl.append('--key %s' % self.session.cert[1]) if 'body' in kwargs: body = strutils.mask_password(kwargs['body']) curl.append('-d \'%s\'' % body) curl.append(urlparse.urljoin(self.endpoint_trimmed, url)) LOG.debug(' '.join(curl)) @staticmethod def log_http_response(resp, body=None): # NOTE(aarefiev): resp.raw is urllib3 response object, it's used # only to get 'version', response from request with 'stream = True' # should be used for raw reading. status = (resp.raw.version / 10.0, resp.status_code, resp.reason) dump = ['\nHTTP/%.1f %s %s' % status] dump.extend(['%s: %s' % (k, v) for k, v in resp.headers.items()]) dump.append('') if body: body = strutils.mask_password(body) dump.extend([body, '']) LOG.debug('\n'.join(dump)) def _make_connection_url(self, url): return '%s/%s' % (self.endpoint_trimmed.rstrip('/'), url.lstrip('/')) def _parse_version_headers(self, resp): return self._generic_parse_version_headers(resp.headers.get) def _make_simple_request(self, conn, method, url): return conn.request(method, self._make_connection_url(url)) @with_retries def _http_request(self, url, method, **kwargs): """Send an http request with the specified characteristics. Wrapper around request.Session.request to handle tasks such as setting headers and error handling. """ # Copy the kwargs so we can reuse the original in case of redirects kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {})) kwargs['headers'].setdefault('User-Agent', USER_AGENT) if self.os_infra_optim_api_version: api_version = api_versioning.get_api_version( self.os_infra_optim_api_version) if api_version.is_latest(): api_version = api_versioning.get_api_version( LATEST_VERSION) kwargs['headers'].setdefault( 'OpenStack-API-Version', ' '.join(['infra-optim', api_version.get_string()])) if self.auth_token: kwargs['headers'].setdefault('X-Auth-Token', self.auth_token) self.log_curl_request(method, url, kwargs) # NOTE(aarefiev): This is for backwards compatibility, request # expected body in 'data' field, previously we used httplib, # which expected 'body' field. body = kwargs.pop('body', None) if body: kwargs['data'] = body conn_url = self._make_connection_url(url) try: resp = self.session.request(method, conn_url, **kwargs) # TODO(deva): implement graceful client downgrade when connecting # to servers that did not support microversions. Details here: # http://specs.openstack.org/openstack/watcher-specs/specs/kilo/api-microversions.html#use-case-3b-new-client-communicating-with-a-old-watcher-user-specified # noqa if resp.status_code == http.client.NOT_ACCEPTABLE: negotiated_ver = self.negotiate_version(self.session, resp) kwargs['headers']['OpenStack-API-Version'] = ( ' '.join(['infra-optim', negotiated_ver])) return self._http_request(url, method, **kwargs) except requests.exceptions.RequestException as e: message = (_("Error has occurred while handling " "request for %(url)s: %(e)s") % dict(url=conn_url, e=e)) # NOTE(aarefiev): not valid request(invalid url, missing schema, # and so on), retrying is not needed. if isinstance(e, ValueError): raise exceptions.ValidationError(message) raise exceptions.ConnectionRefused(message) body_iter = resp.iter_content(chunk_size=CHUNKSIZE) # Read body into string if it isn't obviously image data body_str = None if resp.headers.get('Content-Type') != 'application/octet-stream': # decoding byte to string is necessary for Python 3 compatibility # this issues has not been found with Python 3 unit tests # because the test creates a fake http response of type str # the if statement satisfies test (str) and real (bytes) behavior body_list = [ chunk.decode("utf-8") if isinstance(chunk, bytes) else chunk for chunk in body_iter ] body_str = ''.join(body_list) self.log_http_response(resp, body_str) body_iter = io.StringIO(body_str) else: self.log_http_response(resp) if resp.status_code >= http.client.BAD_REQUEST: error_json = _extract_error_json(body_str) raise exceptions.from_response( resp, error_json.get('faultstring'), error_json.get('debuginfo'), method, url) elif resp.status_code in (http.client.MOVED_PERMANENTLY, http.client.FOUND, http.client.USE_PROXY): # Redirected. Reissue the request to the new location. return self._http_request(resp['location'], method, **kwargs) elif resp.status_code == http.client.MULTIPLE_CHOICES: raise exceptions.from_response(resp, method=method, url=url) return resp, body_iter def json_request(self, method, url, **kwargs): kwargs.setdefault('headers', {}) kwargs['headers'].setdefault('Content-Type', 'application/json') kwargs['headers'].setdefault('Accept', 'application/json') if 'body' in kwargs: kwargs['body'] = jsonutils.dumps(kwargs['body']) resp, body_iter = self._http_request(url, method, **kwargs) content_type = resp.headers.get('Content-Type') if (resp.status_code in (http.client.NO_CONTENT, http.client.RESET_CONTENT) or content_type is None): return resp, list() if 'application/json' in content_type: body = ''.join([chunk for chunk in body_iter]) try: body = jsonutils.loads(body) except ValueError: LOG.error('Could not decode response body as JSON') else: body = None return resp, body def raw_request(self, method, url, **kwargs): kwargs.setdefault('headers', {}) kwargs['headers'].setdefault('Content-Type', 'application/octet-stream') return self._http_request(url, method, **kwargs) class VerifiedHTTPSConnection(http.client.HTTPSConnection): """httplib-compatible connection using client-side SSL authentication :see http://code.activestate.com/recipes/ 577548-https-httplib-client-connection-with-certificate-v/ """ def __init__(self, host, port, key_file=None, cert_file=None, ca_file=None, timeout=None, insecure=False): super(VerifiedHTTPSConnection, self).__init__( self, host, port, key_file=key_file, cert_file=cert_file) self.key_file = key_file self.cert_file = cert_file if ca_file is not None: self.ca_file = ca_file else: self.ca_file = self.get_system_ca_file() self.timeout = timeout self.insecure = insecure def connect(self): """Connect to a host on a given (SSL) port. If ca_file is pointing somewhere, use it to check Server Certificate. """ sock = socket.create_connection((self.host, self.port), self.timeout) if self._tunnel_host: self.sock = sock self._tunnel() context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) if self.insecure is True: context.check_hostname = False context.verify_mode = ssl.CERT_NONE else: context.load_verify_locations(self.ca_file) if self.cert_file: if self.key_file: context.load_cert_chain(self.cert_file, self.key_file) else: context.load_cert_chain(self.cert_file) self.sock = context.wrap_socket(sock) @staticmethod def get_system_ca_file(): """Return path to system default CA file.""" # Standard CA file locations for Debian/Ubuntu, RedHat/Fedora, # Suse, FreeBSD/OpenBSD ca_path = ['/etc/ssl/certs/ca-certificates.crt', '/etc/pki/tls/certs/ca-bundle.crt', '/etc/ssl/ca-bundle.pem', '/etc/ssl/cert.pem'] for ca in ca_path: if os.path.exists(ca): return ca return None class SessionClient(VersionNegotiationMixin, adapter.LegacyJsonAdapter): """HTTP client based on Keystone client session.""" def __init__(self, os_infra_optim_api_version, api_version_select_state, max_retries, retry_interval, endpoint, **kwargs): self.os_infra_optim_api_version = os_infra_optim_api_version self.api_version_select_state = api_version_select_state self.conflict_max_retries = max_retries self.conflict_retry_interval = retry_interval self.endpoint = endpoint super(SessionClient, self).__init__(**kwargs) def _parse_version_headers(self, resp): return self._generic_parse_version_headers(resp.headers.get) def _make_simple_request(self, conn, method, url): # NOTE: conn is self.session for this class return conn.request(url, method, raise_exc=False) @with_retries def _http_request(self, url, method, **kwargs): kwargs.setdefault('user_agent', USER_AGENT) kwargs.setdefault('auth', self.auth) if isinstance(self.endpoint_override, str): kwargs.setdefault( 'endpoint_override', _trim_endpoint_api_version(self.endpoint_override) ) if getattr(self, 'os_infra_optim_api_version', None): api_version = api_versioning.get_api_version( self.os_infra_optim_api_version) if api_version.is_latest(): api_version = api_versioning.get_api_version( LATEST_VERSION) kwargs['headers'].setdefault( 'OpenStack-API-Version', ' '.join(['infra-optim', api_version.get_string()])) endpoint_filter = kwargs.setdefault('endpoint_filter', {}) endpoint_filter.setdefault('interface', self.interface) endpoint_filter.setdefault('service_type', self.service_type) endpoint_filter.setdefault('region_name', self.region_name) resp = self.session.request(url, method, raise_exc=False, **kwargs) if resp.status_code == http.client.NOT_ACCEPTABLE: negotiated_ver = self.negotiate_version(self.session, resp) kwargs['headers']['OpenStack-API-Version'] = ( ' '.join(['infra-optim', negotiated_ver])) return self._http_request(url, method, **kwargs) if resp.status_code >= http.client.BAD_REQUEST: error_json = _extract_error_json(resp.content) raise exceptions.from_response( resp, error_json.get('faultstring'), error_json.get('debuginfo'), method, url) elif resp.status_code in (http.client.MOVED_PERMANENTLY, http.client.FOUND, http.client.USE_PROXY): # Redirected. Reissue the request to the new location. location = resp.headers.get('location') resp = self._http_request(location, method, **kwargs) elif resp.status_code == http.client.MULTIPLE_CHOICES: raise exceptions.from_response(resp, method=method, url=url) return resp def json_request(self, method, url, **kwargs): kwargs.setdefault('headers', {}) kwargs['headers'].setdefault('Content-Type', 'application/json') kwargs['headers'].setdefault('Accept', 'application/json') if 'body' in kwargs: kwargs['data'] = jsonutils.dumps(kwargs.pop('body')) resp = self._http_request(url, method, **kwargs) body = resp.content content_type = resp.headers.get('content-type', None) status = resp.status_code if (status in (http.client.NO_CONTENT, http.client.RESET_CONTENT) or content_type is None): return resp, list() if 'application/json' in content_type: try: body = resp.json() except ValueError: LOG.error('Could not decode response body as JSON') else: body = None return resp, body def raw_request(self, method, url, **kwargs): kwargs.setdefault('headers', {}) kwargs['headers'].setdefault('Content-Type', 'application/octet-stream') return self._http_request(url, method, **kwargs) def _construct_http_client(endpoint=None, session=None, token=None, auth_ref=None, os_infra_optim_api_version=DEFAULT_VER, api_version_select_state='default', max_retries=DEFAULT_MAX_RETRIES, retry_interval=DEFAULT_RETRY_INTERVAL, timeout=600, ca_file=None, cert_file=None, key_file=None, insecure=None, **kwargs): if session: kwargs.setdefault('service_type', 'infra-optim') kwargs.setdefault('user_agent', 'python-watcherclient') kwargs.setdefault('interface', kwargs.pop('endpoint_type', None)) kwargs.setdefault('endpoint_override', endpoint) ignored = {'token': token, 'auth_ref': auth_ref, 'timeout': timeout != 600, 'ca_file': ca_file, 'cert_file': cert_file, 'key_file': key_file, 'insecure': insecure} dvars = [k for k, v in ignored.items() if v] if dvars: LOG.warning('The following arguments are ignored when using ' 'the session to construct a client: %s', ', '.join(dvars)) return SessionClient( session=session, os_infra_optim_api_version=os_infra_optim_api_version, api_version_select_state=api_version_select_state, max_retries=max_retries, retry_interval=retry_interval, endpoint=endpoint, **kwargs) else: if kwargs: LOG.warning('The following arguments are being ignored when ' 'constructing the client: %s', ', '.join(kwargs)) return HTTPClient( endpoint=endpoint, token=token, auth_ref=auth_ref, os_infra_optim_api_version=os_infra_optim_api_version, api_version_select_state=api_version_select_state, max_retries=max_retries, retry_interval=retry_interval, timeout=timeout, ca_file=ca_file, cert_file=cert_file, key_file=key_file, insecure=insecure) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/common/utils.py0000664000175000017500000001601200000000000023641 0ustar00zuulzuul00000000000000# # Copyright 2012 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import argparse import os import uuid import yaml from oslo_serialization import jsonutils from oslo_utils import importutils from watcherclient._i18n import _ from watcherclient import exceptions as exc class HelpFormatter(argparse.HelpFormatter): def start_section(self, heading): # Title-case the headings heading = '%s%s' % (heading[0].upper(), heading[1:]) super(HelpFormatter, self).start_section(heading) def define_command(subparsers, command, callback, cmd_mapper): '''Define a command in the subparsers collection. :param subparsers: subparsers collection where the command will go :param command: command name :param callback: function that will be used to process the command ''' desc = callback.__doc__ or '' help = desc.strip().split('\n')[0] arguments = getattr(callback, 'arguments', []) subparser = subparsers.add_parser(command, help=help, description=desc, add_help=False, formatter_class=HelpFormatter) subparser.add_argument('-h', '--help', action='help', help=argparse.SUPPRESS) cmd_mapper[command] = subparser for (args, kwargs) in arguments: subparser.add_argument(*args, **kwargs) subparser.set_defaults(func=callback) def define_commands_from_module(subparsers, command_module, cmd_mapper): """Add *do_* methods in a module and add as commands into a subparsers.""" for method_name in (a for a in dir(command_module) if a.startswith('do_')): # Commands should be hypen-separated instead of underscores. command = method_name[3:].replace('_', '-') callback = getattr(command_module, method_name) define_command(subparsers, command, callback, cmd_mapper) def import_versioned_module(version, submodule=None): module = 'watcherclient.v%s' % version if submodule: module = '.'.join((module, submodule)) return importutils.import_module(module) def split_and_deserialize(string, exclude_fields=[]): """Split and try to JSON deserialize a string. Gets a string with the KEY=VALUE format, split it (using '=' as the separator) and try to JSON deserialize the VALUE. :returns: A tuple of (key, value). """ try: key, value = string.split("=", 1) except ValueError: raise exc.CommandError(_('Attributes must be a list of ' 'PATH=VALUE not "%s"') % string) if key not in exclude_fields: try: value = jsonutils.loads(value) except ValueError: pass return (key, value) def args_array_to_dict(kwargs, key_to_convert): values_to_convert = kwargs.get(key_to_convert) if values_to_convert: kwargs[key_to_convert] = dict(split_and_deserialize(v) for v in values_to_convert) return kwargs def args_array_to_patch(op, attributes, exclude_fields=[]): patch = [] for attr in attributes: # Sanitize if not attr.startswith('/'): attr = '/' + attr if op in ['add', 'replace']: path, value = split_and_deserialize(attr, exclude_fields=exclude_fields) patch.append({'op': op, 'path': path, 'value': value}) elif op == "remove": # For remove only the key is needed patch.append({'op': op, 'path': attr}) else: raise exc.CommandError(_('Unknown PATCH operation: %s') % op) return patch def common_params_for_list(args, fields, field_labels): """Generate 'params' dict that is common for every 'list' command. :param args: arguments from command line. :param fields: possible fields for sorting. :param field_labels: possible field labels for sorting. :returns: a dict with params to pass to the client method. """ params = {} if args.limit is not None: if args.limit < 0: raise exc.CommandError( _('Expected non-negative --limit, got %s') % args.limit) params['limit'] = args.limit if args.sort_key is not None: # Support using both heading and field name for sort_key fields_map = dict(zip(field_labels, fields)) fields_map.update(zip(fields, fields)) try: sort_key = fields_map[args.sort_key] except KeyError: raise exc.CommandError( _("%(sort_key)s is an invalid field for sorting, " "valid values for --sort-key are: %(valid)s") % {'sort_key': args.sort_key, 'valid': list(fields_map)}) params['sort_key'] = sort_key if args.sort_dir is not None: if args.sort_dir not in ('asc', 'desc'): raise exc.CommandError( _("%s is an invalid value for sort direction, " "valid values for --sort-dir are: 'asc', 'desc'") % args.sort_dir) params['sort_dir'] = args.sort_dir marker = getattr(args, 'marker', None) if marker is not None: params['marker'] = marker params['detail'] = args.detail return params def common_filters(limit=None, sort_key=None, sort_dir=None, marker=None): """Generate common filters for any list request. :param limit: maximum number of entities to return. :param sort_key: field to use for sorting. :param sort_dir: direction of sorting: 'asc' or 'desc'. :param marker: The last actionplan UUID of the previous page. :returns: list of string filters. """ filters = [] if isinstance(limit, int) and limit > 0: filters.append('limit=%s' % limit) if sort_key is not None: filters.append('sort_key=%s' % sort_key) if sort_dir is not None: filters.append('sort_dir=%s' % sort_dir) if marker is not None: filters.append('marker=%s' % marker) return filters def is_uuid_like(val): """Returns validation of a value as a UUID. For our purposes, a UUID is a canonical form string: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa """ try: return str(uuid.UUID(val)) == val except (TypeError, ValueError, AttributeError): return False def serialize_file_to_dict(filename): filename = os.path.expanduser(filename) with open(filename, "rb") as stream: scope = yaml.safe_load(stream.read()) return scope ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/exceptions.py0000664000175000017500000001003400000000000023370 0ustar00zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from watcherclient.common.apiclient import exceptions # NOTE(akurilin): This alias is left here since v.0.1.3 to support backwards # compatibility. InvalidEndpoint = exceptions.EndpointException CommunicationError = exceptions.ConnectionRefused HTTPBadRequest = exceptions.BadRequest HTTPInternalServerError = exceptions.InternalServerError HTTPNotFound = exceptions.NotFound HTTPServiceUnavailable = exceptions.ServiceUnavailable CommandError = exceptions.CommandError """Error in CLI tool. An alias of :py:exc:`watcherclient.common.apiclient.CommandError` """ Unauthorized = exceptions.Unauthorized """HTTP 401 - Unauthorized. Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. An alias of :py:exc:`watcherclient.common.apiclient.Unauthorized` """ InternalServerError = exceptions.InternalServerError """HTTP 500 - Internal Server Error. A generic error message, given when no more specific message is suitable. An alias of :py:exc:`watcherclient.common.apiclient.InternalServerError` """ ValidationError = exceptions.ValidationError """Error in validation on API client side. A generic error message, given when no more specific message is suitable. An alias of :py:exc:`watcherclient.common.apiclient.ValidationError` """ Conflict = exceptions.Conflict ConnectionRefused = exceptions.ConnectionRefused EndpointException = exceptions.EndpointException EndpointNotFound = exceptions.EndpointNotFound ServiceUnavailable = exceptions.ServiceUnavailable class UnsupportedVersion(Exception): """Unsupported API Version Indicates that the user is trying to use an unsupported version of the API. """ pass class AmbiguousAuthSystem(exceptions.ClientException): """Could not obtain token and endpoint using provided credentials.""" pass # Alias for backwards compatibility AmbigiousAuthSystem = AmbiguousAuthSystem class InvalidAttribute(exceptions.ClientException): pass def from_response(response, message=None, traceback=None, method=None, url=None): """Return an HttpError instance based on response from httplib/requests.""" error_body = {} if message: error_body['message'] = message if traceback: error_body['details'] = traceback if hasattr(response, 'status') and not hasattr(response, 'status_code'): # NOTE(akurilin): These modifications around response object give # ability to get all necessary information in method `from_response` # from common code, which expecting response object from `requests` # library instead of object from `httplib/httplib2` library. response.status_code = response.status response.headers = { 'Content-Type': response.getheader('content-type', "")} if hasattr(response, 'status_code'): # NOTE(hongbin): This allows SessionClient to handle faultstring. response.json = lambda: {'error': error_body} if (response.headers.get('Content-Type', '').startswith('text/') and not hasattr(response, 'text')): # NOTE(clif_h): There seems to be a case in the # common.apiclient.exceptions module where if the # content-type of the response is text/* then it expects # the response to have a 'text' attribute, but that # doesn't always seem to necessarily be the case. # This is to work around that problem. response.text = '' return exceptions.from_response(response, method, url) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1740768124.847024 python_watcherclient-4.8.0/watcherclient/locale/0000775000175000017500000000000000000000000022076 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8350234 python_watcherclient-4.8.0/watcherclient/locale/fr/0000775000175000017500000000000000000000000022505 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1740768124.847024 python_watcherclient-4.8.0/watcherclient/locale/fr/LC_MESSAGES/0000775000175000017500000000000000000000000024272 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/locale/fr/LC_MESSAGES/watcherclient.po0000664000175000017500000001507500000000000027476 0ustar00zuulzuul00000000000000# French translations for python-watcherclient. # Copyright (C) 2016 ORGANIZATION # This file is distributed under the same license as the # python-watcherclient project. # FIRST AUTHOR , 2016. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: python-watcherclient 0.20.1.dev4\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-01-14 14:57+0100\n" "PO-Revision-Date: 2016-01-12 02:05+0100\n" "Last-Translator: FULL NAME \n" "Language: fr\n" "Language-Team: fr \n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.1.1\n" #: watcherclient/client.py:103 msgid "Must provide Keystone credentials or user-defined endpoint and token" msgstr "" #: watcherclient/shell.py:279 #, python-format msgid "" "Unable to determine the Keystone version to authenticate with using the " "given auth_url. Identity service may not support API version discovery. " "Please provide a versioned auth_url instead. %s" msgstr "" #: watcherclient/shell.py:344 msgid "" "Unable to determine the Keystone version to authenticate with using the " "given auth_url." msgstr "" #: watcherclient/shell.py:380 msgid "" "You must provide a username via either --os-username or via " "env[OS_USERNAME]" msgstr "" #: watcherclient/shell.py:396 msgid "" "You must provide a password via either --os-password, env[OS_PASSWORD], " "or prompted response" msgstr "" #: watcherclient/shell.py:403 msgid "" "You must provide a project name or project id via --os-project-name, " "--os-project-id, env[OS_PROJECT_ID] or env[OS_PROJECT_NAME]. You may use" " os-project and os-tenant interchangeably." msgstr "" #: watcherclient/shell.py:410 msgid "" "You must provide an auth url via either --os-auth-url or via " "env[OS_AUTH_URL]" msgstr "" #: watcherclient/shell.py:473 msgid "Invalid OpenStack Identity credentials" msgstr "" #: watcherclient/shell.py:483 #, python-format msgid "'%s' is not a valid subcommand" msgstr "" #: watcherclient/common/cliutils.py:40 #, python-format msgid "Missing arguments: %s" msgstr "" #: watcherclient/common/cliutils.py:158 #, python-format msgid "" "Field labels list %(labels)s has different number of elements than fields" " list %(fields)s" msgstr "" #: watcherclient/common/http.py:88 #, python-format msgid "Unsupported scheme: %s" msgstr "" #: watcherclient/common/http.py:162 #, python-format msgid "Error finding address for %(url)s: %(e)s" msgstr "" #: watcherclient/common/http.py:167 #, python-format msgid "Error communicating with %(endpoint)s %(e)s" msgstr "" #: watcherclient/common/http.py:181 msgid "Request returned failure status." msgstr "" #: watcherclient/common/http.py:213 watcherclient/common/http.py:337 msgid "Could not decode response body as JSON" msgstr "" #: watcherclient/common/utils.py:87 #, python-format msgid "Attributes must be a list of PATH=VALUE not \"%s\"" msgstr "" #: watcherclient/common/utils.py:120 #, python-format msgid "Unknown PATCH operation: %s" msgstr "" #: watcherclient/common/utils.py:136 #, python-format msgid "Expected non-negative --limit, got %s" msgstr "" #: watcherclient/common/utils.py:147 #, python-format msgid "" "%(sort_key)s is an invalid field for sorting, valid values for --sort-key" " are: %(valid)s" msgstr "" #: watcherclient/common/utils.py:155 #, python-format msgid "" "%s is an invalid value for sort direction, valid values for --sort-dir " "are: 'asc', 'desc'" msgstr "" #: watcherclient/common/apiclient/base.py:244 #: watcherclient/common/apiclient/base.py:401 #, python-format msgid "No %(name)s matching %(args)s." msgstr "" #: watcherclient/common/apiclient/exceptions.py:83 #, python-format msgid "Authentication failed. Missing options: %s" msgstr "" #: watcherclient/common/apiclient/exceptions.py:92 #, python-format msgid "AuthSystemNotFound: %r" msgstr "" #: watcherclient/common/apiclient/exceptions.py:115 #, python-format msgid "AmbiguousEndpoints: %r" msgstr "" #: watcherclient/common/apiclient/exceptions.py:122 msgid "HTTP Error" msgstr "" #: watcherclient/common/apiclient/exceptions.py:142 msgid "HTTP Redirection" msgstr "" #: watcherclient/common/apiclient/exceptions.py:150 msgid "HTTP Client Error" msgstr "" #: watcherclient/common/apiclient/exceptions.py:159 msgid "HTTP Server Error" msgstr "" #: watcherclient/common/apiclient/exceptions.py:169 msgid "Multiple Choices" msgstr "" #: watcherclient/common/apiclient/exceptions.py:178 msgid "Bad Request" msgstr "" #: watcherclient/common/apiclient/exceptions.py:188 msgid "Unauthorized" msgstr "" #: watcherclient/common/apiclient/exceptions.py:197 msgid "Payment Required" msgstr "" #: watcherclient/common/apiclient/exceptions.py:207 msgid "Forbidden" msgstr "" #: watcherclient/common/apiclient/exceptions.py:217 msgid "Not Found" msgstr "" #: watcherclient/common/apiclient/exceptions.py:227 msgid "Method Not Allowed" msgstr "" #: watcherclient/common/apiclient/exceptions.py:237 msgid "Not Acceptable" msgstr "" #: watcherclient/common/apiclient/exceptions.py:246 msgid "Proxy Authentication Required" msgstr "" #: watcherclient/common/apiclient/exceptions.py:255 msgid "Request Timeout" msgstr "" #: watcherclient/common/apiclient/exceptions.py:265 msgid "Conflict" msgstr "" #: watcherclient/common/apiclient/exceptions.py:275 msgid "Gone" msgstr "" #: watcherclient/common/apiclient/exceptions.py:285 msgid "Length Required" msgstr "" #: watcherclient/common/apiclient/exceptions.py:295 msgid "Precondition Failed" msgstr "" #: watcherclient/common/apiclient/exceptions.py:304 msgid "Request Entity Too Large" msgstr "" #: watcherclient/common/apiclient/exceptions.py:321 msgid "Request-URI Too Long" msgstr "" #: watcherclient/common/apiclient/exceptions.py:331 msgid "Unsupported Media Type" msgstr "" #: watcherclient/common/apiclient/exceptions.py:341 msgid "Requested Range Not Satisfiable" msgstr "" #: watcherclient/common/apiclient/exceptions.py:350 msgid "Expectation Failed" msgstr "" #: watcherclient/common/apiclient/exceptions.py:360 msgid "Unprocessable Entity" msgstr "" #: watcherclient/common/apiclient/exceptions.py:369 msgid "Internal Server Error" msgstr "" #: watcherclient/common/apiclient/exceptions.py:380 msgid "Not Implemented" msgstr "" #: watcherclient/common/apiclient/exceptions.py:390 msgid "Bad Gateway" msgstr "" #: watcherclient/common/apiclient/exceptions.py:399 msgid "Service Unavailable" msgstr "" #: watcherclient/common/apiclient/exceptions.py:409 msgid "Gateway Timeout" msgstr "" #: watcherclient/common/apiclient/exceptions.py:418 msgid "HTTP Version Not Supported" msgstr "" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/locale/watcherclient.pot0000664000175000017500000001500700000000000025461 0ustar00zuulzuul00000000000000# Translations template for python-watcherclient. # Copyright (C) 2016 ORGANIZATION # This file is distributed under the same license as the # python-watcherclient project. # FIRST AUTHOR , 2016. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: python-watcherclient 0.20.1.dev5\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-01-14 14:57+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.1.1\n" #: watcherclient/client.py:103 msgid "Must provide Keystone credentials or user-defined endpoint and token" msgstr "" #: watcherclient/shell.py:279 #, python-format msgid "" "Unable to determine the Keystone version to authenticate with using the " "given auth_url. Identity service may not support API version discovery. " "Please provide a versioned auth_url instead. %s" msgstr "" #: watcherclient/shell.py:344 msgid "" "Unable to determine the Keystone version to authenticate with using the " "given auth_url." msgstr "" #: watcherclient/shell.py:380 msgid "" "You must provide a username via either --os-username or via " "env[OS_USERNAME]" msgstr "" #: watcherclient/shell.py:396 msgid "" "You must provide a password via either --os-password, env[OS_PASSWORD], " "or prompted response" msgstr "" #: watcherclient/shell.py:403 msgid "" "You must provide a project name or project id via --os-project-name, " "--os-project-id, env[OS_PROJECT_ID] or env[OS_PROJECT_NAME]. You may use" " os-project and os-tenant interchangeably." msgstr "" #: watcherclient/shell.py:410 msgid "" "You must provide an auth url via either --os-auth-url or via " "env[OS_AUTH_URL]" msgstr "" #: watcherclient/shell.py:473 msgid "Invalid OpenStack Identity credentials" msgstr "" #: watcherclient/shell.py:483 #, python-format msgid "'%s' is not a valid subcommand" msgstr "" #: watcherclient/common/cliutils.py:40 #, python-format msgid "Missing arguments: %s" msgstr "" #: watcherclient/common/cliutils.py:158 #, python-format msgid "" "Field labels list %(labels)s has different number of elements than fields" " list %(fields)s" msgstr "" #: watcherclient/common/http.py:88 #, python-format msgid "Unsupported scheme: %s" msgstr "" #: watcherclient/common/http.py:162 #, python-format msgid "Error finding address for %(url)s: %(e)s" msgstr "" #: watcherclient/common/http.py:167 #, python-format msgid "Error communicating with %(endpoint)s %(e)s" msgstr "" #: watcherclient/common/http.py:181 msgid "Request returned failure status." msgstr "" #: watcherclient/common/http.py:213 watcherclient/common/http.py:337 msgid "Could not decode response body as JSON" msgstr "" #: watcherclient/common/utils.py:87 #, python-format msgid "Attributes must be a list of PATH=VALUE not \"%s\"" msgstr "" #: watcherclient/common/utils.py:120 #, python-format msgid "Unknown PATCH operation: %s" msgstr "" #: watcherclient/common/utils.py:136 #, python-format msgid "Expected non-negative --limit, got %s" msgstr "" #: watcherclient/common/utils.py:147 #, python-format msgid "" "%(sort_key)s is an invalid field for sorting, valid values for --sort-key" " are: %(valid)s" msgstr "" #: watcherclient/common/utils.py:155 #, python-format msgid "" "%s is an invalid value for sort direction, valid values for --sort-dir " "are: 'asc', 'desc'" msgstr "" #: watcherclient/common/apiclient/base.py:244 #: watcherclient/common/apiclient/base.py:401 #, python-format msgid "No %(name)s matching %(args)s." msgstr "" #: watcherclient/common/apiclient/exceptions.py:83 #, python-format msgid "Authentication failed. Missing options: %s" msgstr "" #: watcherclient/common/apiclient/exceptions.py:92 #, python-format msgid "AuthSystemNotFound: %r" msgstr "" #: watcherclient/common/apiclient/exceptions.py:115 #, python-format msgid "AmbiguousEndpoints: %r" msgstr "" #: watcherclient/common/apiclient/exceptions.py:122 msgid "HTTP Error" msgstr "" #: watcherclient/common/apiclient/exceptions.py:142 msgid "HTTP Redirection" msgstr "" #: watcherclient/common/apiclient/exceptions.py:150 msgid "HTTP Client Error" msgstr "" #: watcherclient/common/apiclient/exceptions.py:159 msgid "HTTP Server Error" msgstr "" #: watcherclient/common/apiclient/exceptions.py:169 msgid "Multiple Choices" msgstr "" #: watcherclient/common/apiclient/exceptions.py:178 msgid "Bad Request" msgstr "" #: watcherclient/common/apiclient/exceptions.py:188 msgid "Unauthorized" msgstr "" #: watcherclient/common/apiclient/exceptions.py:197 msgid "Payment Required" msgstr "" #: watcherclient/common/apiclient/exceptions.py:207 msgid "Forbidden" msgstr "" #: watcherclient/common/apiclient/exceptions.py:217 msgid "Not Found" msgstr "" #: watcherclient/common/apiclient/exceptions.py:227 msgid "Method Not Allowed" msgstr "" #: watcherclient/common/apiclient/exceptions.py:237 msgid "Not Acceptable" msgstr "" #: watcherclient/common/apiclient/exceptions.py:246 msgid "Proxy Authentication Required" msgstr "" #: watcherclient/common/apiclient/exceptions.py:255 msgid "Request Timeout" msgstr "" #: watcherclient/common/apiclient/exceptions.py:265 msgid "Conflict" msgstr "" #: watcherclient/common/apiclient/exceptions.py:275 msgid "Gone" msgstr "" #: watcherclient/common/apiclient/exceptions.py:285 msgid "Length Required" msgstr "" #: watcherclient/common/apiclient/exceptions.py:295 msgid "Precondition Failed" msgstr "" #: watcherclient/common/apiclient/exceptions.py:304 msgid "Request Entity Too Large" msgstr "" #: watcherclient/common/apiclient/exceptions.py:321 msgid "Request-URI Too Long" msgstr "" #: watcherclient/common/apiclient/exceptions.py:331 msgid "Unsupported Media Type" msgstr "" #: watcherclient/common/apiclient/exceptions.py:341 msgid "Requested Range Not Satisfiable" msgstr "" #: watcherclient/common/apiclient/exceptions.py:350 msgid "Expectation Failed" msgstr "" #: watcherclient/common/apiclient/exceptions.py:360 msgid "Unprocessable Entity" msgstr "" #: watcherclient/common/apiclient/exceptions.py:369 msgid "Internal Server Error" msgstr "" #: watcherclient/common/apiclient/exceptions.py:380 msgid "Not Implemented" msgstr "" #: watcherclient/common/apiclient/exceptions.py:390 msgid "Bad Gateway" msgstr "" #: watcherclient/common/apiclient/exceptions.py:399 msgid "Service Unavailable" msgstr "" #: watcherclient/common/apiclient/exceptions.py:409 msgid "Gateway Timeout" msgstr "" #: watcherclient/common/apiclient/exceptions.py:418 msgid "HTTP Version Not Supported" msgstr "" ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1740768124.847024 python_watcherclient-4.8.0/watcherclient/osc/0000775000175000017500000000000000000000000021423 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/osc/__init__.py0000664000175000017500000000000000000000000023522 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/osc/plugin.py0000664000175000017500000000576000000000000023303 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from osc_lib import utils import watcherclient from watcherclient.common import api_versioning from watcherclient.common import httpclient from watcherclient import exceptions LOG = logging.getLogger(__name__) DEFAULT_API_VERSION = httpclient.LATEST_VERSION API_VERSION_OPTION = 'os_infra_optim_api_version' API_NAME = 'infra-optim' API_VERSIONS = { '1': 'watcherclient.v1.client.Client', } def make_client(instance): """Returns an infra-optim service client.""" version = api_versioning.APIVersion(instance._api_version[API_NAME]) infraoptim_client_class = utils.get_client_class( API_NAME, version.ver_major, API_VERSIONS) LOG.debug('Instantiating infraoptim client: %s', infraoptim_client_class) client = infraoptim_client_class( os_infra_optim_api_version=instance._api_version[API_NAME], session=instance.session, region_name=instance._region_name, ) return client def build_option_parser(parser): """Hook to add global options.""" parser.add_argument('--os-infra-optim-api-version', metavar='', default=utils.env( 'OS_INFRA_OPTIM_API_VERSION', default=DEFAULT_API_VERSION), help=('Watcher API version, default=' + DEFAULT_API_VERSION + ' (Env: OS_INFRA_OPTIM_API_VERSION)')) return parser def check_api_version(check_version): """Validate version supplied by user Returns: * True if version is OK * False if the version has not been checked and the previous plugin check should be performed * throws an exception if the version is no good """ infra_api_version = api_versioning.get_api_version(check_version) # Bypass X.latest format microversion if not infra_api_version.is_latest(): if infra_api_version > api_versioning.APIVersion("2.0"): if not infra_api_version.matches( watcherclient.API_MIN_VERSION, watcherclient.API_MAX_VERSION, ): msg = "versions supported by client: %(min)s - %(max)s" % { "min": watcherclient.API_MIN_VERSION.get_string(), "max": watcherclient.API_MAX_VERSION.get_string(), } raise exceptions.CommandError(msg) return True ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/shell.py0000664000175000017500000002201000000000000022313 0ustar00zuulzuul00000000000000# Copyright (c) 2013 Rackspace, 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. """ Command-line interface to the Watcher API. """ from collections import namedtuple import logging import sys from cliff import app from cliff import command from cliff import commandmanager from cliff import complete from cliff import help as cli_help from keystoneauth1 import loading from osc_lib import logs from osc_lib import utils from watcherclient import client as watcherclient from watcherclient import version LOG = logging.getLogger(__name__) API_NAME = 'infra-optim' API_VERSIONS = { '1': 'watcherclient.v1.client.Client', } DEFAULT_OS_INFRA_OPTIM_API_VERSION = '1.latest' _DEFAULT_IDENTITY_API_VERSION = '3' _IDENTITY_API_VERSION_2 = ['2', '2.0'] _IDENTITY_API_VERSION_3 = ['3'] class WatcherShell(app.App): """Watcher command line interface.""" def __init__(self, **kwargs): self.client = None # Patch command.Command to add a default auth_required = True command.Command.auth_required = True # Some commands do not need authentication cli_help.HelpCommand.auth_required = False complete.CompleteCommand.auth_required = False super(WatcherShell, self).__init__( description=__doc__.strip(), version=version.__version__, command_manager=commandmanager.CommandManager( 'watcherclient.v1'), deferred_help=True, **kwargs ) def create_client(self, args): client = watcherclient.get_client('1', **args.__dict__) return client def build_option_parser(self, description, version, argparse_kwargs=None): """Introduces global arguments for the application. This is inherited from the framework. """ parser = super(WatcherShell, self).build_option_parser( description, version, argparse_kwargs) parser.add_argument('--no-auth', '-N', action='store_true', help='Do not use authentication.') parser.add_argument('--os-identity-api-version', metavar='', default=utils.env('OS_IDENTITY_API_VERSION'), help='Specify Identity API version to use. ' 'Defaults to env[OS_IDENTITY_API_VERSION]' ' or 3.') parser.add_argument('--os-auth-url', '-A', metavar='', default=utils.env('OS_AUTH_URL'), help='Defaults to env[OS_AUTH_URL].') parser.add_argument('--os-region-name', '-R', metavar='', default=utils.env('OS_REGION_NAME'), help='Defaults to env[OS_REGION_NAME].') parser.add_argument('--os-username', '-U', metavar='', default=utils.env('OS_USERNAME'), help='Defaults to env[OS_USERNAME].') parser.add_argument('--os-user-id', metavar='', default=utils.env('OS_USER_ID'), help='Defaults to env[OS_USER_ID].') parser.add_argument('--os-password', '-P', metavar='', default=utils.env('OS_PASSWORD'), help='Defaults to env[OS_PASSWORD].') parser.add_argument('--os-user-domain-id', metavar='', default=utils.env('OS_USER_DOMAIN_ID'), help='Defaults to env[OS_USER_DOMAIN_ID].') parser.add_argument('--os-user-domain-name', metavar='', default=utils.env('OS_USER_DOMAIN_NAME'), help='Defaults to env[OS_USER_DOMAIN_NAME].') parser.add_argument('--os-tenant-name', '-T', metavar='', default=utils.env('OS_TENANT_NAME'), help='Defaults to env[OS_TENANT_NAME].') parser.add_argument('--os-tenant-id', '-I', metavar='', default=utils.env('OS_TENANT_ID'), help='Defaults to env[OS_TENANT_ID].') parser.add_argument('--os-project-id', metavar='', default=utils.env('OS_PROJECT_ID'), help='Another way to specify tenant ID. ' 'This option is mutually exclusive with ' ' --os-tenant-id. ' 'Defaults to env[OS_PROJECT_ID].') parser.add_argument('--os-project-name', metavar='', default=utils.env('OS_PROJECT_NAME'), help='Another way to specify tenant name. ' 'This option is mutually exclusive with ' ' --os-tenant-name. ' 'Defaults to env[OS_PROJECT_NAME].') parser.add_argument('--os-project-domain-id', metavar='', default=utils.env('OS_PROJECT_DOMAIN_ID'), help='Defaults to env[OS_PROJECT_DOMAIN_ID].') parser.add_argument('--os-project-domain-name', metavar='', default=utils.env('OS_PROJECT_DOMAIN_NAME'), help='Defaults to env[OS_PROJECT_DOMAIN_NAME].') parser.add_argument('--os-auth-token', metavar='', default=utils.env('OS_AUTH_TOKEN'), help='Defaults to env[OS_AUTH_TOKEN].') parser.add_argument( '--os-infra-optim-api-version', metavar='', default=utils.env('OS_INFRA_OPTIM_API_VERSION', default=DEFAULT_OS_INFRA_OPTIM_API_VERSION), help='Accepts X, X.Y (where X is major and Y is minor part) or ' '"X.latest", defaults to env[OS_INFRA_OPTIM_API_VERSION].') parser.add_argument('--os-endpoint-type', default=utils.env('OS_ENDPOINT_TYPE'), help='Defaults to env[OS_ENDPOINT_TYPE] or ' '"publicURL"') parser.add_argument('--os-endpoint-override', metavar='', default=utils.env('OS_ENDPOINT_OVERRIDE'), help="Use this API endpoint instead of the " "Service Catalog.") parser.epilog = ('See "watcher help COMMAND" for help ' 'on a specific command.') loading.register_session_argparse_arguments(parser) return parser def configure_logging(self): """Configure logging for the app.""" self.log_configurator = logs.LogConfigurator(self.options) self.dump_stack_trace = self.log_configurator.dump_trace def prepare_to_run_command(self, cmd): """Prepares to run the command Checks if the minimal parameters are provided and creates the client interface. This is inherited from the framework. """ self.client_manager = namedtuple('ClientManager', 'infra_optim') if cmd.auth_required: client = self.create_client(self.options) setattr(self.client_manager, 'infra-optim', client) def run(self, argv): ret_val = 1 self.command_options = argv try: ret_val = super(WatcherShell, self).run(argv) return ret_val except Exception as e: if not logging.getLogger('').handlers: logging.basicConfig() LOG.error('Exception raised: %s', str(e)) return ret_val finally: LOG.info("END return value: %s", ret_val) def main(argv=None): if argv is None: argv = sys.argv[1:] watcher_app = WatcherShell() return watcher_app.run(argv) if __name__ == '__main__': # pragma: no cover sys.exit(main(sys.argv[1:])) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1740768124.847024 python_watcherclient-4.8.0/watcherclient/tests/0000775000175000017500000000000000000000000022001 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/__init__.py0000664000175000017500000000000000000000000024100 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8510242 python_watcherclient-4.8.0/watcherclient/tests/unit/0000775000175000017500000000000000000000000022760 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/__init__.py0000664000175000017500000000000000000000000025057 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8510242 python_watcherclient-4.8.0/watcherclient/tests/unit/common/0000775000175000017500000000000000000000000024250 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/common/__init__.py0000664000175000017500000000000000000000000026347 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/common/test_api_versioning.py0000664000175000017500000001332100000000000030675 0ustar00zuulzuul00000000000000# Copyright 2016 Mirantis # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES 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 watcherclient.common import api_versioning from watcherclient import exceptions from watcherclient.tests.unit import utils class APIVersionTestCase(utils.BaseTestCase): def test_valid_version_strings(self): def _test_string(version, exp_major, exp_minor): v = api_versioning.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_versioning.APIVersion() self.assertTrue(v.is_null()) def test_invalid_version_strings(self): self.assertRaises(exceptions.UnsupportedVersion, api_versioning.APIVersion, "2") self.assertRaises(exceptions.UnsupportedVersion, api_versioning.APIVersion, "200") self.assertRaises(exceptions.UnsupportedVersion, api_versioning.APIVersion, "2.1.4") self.assertRaises(exceptions.UnsupportedVersion, api_versioning.APIVersion, "200.23.66.3") self.assertRaises(exceptions.UnsupportedVersion, api_versioning.APIVersion, "5 .3") self.assertRaises(exceptions.UnsupportedVersion, api_versioning.APIVersion, "5. 3") self.assertRaises(exceptions.UnsupportedVersion, api_versioning.APIVersion, "5.03") self.assertRaises(exceptions.UnsupportedVersion, api_versioning.APIVersion, "02.1") self.assertRaises(exceptions.UnsupportedVersion, api_versioning.APIVersion, "2.001") self.assertRaises(exceptions.UnsupportedVersion, api_versioning.APIVersion, "") self.assertRaises(exceptions.UnsupportedVersion, api_versioning.APIVersion, " 2.1") self.assertRaises(exceptions.UnsupportedVersion, api_versioning.APIVersion, "2.1 ") def test_version_comparisons(self): v1 = api_versioning.APIVersion("2.0") v2 = api_versioning.APIVersion("2.5") v3 = api_versioning.APIVersion("5.23") v4 = api_versioning.APIVersion("2.0") v_null = api_versioning.APIVersion() self.assertLess(v1, v2) self.assertGreater(v3, v2) self.assertNotEqual(v1, v2) self.assertEqual(v1, v4) self.assertNotEqual(v1, v_null) self.assertEqual(v_null, v_null) self.assertRaises(TypeError, v1.__le__, "2.1") def test_version_matches(self): v1 = api_versioning.APIVersion("2.0") v2 = api_versioning.APIVersion("2.5") v3 = api_versioning.APIVersion("2.45") v4 = api_versioning.APIVersion("3.3") v5 = api_versioning.APIVersion("3.23") v6 = api_versioning.APIVersion("2.0") v7 = api_versioning.APIVersion("3.3") v8 = api_versioning.APIVersion("4.0") v_null = api_versioning.APIVersion() self.assertTrue(v2.matches(v1, v3)) self.assertTrue(v2.matches(v1, v_null)) 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_versioning.APIVersion(v1_string) self.assertEqual(v1_string, v1.get_string()) self.assertRaises(ValueError, api_versioning.APIVersion().get_string) class GetAPIVersionTestCase(utils.BaseTestCase): def test_get_available_client_versions(self): output = api_versioning.get_available_major_versions() self.assertNotEqual([], output) def test_wrong_format(self): self.assertRaises(exceptions.UnsupportedVersion, api_versioning.get_api_version, "something_wrong") def test_wrong_major_version(self): self.assertRaises(exceptions.UnsupportedVersion, api_versioning.get_api_version, "2") @mock.patch("watcherclient.common.api_versioning.APIVersion") def test_only_major_part_is_presented(self, mock_apiversion): version = 7 self.assertEqual(mock_apiversion.return_value, api_versioning.get_api_version(version)) mock_apiversion.assert_called_once_with("%s.0" % str(version)) @mock.patch("watcherclient.common.api_versioning.APIVersion") def test_major_and_minor_parts_is_presented(self, mock_apiversion): version = "2.7" self.assertEqual(mock_apiversion.return_value, api_versioning.get_api_version(version)) mock_apiversion.assert_called_once_with(version) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/keystone_client_fixtures.py0000664000175000017500000000605500000000000030470 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils from oslo_utils import uuidutils from keystoneauth1.fixture import v2 as ks_v2_fixture from keystoneauth1.fixture import v3 as ks_v3_fixture # these are copied from python-keystoneclient tests BASE_HOST = 'http://keystone.example.com' BASE_URL = "%s:5000/" % BASE_HOST UPDATED = '2013-03-06T00:00:00Z' V2_URL = "%sv2.0" % BASE_URL V2_DESCRIBED_BY_HTML = {'href': 'http://docs.openstack.org/api/' 'openstack-identity-service/2.0/content/', 'rel': 'describedby', 'type': 'text/html'} V2_DESCRIBED_BY_PDF = {'href': 'http://docs.openstack.org/api/openstack-ident' 'ity-service/2.0/identity-dev-guide-2.0.pdf', 'rel': 'describedby', 'type': 'application/pdf'} V2_VERSION = {'id': 'v2.0', 'links': [{'href': V2_URL, 'rel': 'self'}, V2_DESCRIBED_BY_HTML, V2_DESCRIBED_BY_PDF], 'status': 'stable', 'updated': UPDATED} V3_URL = "%sv3" % BASE_URL V3_MEDIA_TYPES = [{'base': 'application/json', 'type': 'application/vnd.openstack.identity-v3+json'}, {'base': 'application/xml', 'type': 'application/vnd.openstack.identity-v3+xml'}] V3_VERSION = {'id': 'v3.0', 'links': [{'href': V3_URL, 'rel': 'self'}], 'media-types': V3_MEDIA_TYPES, 'status': 'stable', 'updated': UPDATED} TOKENID = uuidutils.generate_uuid(dashed=False) def _create_version_list(versions): return jsonutils.dumps({'versions': {'values': versions}}) def _create_single_version(version): return jsonutils.dumps({'version': version}) V3_VERSION_LIST = _create_version_list([V3_VERSION, V2_VERSION]) V2_VERSION_LIST = _create_version_list([V2_VERSION]) V3_VERSION_ENTRY = _create_single_version(V3_VERSION) V2_VERSION_ENTRY = _create_single_version(V2_VERSION) def keystone_request_callback(request, uri, headers): response_headers = {"content-type": "application/json"} token_id = TOKENID if uri == BASE_URL: return (200, headers, V3_VERSION_LIST) elif uri == BASE_URL + "/v2.0": v2_token = ks_v2_fixture.Token(token_id) return (200, response_headers, jsonutils.dumps(v2_token)) elif uri == BASE_URL + "/v3": v3_token = ks_v3_fixture.Token() response_headers["X-Subject-Token"] = token_id return (201, response_headers, jsonutils.dumps(v3_token)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/test_client.py0000664000175000017500000003425100000000000025654 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from unittest import mock from keystoneauth1 import loading as kaloading from watcherclient import client as watcherclient from watcherclient.common import httpclient from watcherclient import exceptions from watcherclient.tests.unit import utils class ClientTest(utils.BaseTestCase): def test_get_client_with_auth_token_watcher_url(self): kwargs = { 'watcher_url': 'http://watcher.example.org:9322/', 'os_auth_token': 'USER_AUTH_TOKEN', } client = watcherclient.get_client('1', **kwargs) self.assertEqual('USER_AUTH_TOKEN', client.http_client.auth_token) self.assertEqual('http://watcher.example.org:9322/', client.http_client.endpoint) @mock.patch.object(kaloading.session, 'Session', autospec=True) @mock.patch.object(kaloading, 'get_plugin_loader', autospec=True) def _test_get_client(self, mock_ks_loader, mock_ks_session, version=None, auth='password', **kwargs): session = mock_ks_session.return_value.load_from_options.return_value session.get_endpoint.return_value = 'http://localhost:9322/v1/f14b4123' mock_ks_loader.return_value.load_from_options.return_value = 'auth' watcherclient.get_client('1', **kwargs) mock_ks_loader.assert_called_once_with(auth) mock_ks_session.return_value.load_from_options.assert_called_once_with( auth='auth', timeout=kwargs.get('timeout'), insecure=kwargs.get('insecure'), cert=kwargs.get('cert'), cacert=kwargs.get('cacert'), key=kwargs.get('key')) session.get_endpoint.assert_called_once_with( service_type=kwargs.get('os_service_type') or 'infra-optim', interface=kwargs.get('os_endpoint_type') or 'publicURL', region_name=kwargs.get('os_region_name')) def test_get_client_no_auth_token(self): kwargs = { 'os_tenant_name': 'TENANT_NAME', 'os_username': 'USERNAME', 'os_password': 'PASSWORD', 'os_auth_url': 'http://localhost:35357/v2.0', 'os_auth_token': '', } self._test_get_client(**kwargs) def test_get_client_service_and_endpoint_type_defaults(self): kwargs = { 'os_tenant_name': 'TENANT_NAME', 'os_username': 'USERNAME', 'os_password': 'PASSWORD', 'os_auth_url': 'http://localhost:35357/v2.0', 'os_auth_token': '', 'os_service_type': '', 'os_endpoint_type': '' } self._test_get_client(**kwargs) def test_get_client_with_region_no_auth_token(self): kwargs = { 'os_tenant_name': 'TENANT_NAME', 'os_username': 'USERNAME', 'os_password': 'PASSWORD', 'os_region_name': 'REGIONONE', 'os_auth_url': 'http://localhost:35357/v2.0', 'os_auth_token': '', } self._test_get_client(**kwargs) def test_get_client_no_url(self): kwargs = { 'os_tenant_name': 'TENANT_NAME', 'os_username': 'USERNAME', 'os_password': 'PASSWORD', 'os_auth_url': '', } self.assertRaises( exceptions.AmbiguousAuthSystem, watcherclient.get_client, '1', **kwargs) # test the alias as well to ensure backwards compatibility self.assertRaises( exceptions.AmbigiousAuthSystem, watcherclient.get_client, '1', **kwargs) def test_get_client_incorrect_auth_params(self): kwargs = { 'os_tenant_name': 'TENANT_NAME', 'os_username': 'USERNAME', 'os_auth_url': 'http://localhost:35357/v2.0', } self.assertRaises( exceptions.AmbiguousAuthSystem, watcherclient.get_client, '1', **kwargs) def test_get_client_with_api_version_latest(self): kwargs = { 'os_tenant_name': 'TENANT_NAME', 'os_username': 'USERNAME', 'os_password': 'PASSWORD', 'os_auth_url': 'http://localhost:35357/v2.0', 'os_auth_token': '', 'os_infra_optim_api_version': "latest", } self._test_get_client(**kwargs) def test_get_client_with_api_version_numeric(self): kwargs = { 'os_tenant_name': 'TENANT_NAME', 'os_username': 'USERNAME', 'os_password': 'PASSWORD', 'os_auth_url': 'http://localhost:35357/v2.0', 'os_auth_token': '', 'os_infra_optim_api_version': "1.4", } self._test_get_client(**kwargs) def test_get_client_with_auth_token(self): kwargs = { 'os_auth_url': 'http://localhost:35357/v2.0', 'os_auth_token': 'USER_AUTH_TOKEN', } self._test_get_client(auth='token', **kwargs) def test_get_client_with_region_name_auth_token(self): kwargs = { 'os_auth_url': 'http://localhost:35357/v2.0', 'os_region_name': 'REGIONONE', 'os_auth_token': 'USER_AUTH_TOKEN', } self._test_get_client(auth='token', **kwargs) def test_get_client_only_session_passed(self): session = mock.Mock() session.get_endpoint.return_value = 'http://localhost:35357/v2.0' kwargs = { 'session': session, } watcherclient.get_client('1', **kwargs) session.get_endpoint.assert_called_once_with( service_type='infra-optim', interface='publicURL', region_name=None) def test_get_client_incorrect_session_passed(self): session = mock.Mock() session.get_endpoint.side_effect = Exception('boo') kwargs = { 'session': session, } self.assertRaises( exceptions.AmbiguousAuthSystem, watcherclient.get_client, '1', **kwargs) @mock.patch.object(kaloading.session, 'Session', autospec=True) @mock.patch.object(kaloading, 'get_plugin_loader', autospec=True) def _test_loader_arguments_passed_correctly( self, mock_ks_loader, mock_ks_session, passed_kwargs, expected_kwargs): session = mock_ks_session.return_value.load_from_options.return_value session.get_endpoint.return_value = 'http://localhost:9322/v1/f14b4123' mock_ks_loader.return_value.load_from_options.return_value = 'auth' watcherclient.get_client('1', **passed_kwargs) mock_ks_loader.return_value.load_from_options.assert_called_once_with( **expected_kwargs) mock_ks_session.return_value.load_from_options.assert_called_once_with( auth='auth', timeout=passed_kwargs.get('timeout'), insecure=passed_kwargs.get('insecure'), cert=passed_kwargs.get('cert'), cacert=passed_kwargs.get('cacert'), key=passed_kwargs.get('key')) session.get_endpoint.assert_called_once_with( service_type=passed_kwargs.get('os_service_type') or 'infra-optim', interface=passed_kwargs.get('os_endpoint_type') or 'publicURL', region_name=passed_kwargs.get('os_region_name')) def test_loader_arguments_token(self): passed_kwargs = { 'os_auth_url': 'http://localhost:35357/v3', 'os_region_name': 'REGIONONE', 'os_auth_token': 'USER_AUTH_TOKEN', } expected_kwargs = { 'auth_url': 'http://localhost:35357/v3', 'project_id': None, 'project_name': None, 'user_domain_id': None, 'user_domain_name': None, 'project_domain_id': None, 'project_domain_name': None, 'token': 'USER_AUTH_TOKEN' } self._test_loader_arguments_passed_correctly( passed_kwargs=passed_kwargs, expected_kwargs=expected_kwargs) def test_loader_arguments_password_tenant_name(self): passed_kwargs = { 'os_auth_url': 'http://localhost:35357/v3', 'os_region_name': 'REGIONONE', 'os_tenant_name': 'TENANT', 'os_username': 'user', 'os_password': '1234', 'os_project_domain_id': 'DEFAULT', 'os_user_domain_id': 'DEFAULT' } expected_kwargs = { 'auth_url': 'http://localhost:35357/v3', 'project_id': None, 'project_name': 'TENANT', 'user_domain_id': 'DEFAULT', 'user_domain_name': None, 'project_domain_id': 'DEFAULT', 'project_domain_name': None, 'username': 'user', 'password': '1234' } self._test_loader_arguments_passed_correctly( passed_kwargs=passed_kwargs, expected_kwargs=expected_kwargs) def test_loader_arguments_password_project_id(self): passed_kwargs = { 'os_auth_url': 'http://localhost:35357/v3', 'os_region_name': 'REGIONONE', 'os_project_id': '1000', 'os_username': 'user', 'os_password': '1234', 'os_project_domain_name': 'domain1', 'os_user_domain_name': 'domain1' } expected_kwargs = { 'auth_url': 'http://localhost:35357/v3', 'project_id': '1000', 'project_name': None, 'user_domain_id': None, 'user_domain_name': 'domain1', 'project_domain_id': None, 'project_domain_name': 'domain1', 'username': 'user', 'password': '1234' } self._test_loader_arguments_passed_correctly( passed_kwargs=passed_kwargs, expected_kwargs=expected_kwargs) @mock.patch.object(watcherclient, 'Client') @mock.patch.object(kaloading.session, 'Session', autospec=True) def test_correct_arguments_passed_to_client_constructor_noauth_mode( self, mock_ks_session, mock_client): kwargs = { 'watcher_url': 'http://watcher.example.org:9322/', 'os_auth_token': 'USER_AUTH_TOKEN', 'os_infra_optim_api_version': 'latest', 'insecure': True, 'max_retries': 10, 'retry_interval': 10, 'os_cacert': 'data' } watcherclient.get_client('1', **kwargs) mock_client.assert_called_once_with( '1', 'http://watcher.example.org:9322/', **{ 'os_infra_optim_api_version': 'latest', 'max_retries': 10, 'retry_interval': 10, 'token': 'USER_AUTH_TOKEN', 'insecure': True, 'ca_file': 'data', 'cert_file': None, 'key_file': None, 'timeout': None, 'session': None } ) self.assertFalse(mock_ks_session.called) @mock.patch.object(watcherclient, 'Client') @mock.patch.object(kaloading.session, 'Session', autospec=True) def test_correct_arguments_passed_to_client_constructor_session_created( self, mock_ks_session, mock_client): session = mock_ks_session.return_value.load_from_options.return_value kwargs = { 'os_auth_url': 'http://localhost:35357/v3', 'os_region_name': 'REGIONONE', 'os_project_id': '1000', 'os_username': 'user', 'os_password': '1234', 'os_project_domain_name': 'domain1', 'os_user_domain_name': 'domain1' } watcherclient.get_client('1', **kwargs) mock_client.assert_called_once_with( '1', session.get_endpoint.return_value, **{ 'os_infra_optim_api_version': None, 'max_retries': None, 'retry_interval': None, 'session': session, } ) @mock.patch.object(watcherclient, 'Client') @mock.patch.object(kaloading.session, 'Session', autospec=True) def test_correct_arguments_passed_to_client_constructor_session_passed( self, mock_ks_session, mock_client): session = mock.Mock() kwargs = { 'session': session, } watcherclient.get_client('1', **kwargs) mock_client.assert_called_once_with( '1', session.get_endpoint.return_value, **{ 'os_infra_optim_api_version': None, 'max_retries': None, 'retry_interval': None, 'session': session, } ) self.assertFalse(mock_ks_session.called) def test_safe_header_with_auth_token(self): (name, value) = ('X-Auth-Token', u'3b640e2e64d946ac8f55615aff221dc1') expected_header = (u'X-Auth-Token', '{SHA1}6de9fb3b0b89099030a54abfeb468e7b1b1f0f2b') client = httpclient.HTTPClient('http://localhost/') header_redact = client._process_header(name, value) self.assertEqual(expected_header, header_redact) def test_safe_header_with_no_auth_token(self): name, value = ('Accept', 'application/json') header = ('Accept', 'application/json') client = httpclient.HTTPClient('http://localhost/') header_redact = client._process_header(name, value) self.assertEqual(header, header_redact) def test_make_connection_url(self): endpoint = 'http://localhost/infra-optim' url = '/v1/goals' expected_url = 'http://localhost/infra-optim/v1/goals' client = httpclient.HTTPClient(endpoint) conn_url = client._make_connection_url(url) self.assertEqual(expected_url, conn_url) def test_port_ends_with_one(self): endpoint = "http://localhost:8081/" http_client = httpclient.HTTPClient(endpoint) self.assertEqual(endpoint, http_client._make_connection_url("")) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/test_import.py0000664000175000017500000000250600000000000025706 0ustar00zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from watcherclient.tests.unit import utils module_str = 'watcherclient' class ImportTest(utils.BaseTestCase): def check_exported_symbols(self, exported_symbols): self.assertIn('client', exported_symbols) self.assertIn('exceptions', exported_symbols) def test_import_objects(self): module = __import__(module_str) exported_symbols = dir(module) self.check_exported_symbols(exported_symbols) def test_default_import(self): default_imports = __import__(module_str, globals(), locals(), ['*']) exported_symbols = dir(default_imports) self.check_exported_symbols(exported_symbols) def test_import__all__(self): module = __import__(module_str) self.check_exported_symbols(module.__all__) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/test_utils.py0000664000175000017500000001565700000000000025547 0ustar00zuulzuul00000000000000# # Copyright 2013 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES 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 watcherclient.common.apiclient import exceptions as exc from watcherclient.common import utils from watcherclient.tests.unit import utils as test_utils class UtilsTest(test_utils.BaseTestCase): def test_args_array_to_dict(self): my_args = { 'matching_metadata': ['str=foo', 'int=1', 'bool=true', 'list=[1, 2, 3]', 'dict={"foo": "bar"}'], 'other': 'value' } cleaned_dict = utils.args_array_to_dict(my_args, "matching_metadata") self.assertEqual({ 'matching_metadata': {'str': 'foo', 'int': 1, 'bool': True, 'list': [1, 2, 3], 'dict': {'foo': 'bar'}}, 'other': 'value' }, cleaned_dict) def test_args_array_to_patch(self): my_args = { 'attributes': ['str=foo', 'int=1', 'bool=true', 'list=[1, 2, 3]', 'dict={"foo": "bar"}'], 'op': 'add', } patch = utils.args_array_to_patch(my_args['op'], my_args['attributes']) self.assertEqual([{'op': 'add', 'value': 'foo', 'path': '/str'}, {'op': 'add', 'value': 1, 'path': '/int'}, {'op': 'add', 'value': True, 'path': '/bool'}, {'op': 'add', 'value': [1, 2, 3], 'path': '/list'}, {'op': 'add', 'value': {"foo": "bar"}, 'path': '/dict'}], patch) def test_args_array_to_patch_format_error(self): my_args = { 'attributes': ['foobar'], 'op': 'add', } self.assertRaises(exc.CommandError, utils.args_array_to_patch, my_args['op'], my_args['attributes']) def test_args_array_to_patch_remove(self): my_args = { 'attributes': ['/foo', 'extra/bar'], 'op': 'remove', } patch = utils.args_array_to_patch(my_args['op'], my_args['attributes']) self.assertEqual([{'op': 'remove', 'path': '/foo'}, {'op': 'remove', 'path': '/extra/bar'}], patch) def test_split_and_deserialize(self): ret = utils.split_and_deserialize('str=foo') self.assertEqual(('str', 'foo'), ret) ret = utils.split_and_deserialize('int=1') self.assertEqual(('int', 1), ret) ret = utils.split_and_deserialize('bool=false') self.assertEqual(('bool', False), ret) ret = utils.split_and_deserialize('list=[1, "foo", 2]') self.assertEqual(('list', [1, "foo", 2]), ret) ret = utils.split_and_deserialize('dict={"foo": 1}') self.assertEqual(('dict', {"foo": 1}), ret) ret = utils.split_and_deserialize('str_int="1"') self.assertEqual(('str_int', "1"), ret) def test_split_and_deserialize_fail(self): self.assertRaises(exc.CommandError, utils.split_and_deserialize, 'foo:bar') class CommonParamsForListTest(test_utils.BaseTestCase): def setUp(self): super(CommonParamsForListTest, self).setUp() self.args = mock.Mock(limit=None, marker=None, sort_key=None, sort_dir=None) self.args.detail = False self.expected_params = {'detail': False} def test_nothing_set(self): self.assertEqual(self.expected_params, utils.common_params_for_list(self.args, [], [])) def test_limit(self): self.args.limit = 42 self.expected_params.update({'limit': 42}) self.assertEqual(self.expected_params, utils.common_params_for_list(self.args, [], [])) def test_invalid_limit(self): self.args.limit = -42 self.assertRaises(exc.CommandError, utils.common_params_for_list, self.args, [], []) def test_marker(self): self.args.marker = 'e420a881-d7df-4de2-bbf3-378cc13d9b3a' self.expected_params.update( {'marker': 'e420a881-d7df-4de2-bbf3-378cc13d9b3a'}) self.assertEqual(self.expected_params, utils.common_params_for_list(self.args, [], [])) def test_sort_key_and_sort_dir(self): self.args.sort_key = 'field' self.args.sort_dir = 'desc' self.expected_params.update({'sort_key': 'field', 'sort_dir': 'desc'}) self.assertEqual(self.expected_params, utils.common_params_for_list(self.args, ['field'], [])) def test_sort_key_allows_label(self): self.args.sort_key = 'Label' self.expected_params.update({'sort_key': 'field'}) self.assertEqual(self.expected_params, utils.common_params_for_list(self.args, ['field', 'field2'], ['Label', 'Label2'])) def test_sort_key_invalid(self): self.args.sort_key = 'something' self.assertRaises(exc.CommandError, utils.common_params_for_list, self.args, ['field', 'field2'], []) def test_sort_dir_invalid(self): self.args.sort_dir = 'something' self.assertRaises(exc.CommandError, utils.common_params_for_list, self.args, [], []) def test_detail(self): self.args.detail = True self.expected_params['detail'] = True self.assertEqual(self.expected_params, utils.common_params_for_list(self.args, [], [])) class CommonFiltersTest(test_utils.BaseTestCase): def test_limit(self): result = utils.common_filters(limit=42) self.assertEqual(['limit=42'], result) def test_limit_0(self): result = utils.common_filters(limit=0) self.assertEqual([], result) def test_other(self): for key in ('sort_key', 'sort_dir'): result = utils.common_filters(**{key: 'test'}) self.assertEqual(['%s=test' % key], result) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/utils.py0000664000175000017500000001043100000000000024471 0ustar00zuulzuul00000000000000# Copyright 2012 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES 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 io import os from unittest import mock import fixtures from oslo_utils import strutils import testtools class BaseTestCase(testtools.TestCase): def setUp(self): super(BaseTestCase, self).setUp() self.useFixture(fixtures.FakeLogger()) # If enabled, stdout and/or stderr is captured and will appear in # test results if that test fails. if strutils.bool_from_string(os.environ.get('OS_STDOUT_CAPTURE')): stdout = self.useFixture(fixtures.StringStream('stdout')).stream self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) if strutils.bool_from_string(os.environ.get('OS_STDERR_CAPTURE')): stderr = self.useFixture(fixtures.StringStream('stderr')).stream self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) class FakeAPI(object): def __init__(self, responses): self.responses = responses self.calls = [] def _request(self, method, url, headers=None, body=None): call = (method, url, headers or {}, body) self.calls.append(call) return self.responses[url][method] def raw_request(self, *args, **kwargs): response = self._request(*args, **kwargs) body_iter = iter(io.StringIO(response[1])) return FakeResponse(response[0]), body_iter def json_request(self, *args, **kwargs): response = self._request(*args, **kwargs) return FakeResponse(response[0]), response[1] class FakeConnection(object): def __init__(self, response=None): self._response = response self._last_request = None def request(self, method, conn_url, **kwargs): self._last_request = (method, conn_url, kwargs) def setresponse(self, response): self._response = response def getresponse(self): return self._response def __repr__(self): return ("FakeConnection(response=%s)" % (self._response)) class FakeResponse(object): def __init__(self, headers, body=None, version=None, status=None, reason=None): """Fake object to help testing. :param headers: dict representing HTTP response headers :param body: file-like object """ self.headers = headers self.body = body self.raw = mock.Mock() self.raw.version = version self.status_code = status self.reason = reason def getheaders(self): return copy.deepcopy(self.headers).items() def getheader(self, key, default): return self.headers.get(key, default) def read(self, amt): return self.body.read(amt) def __repr__(self): return ("FakeResponse(%s, body=%s, version=%s, status=%s, reason=%s)" % (self.headers, self.body, self.version, self.status, self.reason)) class FakeSessionResponse(object): def __init__(self, headers, content=None, status_code=None, version=None): self.headers = headers self.content = content self.status_code = status_code self.raw = mock.Mock() self.raw.version = version self.reason = '' def iter_content(self, chunk_size): return iter(self.content) class FakeSession(object): def __init__(self, headers, content=None, status_code=None, version=None): self.headers = headers self.content = content self.status_code = status_code self.version = version self.verify = False self.cert = ('test_cert', 'test_key') def request(self, url, method, **kwargs): request = FakeSessionResponse( self.headers, self.content, self.status_code, self.version) return request ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1740768124.855024 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/0000775000175000017500000000000000000000000023306 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/__init__.py0000664000175000017500000000000000000000000025405 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/base.py0000664000175000017500000000553400000000000024601 0ustar00zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import shlex from unittest import mock from osc_lib import utils as oscutils from oslo_serialization import jsonutils from watcherclient.common import httpclient from watcherclient.tests.unit import utils class CommandTestCase(utils.BaseTestCase): def setUp(self, os_infra_optim_api_version='1.0'): super(CommandTestCase, self).setUp() self.fake_env = { 'debug': False, 'insecure': False, 'no_auth': False, 'os_auth_token': '', 'os_auth_url': 'http://127.0.0.1:5000/v2.0', 'os_endpoint_override': 'http://watcher-endpoint:9322', 'os_username': 'test', 'os_password': 'test', 'timeout': 600, 'os_infra_optim_api_version': os_infra_optim_api_version} self.m_env = mock.Mock( name='m_env', side_effect=lambda x, *args, **kwargs: self.fake_env.get( x.lower(), kwargs.get('default', ''))) self.p_env = mock.patch.object(oscutils, 'env', self.m_env) self.p_env.start() self.addCleanup(self.p_env.stop) self.p_construct_http_client = mock.patch.object( httpclient, '_construct_http_client') self.m_construct_http_client = self.p_construct_http_client.start() self.addCleanup(self.p_construct_http_client.stop) def run_cmd(self, cmd, formatting='json'): if formatting and formatting != 'table': formatter_arg = " -f %s" % formatting formatter = jsonutils.loads else: formatter_arg = '' formatter = str formatted_cmd = "%(cmd)s%(formatter)s" % dict( cmd=cmd, formatter=formatter_arg) exit_code = self.cmd.run(shlex.split(formatted_cmd)) try: raw_data = self.stdout.getvalue() formatted_output = formatter(self.stdout.getvalue()) except Exception: self.fail("Formatting error (`%s` -> '%s')" % (raw_data, formatting)) return exit_code, formatted_output def resource_as_dict(self, resource, columns=(), column_headers=()): mapping = dict(zip(columns, column_headers)) return {mapping[k]: v for k, v in resource.to_dict().items() if not columns or columns and k in mapping} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_action.py0000664000175000017500000001702100000000000026175 0ustar00zuulzuul00000000000000# Copyright 2013 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 testtools from testtools import matchers from watcherclient.tests.unit import utils import watcherclient.v1.action ACTION1 = { 'id': 1, 'uuid': '770ef053-ecb3-48b0-85b5-d55a2dbc6588', 'action_plan': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', 'description': 'Action_1 description', 'next': '239f02a5-9649-4e14-9d33-ac2bf67cb755', 'state': 'PENDING', } ACTION2 = { 'id': 2, 'uuid': '239f02a5-9649-4e14-9d33-ac2bf67cb755', 'action_plan': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', 'description': 'Action_2 description', 'next': '67653274-eb24-c7ba-70f6-a84e73d80843', 'state': 'PENDING', } ACTION3 = { 'id': 3, 'uuid': '67653274-eb24-c7ba-70f6-a84e73d80843', 'action_plan': 'a5199d0e-0702-4613-9234-5ae2af8dafea', 'description': 'Action_3 description', 'next': None, 'state': 'PENDING', } ACTION_PLAN1 = { 'id': 1, 'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea', 'audit': '770ef053-ecb3-48b0-85b5-d55a2dbc6588', 'state': 'RECOMMENDED' } fake_responses = { '/v1/actions': { 'GET': ( {}, {"actions": [ACTION1, ACTION2, ACTION3]}, ), }, '/v1/actions/?action_plan_uuid=%s' % ACTION1['action_plan']: { 'GET': ( {}, {"actions": [ACTION1, ACTION2]}, ), }, '/v1/actions/?audit_uuid=%s' % ACTION_PLAN1['audit']: { 'GET': ( {}, {"actions": [ACTION3]}, ), }, '/v1/actions/detail': { 'GET': ( {}, {"actions": [ACTION1, ACTION2, ACTION3]}, ), }, '/v1/actions/%s' % ACTION1['uuid']: { 'GET': ( {}, ACTION1, ), 'DELETE': ( {}, None, ), }, '/v1/actions/detail?action_plan_uuid=%s' % ACTION1['action_plan']: { 'GET': ( {}, {"actions": [ACTION1, ACTION2]}, ), }, '/v1/actions/detail?audit_uuid=%s' % ACTION_PLAN1['audit']: { 'GET': ( {}, {"actions": [ACTION3]}, ), } } fake_responses_pagination = { '/v1/actions': { 'GET': ( {}, {"actions": [ACTION1], "next": "http://127.0.0.1:9322/v1/actions/?limit=1"} ), }, '/v1/actions/?limit=1': { 'GET': ( {}, {"actions": [ACTION2]} ), }, } fake_responses_sorting = { '/v1/actions/?sort_key=updated_at': { 'GET': ( {}, {"actions": [ACTION3, ACTION2, ACTION1]} ), }, '/v1/actions/?sort_dir=desc': { 'GET': ( {}, {"actions": [ACTION3, ACTION2, ACTION1]} ), }, } fake_responses_marker = { '/v1/actions/?marker=770ef053-ecb3-48b0-85b5-d55a2dbc6588': { 'GET': ( {}, {"actions": [ACTION2, ACTION3]} ), }, } class ActionManagerTest(testtools.TestCase): def setUp(self): super(ActionManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = watcherclient.v1.action.ActionManager(self.api) def test_actions_list(self): actions = self.mgr.list() expect = [ ('GET', '/v1/actions', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(3, len(actions)) def test_actions_list_by_action_plan(self): actions = self.mgr.list(action_plan=ACTION1['action_plan']) expect = [ ('GET', '/v1/actions/?action_plan_uuid=%s' % ACTION1['action_plan'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(actions)) def test_actions_list_detail(self): actions = self.mgr.list(detail=True) expect = [ ('GET', '/v1/actions/detail', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(3, len(actions)) def test_actions_list_by_action_plan_detail(self): actions = self.mgr.list(action_plan=ACTION1['action_plan'], detail=True) expect = [ ('GET', '/v1/actions/detail?action_plan_uuid=%s' % ACTION1['action_plan'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(actions)) def test_actions_list_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.action.ActionManager(self.api) actions = self.mgr.list(limit=1) expect = [ ('GET', '/v1/actions/?limit=1', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(actions, matchers.HasLength(1)) def test_actions_list_pagination_no_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.action.ActionManager(self.api) actions = self.mgr.list(limit=0) expect = [ ('GET', '/v1/actions', {}, None), ('GET', '/v1/actions/?limit=1', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertThat(actions, matchers.HasLength(2)) def test_actions_list_sort_key(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = watcherclient.v1.action.ActionManager(self.api) actions = self.mgr.list(sort_key='updated_at') expect = [ ('GET', '/v1/actions/?sort_key=updated_at', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(3, len(actions)) def test_actions_list_sort_dir(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = watcherclient.v1.action.ActionManager(self.api) actions = self.mgr.list(sort_dir='desc') expect = [ ('GET', '/v1/actions/?sort_dir=desc', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(3, len(actions)) def test_actions_list_marker(self): self.api = utils.FakeAPI(fake_responses_marker) self.mgr = watcherclient.v1.action.ActionManager(self.api) actions = self.mgr.list( marker='770ef053-ecb3-48b0-85b5-d55a2dbc6588') expect = [ ('GET', '/v1/actions/?marker=770ef053-ecb3-48b0-85b5-d55a2dbc6588', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(actions)) def test_actions_show(self): action = self.mgr.get(ACTION1['uuid']) expect = [ ('GET', '/v1/actions/%s' % ACTION1['uuid'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(ACTION1['uuid'], action.uuid) self.assertEqual(ACTION1['action_plan'], action.action_plan) self.assertEqual(ACTION1['next'], action.next) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_action_plan.py0000664000175000017500000002414400000000000027213 0ustar00zuulzuul00000000000000# Copyright 2013 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 copy from unittest import mock import testtools from testtools import matchers from oslo_utils.uuidutils import generate_uuid from watcherclient.common.apiclient.exceptions import HTTPClientError from watcherclient.tests.unit import utils import watcherclient.v1.action_plan ACTION_PLAN1 = { 'id': 1, 'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', 'audit': '770ef053-ecb3-48b0-85b5-d55a2dbc6588', 'state': 'RECOMMENDED' } ACTION_PLAN2 = { 'id': 2, 'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea', 'audit': '239f02a5-9649-4e14-9d33-ac2bf67cb755', 'state': 'RECOMMENDED' } UPDATED_ACTION_PLAN = copy.deepcopy(ACTION_PLAN1) NEW_STATE = 'PENDING' UPDATED_ACTION_PLAN['state'] = NEW_STATE START_ACTION_PLAN = copy.deepcopy(ACTION_PLAN1) START_ACTION_PLAN['state'] = NEW_STATE ONGOING_ACTION_PLAN = copy.deepcopy(ACTION_PLAN1) ONGOING_ACTION_PLAN['state'] = 'ONGOING' CANCELLING_ACTION_PLAN = copy.deepcopy(ACTION_PLAN1) CANCELLING_ACTION_PLAN['state'] = 'CANCELLING' CANCELD_ACTION_PLAN = copy.deepcopy(ACTION_PLAN2) CANCELD_ACTION_PLAN['state'] = 'CANCELLED' fake_responses = { '/v1/action_plans': { 'GET': ( {}, {"action_plans": [ACTION_PLAN1, ACTION_PLAN2]}, ), }, '/v1/action_plans/detail': { 'GET': ( {}, {"action_plans": [ACTION_PLAN1, ACTION_PLAN2]}, ), }, '/v1/action_plans/%s' % ACTION_PLAN1['uuid']: { 'GET': ( {}, ACTION_PLAN1, ), 'DELETE': ( {}, None, ), 'PATCH': ( {}, UPDATED_ACTION_PLAN, ), }, '/v1/action_plans/%s/start' % ACTION_PLAN1['uuid']: { 'POST': ( {}, START_ACTION_PLAN, ), }, '/v1/action_plans/detail?uuid=%s' % ACTION_PLAN1['uuid']: { 'GET': ( {}, {"action_plans": [ACTION_PLAN1]}, ), }, } fake_responses_pagination = { '/v1/action_plans': { 'GET': ( {}, {"action_plans": [ACTION_PLAN1], "next": "http://127.0.0.1:9322/v1/action_plans/?limit=1"} ), }, '/v1/action_plans/?limit=1': { 'GET': ( {}, {"action_plans": [ACTION_PLAN2]} ), }, '/v1/action_plans/?marker=f8e47706-efcf-49a4-a5c4-af604eb492f2': { 'GET': ( {}, {"action_plans": [ACTION_PLAN2]} ), }, } fake_responses_sorting = { '/v1/action_plans/?sort_key=updated_at': { 'GET': ( {}, {"action_plans": [ACTION_PLAN2, ACTION_PLAN1]} ), }, '/v1/action_plans/?sort_dir=desc': { 'GET': ( {}, {"action_plans": [ACTION_PLAN2, ACTION_PLAN1]} ), }, } fake_responses_cancel = { '/v1/action_plans/%s' % ACTION_PLAN1['uuid']: { 'GET': ( {}, [ONGOING_ACTION_PLAN], ), 'PATCH': ( {}, CANCELLING_ACTION_PLAN, ), }, '/v1/action_plans/%s' % ACTION_PLAN2['uuid']: { 'GET': ( {}, [ACTION_PLAN2], ), 'PATCH': ( {}, CANCELD_ACTION_PLAN, ), }, } class ActionPlanManagerTest(testtools.TestCase): def setUp(self): super(ActionPlanManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = watcherclient.v1.action_plan.ActionPlanManager(self.api) def test_action_plans_list(self): action_plans = self.mgr.list() expect = [ ('GET', '/v1/action_plans', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(action_plans)) def test_action_plans_list_detail(self): action_plans = self.mgr.list(detail=True) expect = [ ('GET', '/v1/action_plans/detail', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(action_plans)) def test_action_plans_list_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.action_plan.ActionPlanManager(self.api) action_plans = self.mgr.list(limit=1) expect = [ ('GET', '/v1/action_plans/?limit=1', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(action_plans, matchers.HasLength(1)) def test_action_plans_list_pagination_no_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.action_plan.ActionPlanManager(self.api) action_plans = self.mgr.list(limit=0) expect = [ ('GET', '/v1/action_plans', {}, None), ('GET', '/v1/action_plans/?limit=1', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertThat(action_plans, matchers.HasLength(2)) def test_action_plans_list_marker(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.action_plan.ActionPlanManager(self.api) action_plans = self.mgr.list( marker='f8e47706-efcf-49a4-a5c4-af604eb492f2') expect = [ ('GET', '/v1/action_plans/?' 'marker=f8e47706-efcf-49a4-a5c4-af604eb492f2', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(action_plans, matchers.HasLength(1)) def test_action_plans_list_sort_key(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = watcherclient.v1.action_plan.ActionPlanManager(self.api) action_plans = self.mgr.list(sort_key='updated_at') expect = [ ('GET', '/v1/action_plans/?sort_key=updated_at', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(action_plans)) def test_action_plans_list_sort_dir(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = watcherclient.v1.action_plan.ActionPlanManager(self.api) action_plans = self.mgr.list(sort_dir='desc') expect = [ ('GET', '/v1/action_plans/?sort_dir=desc', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(action_plans)) def test_action_plans_show(self): action_plan = self.mgr.get(ACTION_PLAN1['uuid']) expect = [ ('GET', '/v1/action_plans/%s' % ACTION_PLAN1['uuid'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(ACTION_PLAN1['uuid'], action_plan.uuid) def test_action_plans_get_index_error(self): # verify this method will return None when meet indexError fake_uuid = generate_uuid() self.api.json_request = mock.Mock(return_value=('404', [])) self.assertIsNone(self.mgr.get(fake_uuid)) def test_action_plans_delete(self): # verity that action plan was successfully deleted self.api.raw_request = mock.Mock(return_value=('204', [])) self.assertIsNone(self.mgr.delete(ACTION_PLAN1['uuid'])) # verity that delete a wrong action plan will raise Exception fake_uuid = generate_uuid() self.api.raw_request = mock.Mock( side_effect=HTTPClientError('404 Not Found')) self.assertRaises(HTTPClientError, self.mgr.delete, fake_uuid) def test_action_plans_cancel(self): # verity that the status of action plan can be converted from # 'ONGOING' to 'CANCELLING' self.api = utils.FakeAPI(fake_responses_cancel) self.mgr = watcherclient.v1.action_plan.ActionPlanManager(self.api) patch = {'op': 'replace', 'value': 'CANCELLING', 'path': '/state'} action_plan = self.mgr.cancel(action_plan_id=ACTION_PLAN1['uuid']) expect = [ ('GET', '/v1/action_plans/%s' % ACTION_PLAN1['uuid'], {}, None), ('PATCH', '/v1/action_plans/%s' % ACTION_PLAN1['uuid'], {}, [patch]) ] self.assertEqual(expect, self.api.calls) self.assertEqual('CANCELLING', action_plan.state) # verity that the status of action plan can be converted from # 'RECOMMENDED' to 'CANCELLED' patch['value'] = 'CANCELLED' self.api.calls = [] action_plan = self.mgr.cancel(action_plan_id=ACTION_PLAN2['uuid']) expect = [ ('GET', '/v1/action_plans/%s' % ACTION_PLAN2['uuid'], {}, None), ('PATCH', '/v1/action_plans/%s' % ACTION_PLAN2['uuid'], {}, [patch]) ] self.assertEqual(expect, self.api.calls) self.assertEqual('CANCELLED', action_plan.state) def test_action_plan_update(self): patch = {'op': 'replace', 'value': NEW_STATE, 'path': '/state'} action_plan = self.mgr.update(action_plan_id=ACTION_PLAN1['uuid'], patch=patch) expect = [ ('PATCH', '/v1/action_plans/%s' % ACTION_PLAN1['uuid'], {}, patch), ] self.assertEqual(expect, self.api.calls) self.assertEqual(NEW_STATE, action_plan.state) def test_action_plan_start(self): action_plan = self.mgr.start(ACTION_PLAN1['uuid']) expect = [('POST', '/v1/action_plans/%s/start' % ACTION_PLAN1['uuid'], {}, {})] self.assertEqual(expect, self.api.calls) self.assertEqual(NEW_STATE, action_plan.state) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_action_plan_shell.py0000664000175000017500000003115700000000000030404 0ustar00zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import datetime import io from unittest import mock from oslo_utils.uuidutils import generate_uuid from watcherclient import exceptions from watcherclient import shell from watcherclient.tests.unit.v1 import base from watcherclient import v1 as resource from watcherclient.v1 import resource_fields ACTION_PLAN_1 = { 'uuid': 'd9d9978e-6db5-4a05-8eab-1531795d7004', 'audit_uuid': '770ef053-ecb3-48b0-85b5-d55a2dbc6588', 'strategy_name': 'dummy', 'state': 'RECOMMENDED', 'efficacy_indicators': [{'description': 'Indicator 1', 'name': 'indicator1', 'unit': '%'}], 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'global_efficacy': [ {"value": 99, "unit": "%", "name": "dummy_global_efficacy", "description": "Dummy Global Efficacy"}, {"value": 75, "unit": "%", "name": "dummy_global_efficacy2", "description": "Dummy Global Efficacy2"} ], 'deleted_at': None, 'hostname': '' } ACTION_PLAN_2 = { 'uuid': 'd6363285-5afa-4a26-96f2-89441e335765', 'audit_uuid': '239f02a5-9649-4e14-9d33-ac2bf67cb755', 'strategy_name': 'dummy', 'state': 'RECOMMENDED', 'created_at': datetime.datetime.now().isoformat(), 'efficacy_indicators': [{'description': 'Indicator 2', 'name': 'indicator2', 'unit': '%'}], 'updated_at': None, 'global_efficacy': [{ "value": 87, "unit": "%", "name": "dummy_global_efficacy", "description": "Dummy Global Efficacy", }], 'deleted_at': None, 'hostname': '' } class ActionPlanShellTest(base.CommandTestCase): SHORT_LIST_FIELDS = resource_fields.ACTION_PLAN_SHORT_LIST_FIELDS SHORT_LIST_FIELD_LABELS = ( resource_fields.ACTION_PLAN_SHORT_LIST_FIELD_LABELS) FIELDS = resource_fields.ACTION_PLAN_FIELDS FIELD_LABELS = resource_fields.ACTION_PLAN_FIELD_LABELS GLOBAL_EFFICACY_FIELDS = resource_fields.GLOBAL_EFFICACY_FIELDS def setUp(self): super(self.__class__, self).setUp() p_audit_manager = mock.patch.object(resource, 'AuditManager') p_audit_template_manager = mock.patch.object( resource, 'ActionPlanManager') p_action_plan_manager = mock.patch.object( resource, 'ActionPlanManager') self.m_audit_mgr_cls = p_audit_manager.start() self.m_audit_template_mgr_cls = p_audit_template_manager.start() self.m_action_plan_mgr_cls = p_action_plan_manager.start() self.addCleanup(p_audit_manager.stop) self.addCleanup(p_audit_template_manager.stop) self.addCleanup(p_action_plan_manager.stop) self.m_audit_mgr = mock.Mock() self.m_audit_template_mgr = mock.Mock() self.m_action_plan_mgr = mock.Mock() self.m_audit_mgr_cls.return_value = self.m_audit_mgr self.m_audit_template_mgr_cls.return_value = self.m_audit_template_mgr self.m_action_plan_mgr_cls.return_value = self.m_action_plan_mgr self.stdout = io.StringIO() self.cmd = shell.WatcherShell(stdout=self.stdout) def test_do_action_plan_list(self): action_plan1 = resource.ActionPlan(mock.Mock(), ACTION_PLAN_1) action_plan2 = resource.ActionPlan(mock.Mock(), ACTION_PLAN_2) self.m_action_plan_mgr.list.return_value = [ action_plan1, action_plan2] exit_code, results = self.run_cmd('actionplan list') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(action_plan1, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS), self.resource_as_dict(action_plan2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.assertEqual(action_plan1.global_efficacy, results[0]['Global efficacy']) self.assertEqual(action_plan2.global_efficacy, results[1]['Global efficacy']) def test_do_action_plan_list_by_table(self): action_plan1 = resource.ActionPlan(mock.Mock(), ACTION_PLAN_1) action_plan2 = resource.ActionPlan(mock.Mock(), ACTION_PLAN_2) self.m_action_plan_mgr.list.return_value = [ action_plan1, action_plan2] exit_code, results = self.run_cmd('actionplan list', 'table') self.assertEqual(0, exit_code) self.assertIn(ACTION_PLAN_1['uuid'], results) self.assertIn(ACTION_PLAN_2['uuid'], results) self.m_action_plan_mgr.list.assert_called_once_with(detail=False) def test_do_action_plan_list_detail(self): action_plan1 = resource.ActionPlan(mock.Mock(), ACTION_PLAN_1) action_plan2 = resource.ActionPlan(mock.Mock(), ACTION_PLAN_2) self.m_action_plan_mgr.list.return_value = [ action_plan1, action_plan2] exit_code, results = self.run_cmd('actionplan list --detail') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(action_plan1, self.FIELDS, self.FIELD_LABELS), self.resource_as_dict(action_plan2, self.FIELDS, self.FIELD_LABELS)], results) self.assertEqual(action_plan1.global_efficacy, results[0]['Global efficacy']) self.assertEqual(action_plan2.global_efficacy, results[1]['Global efficacy']) self.m_action_plan_mgr.list.assert_called_once_with(detail=True) def test_do_action_plan_list_filter_by_audit(self): action_plan1 = resource.ActionPlan(mock.Mock(), ACTION_PLAN_1) self.m_action_plan_mgr.list.return_value = [action_plan1] exit_code, results = self.run_cmd( 'actionplan list --audit ' '770ef053-ecb3-48b0-85b5-d55a2dbc6588') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(action_plan1, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_action_plan_mgr.list.assert_called_once_with( detail=False, audit='770ef053-ecb3-48b0-85b5-d55a2dbc6588', ) def test_do_action_plan_show_by_uuid(self): action_plan = resource.ActionPlan(mock.Mock(), ACTION_PLAN_1) self.m_action_plan_mgr.get.return_value = action_plan exit_code, result = self.run_cmd( 'actionplan show d9d9978e-6db5-4a05-8eab-1531795d7004') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict( action_plan, self.FIELDS, self.FIELD_LABELS), result) self.assertEqual(action_plan.global_efficacy, result['Global efficacy']) self.m_action_plan_mgr.get.assert_called_once_with( 'd9d9978e-6db5-4a05-8eab-1531795d7004') def test_do_action_plan_show_by_not_uuid(self): self.m_action_plan_mgr.get.side_effect = exceptions.HTTPNotFound exit_code, result = self.run_cmd( 'actionplan show not_uuid', formatting=None) self.assertEqual(1, exit_code) self.assertEqual('', result) def test_do_action_plan_show_by_random_uuid(self): # verify that show a wrong actionplan will raise Exception self.m_action_plan_mgr.get.side_effect = exceptions.HTTPNotFound fake_uuid = generate_uuid() exit_code, result = self.run_cmd( 'actionplan show {}'.format(fake_uuid), formatting=None) self.assertEqual(1, exit_code) self.assertEqual('', result) self.m_action_plan_mgr.get.assert_called_once_with(fake_uuid) def test_do_action_plan_show_uuid_by_table(self): # verify that show an actionplan can be in a 'table' format action_plan = resource.ActionPlan(mock.Mock(), ACTION_PLAN_1) self.m_action_plan_mgr.get.return_value = action_plan exit_code, result = self.run_cmd( 'actionplan show d9d9978e-6db5-4a05-8eab-1531795d7004', formatting='table') self.assertEqual(0, exit_code) self.assertIn(ACTION_PLAN_1['uuid'], result) self.m_action_plan_mgr.get.assert_called_once_with( 'd9d9978e-6db5-4a05-8eab-1531795d7004') def test_do_action_plan_delete(self): self.m_action_plan_mgr.delete.return_value = '' exit_code, result = self.run_cmd( 'actionplan delete 5869da81-4876-4687-a1ed-12cd64cf53d9', formatting=None) self.assertEqual(0, exit_code) self.assertEqual('', result) self.m_action_plan_mgr.delete.assert_called_once_with( '5869da81-4876-4687-a1ed-12cd64cf53d9') def test_do_action_plan_delete_not_uuid(self): exit_code, result = self.run_cmd( 'actionplan delete not_uuid', formatting=None) self.assertEqual(1, exit_code) self.assertEqual('', result) def test_do_action_plan_delete_multiple(self): self.m_action_plan_mgr.delete.return_value = '' exit_code, result = self.run_cmd( 'actionplan delete 5869da81-4876-4687-a1ed-12cd64cf53d9 ' 'c20627fa-ea70-4d56-ae15-4106358f773b', formatting=None) self.assertEqual(0, exit_code) self.assertEqual('', result) self.m_action_plan_mgr.delete.assert_any_call( '5869da81-4876-4687-a1ed-12cd64cf53d9') self.m_action_plan_mgr.delete.assert_any_call( 'c20627fa-ea70-4d56-ae15-4106358f773b') def test_do_action_plan_update(self): action_plan = resource.ActionPlan(mock.Mock(), ACTION_PLAN_1) self.m_action_plan_mgr.update.return_value = action_plan exit_code, result = self.run_cmd( 'actionplan update 5869da81-4876-4687-a1ed-12cd64cf53d9 ' 'replace state=CANCELLED') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(action_plan, self.FIELDS, self.FIELD_LABELS), result) self.m_action_plan_mgr.update.assert_called_once_with( '5869da81-4876-4687-a1ed-12cd64cf53d9', [{'op': 'replace', 'path': '/state', 'value': 'CANCELLED'}]) def test_do_action_plan_update_not_uuid(self): exit_code, result = self.run_cmd( 'actionplan update not_uuid ' 'replace state=CANCELLED', formatting=None) self.assertEqual(1, exit_code) self.assertEqual('', result) def test_do_action_plan_start(self): action_plan = resource.ActionPlan(mock.Mock(), ACTION_PLAN_1) self.m_action_plan_mgr.start.return_value = action_plan exit_code, result = self.run_cmd( 'actionplan start 5869da81-4876-4687-a1ed-12cd64cf53d9') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(action_plan, self.FIELDS, self.FIELD_LABELS), result) self.m_action_plan_mgr.start.assert_called_once_with( '5869da81-4876-4687-a1ed-12cd64cf53d9') def test_do_action_plan_start_not_uuid(self): exit_code, result = self.run_cmd( 'actionplan start not_uuid', formatting=None) self.assertEqual(1, exit_code) self.assertEqual('', result) def test_do_action_plan_cancel(self): action_plan = resource.ActionPlan(mock.Mock(), ACTION_PLAN_1) self.m_action_plan_mgr.cancel.return_value = action_plan exit_code, result = self.run_cmd( 'actionplan cancel 5869da81-4876-4687-a1ed-12cd64cf53d9') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(action_plan, self.FIELDS, self.FIELD_LABELS), result) self.m_action_plan_mgr.cancel.assert_called_once_with( '5869da81-4876-4687-a1ed-12cd64cf53d9') def test_do_action_plan_cancel_not_uuid(self): exit_code, result = self.run_cmd( 'actionplan cancel not_uuid', formatting=None) self.assertEqual(1, exit_code) self.assertEqual('', result) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_action_shell.py0000664000175000017500000001440700000000000027371 0ustar00zuulzuul00000000000000# # Copyright 2013 IBM Corp # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import io from unittest import mock from watcherclient import exceptions from watcherclient import shell from watcherclient.tests.unit.v1 import base from watcherclient import v1 as resource from watcherclient.v1 import resource_fields ACTION_1 = { 'uuid': '770ef053-ecb3-48b0-85b5-d55a2dbc6588', 'action_plan_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', 'state': 'PENDING', 'action_type': 'migrate', 'parents': ['239f02a5-9649-4e14-9d33-ac2bf67cb755'], 'input_parameters': {"test": 1}, 'description': 'test', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } ACTION_2 = { 'uuid': '239f02a5-9649-4e14-9d33-ac2bf67cb755', 'action_plan_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', 'state': 'PENDING', 'action_type': 'migrate', 'parents': ['67653274-eb24-c7ba-70f6-a84e73d80843'], 'input_parameters': {"test": 2}, 'description': 'test', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } ACTION_3 = { 'uuid': '67653274-eb24-c7ba-70f6-a84e73d80843', 'action_plan_uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea', 'parents': [], 'state': 'PENDING', 'action_type': 'sleep', 'description': 'test', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } ACTION_PLAN_1 = { 'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea', 'action': '770ef053-ecb3-48b0-85b5-d55a2dbc6588', 'state': 'RECOMMENDED', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } class ActionShellTest(base.CommandTestCase): SHORT_LIST_FIELDS = resource_fields.ACTION_SHORT_LIST_FIELDS SHORT_LIST_FIELD_LABELS = resource_fields.ACTION_SHORT_LIST_FIELD_LABELS FIELDS = resource_fields.ACTION_FIELDS FIELD_LABELS = resource_fields.ACTION_FIELD_LABELS def setUp(self): super(self.__class__, self).setUp() p_action_manager = mock.patch.object(resource, 'ActionManager') p_action_plan_manager = mock.patch.object( resource, 'ActionPlanManager') self.m_action_mgr_cls = p_action_manager.start() self.m_action_plan_mgr_cls = p_action_plan_manager.start() self.addCleanup(p_action_manager.stop) self.addCleanup(p_action_plan_manager.stop) self.m_action_mgr = mock.Mock() self.m_action_plan_mgr = mock.Mock() self.m_action_mgr_cls.return_value = self.m_action_mgr self.m_action_plan_mgr_cls.return_value = self.m_action_plan_mgr self.stdout = io.StringIO() self.cmd = shell.WatcherShell(stdout=self.stdout) def test_do_action_list(self): action1 = resource.Action(mock.Mock(), ACTION_1) action2 = resource.Action(mock.Mock(), ACTION_2) self.m_action_mgr.list.return_value = [action1, action2] exit_code, results = self.run_cmd('action list') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(action1, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS), self.resource_as_dict(action2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_action_mgr.list.assert_called_once_with(detail=False) def test_do_action_list_detail(self): action1 = resource.Action(mock.Mock(), ACTION_1) action2 = resource.Action(mock.Mock(), ACTION_2) self.m_action_mgr.list.return_value = [action1, action2] exit_code, results = self.run_cmd('action list --detail') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(action1, self.FIELDS, self.FIELD_LABELS), self.resource_as_dict(action2, self.FIELDS, self.FIELD_LABELS)], results) self.m_action_mgr.list.assert_called_once_with(detail=True) def test_do_action_list_marker(self): action2 = resource.Action(mock.Mock(), ACTION_2) action3 = resource.Action(mock.Mock(), ACTION_3) self.m_action_mgr.list.return_value = [ action2, action3] exit_code, results = self.run_cmd( 'action list --marker 770ef053-ecb3-48b0-85b5-d55a2dbc6588') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(action2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS), self.resource_as_dict(action3, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_action_mgr.list.assert_called_once_with( detail=False, marker='770ef053-ecb3-48b0-85b5-d55a2dbc6588') def test_do_action_show_by_uuid(self): action = resource.Action(mock.Mock(), ACTION_1) self.m_action_mgr.get.return_value = action self.m_action_plan_mgr.get.return_value = action exit_code, result = self.run_cmd( 'action show 5869da81-4876-4687-a1ed-12cd64cf53d9') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(action, self.FIELDS, self.FIELD_LABELS), result) self.m_action_mgr.get.assert_called_once_with( '5869da81-4876-4687-a1ed-12cd64cf53d9') def test_do_action_show_by_not_uuid(self): self.m_action_mgr.get.side_effect = exceptions.HTTPNotFound exit_code, result = self.run_cmd( 'action show not_uuid', formatting=None) self.assertEqual(1, exit_code) self.assertEqual('', result) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_audit.py0000664000175000017500000001611000000000000026024 0ustar00zuulzuul00000000000000# Copyright 2013 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 copy import testtools from testtools import matchers from watcherclient.tests.unit import utils import watcherclient.v1.audit AUDIT1 = { 'id': 1, 'uuid': '5869da81-4876-4687-a1ed-12cd64cf53d9', 'audit_type': 'ONE_SHOT', 'goal': 'fc087747-61be-4aad-8126-b701731ae836', 'strategy': '2cf86250-d309-4b81-818e-1537f3dba6e5', } AUDIT2 = { 'id': 2, 'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea', 'audit_type': 'ONE_SHOT', 'goal': 'fc087747-61be-4aad-8126-b701731ae836', 'strategy': None, } CREATE_AUDIT = copy.deepcopy(AUDIT1) del CREATE_AUDIT['id'] del CREATE_AUDIT['uuid'] UPDATED_AUDIT1 = copy.deepcopy(AUDIT1) NEW_STATE = 'SUCCESS' UPDATED_AUDIT1['state'] = NEW_STATE fake_responses = { '/v1/audits': { 'GET': ( {}, {"audits": [AUDIT1]}, ), 'POST': ( {}, CREATE_AUDIT, ), }, '/v1/audits/detail': { 'GET': ( {}, {"audits": [AUDIT1]}, ), }, '/v1/audits/%s' % AUDIT1['uuid']: { 'GET': ( {}, AUDIT1, ), 'DELETE': ( {}, None, ), 'PATCH': ( {}, UPDATED_AUDIT1, ), }, '/v1/audits/detail?uuid=%s' % AUDIT1['uuid']: { 'GET': ( {}, {"audits": [AUDIT1]}, ), }, } fake_responses_pagination = { '/v1/audits': { 'GET': ( {}, {"audits": [AUDIT1], "next": "http://127.0.0.1:9322/v1/audits/?limit=1"} ), }, '/v1/audits/?limit=1': { 'GET': ( {}, {"audits": [AUDIT2]} ), }, } fake_responses_sorting = { '/v1/audits/?sort_key=updated_at': { 'GET': ( {}, {"audits": [AUDIT2, AUDIT1]} ), }, '/v1/audits/?sort_dir=desc': { 'GET': ( {}, {"audits": [AUDIT2, AUDIT1]} ), }, } fake_responses_goal = { '/v1/audits/?goal=dummy': { 'GET': ( {}, {"audits": [AUDIT2, AUDIT1]} ), }, } fake_responses_strategy = { '/v1/audits/?strategy=dummy': { 'GET': ( {}, {"audits": [AUDIT1]} ), }, } fake_responses_marker = { '/v1/audits/?marker=5869da81-4876-4687-a1ed-12cd64cf53d9': { 'GET': ( {}, {"audits": [AUDIT2]} ), }, } class AuditManagerTest(testtools.TestCase): def setUp(self): super(AuditManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = watcherclient.v1.audit.AuditManager(self.api) def test_audits_list(self): audits = self.mgr.list() expect = [ ('GET', '/v1/audits', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(audits)) def test_audits_list_detail(self): audits = self.mgr.list(detail=True) expect = [ ('GET', '/v1/audits/detail', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(audits)) def test_audits_list_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.audit.AuditManager(self.api) audits = self.mgr.list(limit=1) expect = [ ('GET', '/v1/audits/?limit=1', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(audits, matchers.HasLength(1)) def test_audits_list_pagination_no_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.audit.AuditManager(self.api) audits = self.mgr.list(limit=0) expect = [ ('GET', '/v1/audits', {}, None), ('GET', '/v1/audits/?limit=1', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertThat(audits, matchers.HasLength(2)) def test_audits_list_sort_key(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = watcherclient.v1.audit.AuditManager(self.api) audits = self.mgr.list(sort_key='updated_at') expect = [ ('GET', '/v1/audits/?sort_key=updated_at', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(audits)) def test_audits_list_sort_dir(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = watcherclient.v1.audit.AuditManager(self.api) audits = self.mgr.list(sort_dir='desc') expect = [ ('GET', '/v1/audits/?sort_dir=desc', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(audits)) def test_audits_list_goal(self): self.api = utils.FakeAPI(fake_responses_goal) self.mgr = watcherclient.v1.audit.AuditManager(self.api) audits = self.mgr.list(goal='dummy') expect = [ ('GET', '/v1/audits/?goal=dummy', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(audits)) def test_audits_list_strategy(self): self.api = utils.FakeAPI(fake_responses_strategy) self.mgr = watcherclient.v1.audit.AuditManager(self.api) audits = self.mgr.list(strategy='dummy') expect = [ ('GET', '/v1/audits/?strategy=dummy', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(audits)) def test_audits_list_marker(self): self.api = utils.FakeAPI(fake_responses_marker) self.mgr = watcherclient.v1.audit.AuditManager(self.api) audits = self.mgr.list(marker=AUDIT1['uuid']) expect = [ ('GET', '/v1/audits/?marker=5869da81-4876-4687-a1ed-12cd64cf53d9', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(audits)) def test_audits_show(self): audit = self.mgr.get(AUDIT1['uuid']) expect = [ ('GET', '/v1/audits/%s' % AUDIT1['uuid'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(AUDIT1['uuid'], audit.uuid) def test_create(self): audit = self.mgr.create(**CREATE_AUDIT) expect = [ ('POST', '/v1/audits', {}, CREATE_AUDIT), ] self.assertEqual(expect, self.api.calls) self.assertTrue(audit) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_audit_shell.py0000775000175000017500000006267100000000000027233 0ustar00zuulzuul00000000000000# # Copyright 2013 IBM Corp # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import io from unittest import mock from watcherclient import shell from watcherclient.tests.unit.v1 import base from watcherclient import v1 as resource AUDIT_TEMPLATE_1 = { 'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', 'name': 'at1', 'description': 'Audit Template 1 description', 'goal_uuid': 'fc087747-61be-4aad-8126-b701731ae836', 'strategy_uuid': '2cf86250-d309-4b81-818e-1537f3dba6e5', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } GOAL_1 = { 'uuid': "fc087747-61be-4aad-8126-b701731ae836", 'name': "SERVER_CONSOLIDATION", 'display_name': 'Server Consolidation', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } STRATEGY_1 = { 'uuid': '2cf86250-d309-4b81-818e-1537f3dba6e5', 'name': 'basic', 'display_name': 'Basic consolidation', 'goal_uuid': 'fc087747-61be-4aad-8126-b701731ae836', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } class AuditShellTest(base.CommandTestCase): AUDIT_1 = { 'uuid': '5869da81-4876-4687-a1ed-12cd64cf53d9', 'audit_type': 'ONESHOT', 'state': 'PENDING', 'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', 'audit_template_name': 'at1', 'goal_name': 'SERVER_CONSOLIDATION', 'strategy_name': 'basic', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, 'parameters': None, 'interval': None, 'scope': '', 'auto_trigger': False, 'next_run_time': None, 'name': 'my_audit1', 'hostname': '', } AUDIT_2 = { 'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea', 'audit_type': 'ONESHOT', 'state': 'PENDING', 'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', 'audit_template_name': 'at1', 'goal_name': 'fc087747-61be-4aad-8126-b701731ae836', 'strategy_name': 'auto', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, 'parameters': None, 'interval': None, 'scope': '', 'auto_trigger': False, 'next_run_time': None, 'name': 'my_audit2', 'hostname': '', } AUDIT_3 = { 'uuid': '43199d0e-0712-1213-9674-5ae2af8dhgte', 'audit_type': 'ONESHOT', 'state': 'PENDING', 'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', 'audit_template_name': 'at1', 'goal_name': None, 'strategy_name': 'auto', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, 'parameters': None, 'interval': 3600, 'scope': '', 'auto_trigger': True, 'next_run_time': None, 'name': 'my_audit3', 'hostname': '', } SHORT_LIST_FIELDS = ['uuid', 'name', 'audit_type', 'state', 'goal_name', 'strategy_name', 'auto_trigger'] SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Audit Type', 'State', 'Goal', 'Strategy', 'Auto Trigger'] FIELDS = ['uuid', 'name', 'created_at', 'updated_at', 'deleted_at', 'state', 'audit_type', 'parameters', 'interval', 'goal_name', 'strategy_name', 'scope', 'auto_trigger', 'next_run_time', 'hostname'] FIELD_LABELS = ['UUID', 'Name', 'Created At', 'Updated At', 'Deleted At', 'State', 'Audit Type', 'Parameters', 'Interval', 'Goal', 'Strategy', 'Audit Scope', 'Auto Trigger', 'Next Run Time', 'Hostname'] def setUp(self, os_infra_optim_api_version='1.0'): super(AuditShellTest, self).setUp( os_infra_optim_api_version=os_infra_optim_api_version) # goal mock p_goal_manager = mock.patch.object(resource, 'GoalManager') self.m_goal_mgr_cls = p_goal_manager.start() self.addCleanup(p_goal_manager.stop) self.m_goal_mgr = mock.Mock() self.m_goal_mgr_cls.return_value = self.m_goal_mgr # strategy mock p_strategy_manager = mock.patch.object(resource, 'StrategyManager') self.m_strategy_mgr_cls = p_strategy_manager.start() self.addCleanup(p_strategy_manager.stop) self.m_strategy_mgr = mock.Mock() self.m_strategy_mgr_cls.return_value = self.m_strategy_mgr p_audit_manager = mock.patch.object(resource, 'AuditManager') p_audit_template_manager = mock.patch.object( resource, 'AuditTemplateManager') self.m_audit_mgr_cls = p_audit_manager.start() self.m_audit_template_mgr_cls = p_audit_template_manager.start() self.addCleanup(p_audit_manager.stop) self.addCleanup(p_audit_template_manager.stop) self.m_audit_mgr = mock.Mock() self.m_audit_template_mgr = mock.Mock() self.m_audit_mgr_cls.return_value = self.m_audit_mgr self.m_audit_template_mgr_cls.return_value = self.m_audit_template_mgr # stdout mock self.stdout = io.StringIO() self.cmd = shell.WatcherShell(stdout=self.stdout) def test_do_audit_list(self): audit1 = resource.Audit(mock.Mock(), self.AUDIT_1) audit2 = resource.Audit(mock.Mock(), self.AUDIT_2) self.m_audit_mgr.list.return_value = [ audit1, audit2] exit_code, results = self.run_cmd('audit list') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(audit1, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS), self.resource_as_dict(audit2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_audit_mgr.list.assert_called_once_with(detail=False) def test_do_audit_list_marker(self): audit2 = resource.Audit(mock.Mock(), self.AUDIT_2) self.m_audit_mgr.list.return_value = [audit2] exit_code, results = self.run_cmd( 'audit list --marker 5869da81-4876-4687-a1ed-12cd64cf53d9') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(audit2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_audit_mgr.list.assert_called_once_with( detail=False, marker='5869da81-4876-4687-a1ed-12cd64cf53d9') def test_do_audit_list_detail(self): audit1 = resource.Audit(mock.Mock(), self.AUDIT_1) audit2 = resource.Audit(mock.Mock(), self.AUDIT_2) self.m_audit_mgr.list.return_value = [ audit1, audit2] exit_code, results = self.run_cmd('audit list --detail') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(audit1, self.FIELDS, self.FIELD_LABELS), self.resource_as_dict(audit2, self.FIELDS, self.FIELD_LABELS)], results) self.m_audit_mgr.list.assert_called_once_with(detail=True) def test_do_audit_show_by_uuid(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.get.return_value = audit exit_code, result = self.run_cmd( 'audit show 5869da81-4876-4687-a1ed-12cd64cf53d9') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.get.assert_called_once_with( '5869da81-4876-4687-a1ed-12cd64cf53d9') def test_do_audit_show_by_name(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.get.return_value = audit exit_code, result = self.run_cmd( 'audit show my_audit') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.get.assert_called_once_with( 'my_audit') def test_do_audit_delete(self): self.m_audit_mgr.delete.return_value = '' exit_code, result = self.run_cmd( 'audit delete 5869da81-4876-4687-a1ed-12cd64cf53d9', formatting=None) self.assertEqual(0, exit_code) self.assertEqual('', result) self.m_audit_mgr.delete.assert_called_once_with( '5869da81-4876-4687-a1ed-12cd64cf53d9') def test_do_audit_delete_by_name(self): self.m_audit_mgr.delete.return_value = '' exit_code, result = self.run_cmd( 'audit delete my_audit', formatting=None) self.assertEqual(0, exit_code) self.assertEqual('', result) self.m_audit_mgr.delete.assert_called_once_with( 'my_audit') def test_do_audit_delete_multiple(self): self.m_audit_mgr.delete.return_value = '' exit_code, result = self.run_cmd( 'audit delete 5869da81-4876-4687-a1ed-12cd64cf53d9 ' '5b157edd-5a7e-4aaa-b511-f7b33ec86e9f', formatting=None) self.assertEqual(0, exit_code) self.assertEqual('', result) self.m_audit_mgr.delete.assert_any_call( '5869da81-4876-4687-a1ed-12cd64cf53d9') self.m_audit_mgr.delete.assert_any_call( '5b157edd-5a7e-4aaa-b511-f7b33ec86e9f') def test_do_audit_update(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.update.return_value = audit exit_code, result = self.run_cmd( 'audit update 5869da81-4876-4687-a1ed-12cd64cf53d9 ' 'replace state=PENDING') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.update.assert_called_once_with( '5869da81-4876-4687-a1ed-12cd64cf53d9', [{'op': 'replace', 'path': '/state', 'value': 'PENDING'}]) def test_do_audit_update_by_name(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.update.return_value = audit exit_code, result = self.run_cmd( 'audit update my_audit replace state=PENDING') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.update.assert_called_once_with( 'my_audit', [{'op': 'replace', 'path': '/state', 'value': 'PENDING'}]) def test_do_audit_create_with_audit_template_uuid(self): audit = resource.Audit(mock.Mock(), self.AUDIT_3) audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) self.m_audit_template_mgr.get.return_value = audit_template self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -a f8e47706-efcf-49a4-a5c4-af604eb492f2') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2', audit_type='ONESHOT', auto_trigger=False ) def test_do_audit_create_with_audit_template_name(self): audit = resource.Audit(mock.Mock(), self.AUDIT_3) audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) self.m_audit_template_mgr.get.return_value = audit_template self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd('audit create -a at1') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2', auto_trigger=False, audit_type='ONESHOT' ) def test_do_audit_create_with_goal(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -g fc087747-61be-4aad-8126-b701731ae836') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', auto_trigger=False, audit_type='ONESHOT' ) def test_do_audit_create_with_goal_and_strategy(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -g fc087747-61be-4aad-8126-b701731ae836 -s ' '2cf86250-d309-4b81-818e-1537f3dba6e5') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', strategy='2cf86250-d309-4b81-818e-1537f3dba6e5', auto_trigger=False, audit_type='ONESHOT' ) def test_do_audit_create_with_type(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -g fc087747-61be-4aad-8126-b701731ae836 -t ONESHOT') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', auto_trigger=False, audit_type='ONESHOT' ) def test_do_audit_create_with_parameter(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -g fc087747-61be-4aad-8126-b701731ae836 -p para1=10 ' '-p para2=20') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', audit_type='ONESHOT', auto_trigger=False, parameters={'para1': 10, 'para2': 20} ) def test_do_audit_create_with_type_event(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -g fc087747-61be-4aad-8126-b701731ae836 -t EVENT') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', auto_trigger=False, audit_type='EVENT' ) def test_do_audit_create_with_type_continuous(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -g fc087747-61be-4aad-8126-b701731ae836 ' '-t CONTINUOUS -i 3600') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', audit_type='CONTINUOUS', auto_trigger=False, interval='3600' ) def test_do_audit_create_with_name(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -g fc087747-61be-4aad-8126-b701731ae836 ' '-t CONTINUOUS -i 3600 --name my_audit') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', audit_type='CONTINUOUS', auto_trigger=False, interval='3600', name='my_audit' ) class AuditShellTestv11(AuditShellTest): def setUp(self): super(AuditShellTestv11, self).setUp(os_infra_optim_api_version='1.1') v11 = dict(start_time=None, end_time=None) for audit in (self.AUDIT_1, self.AUDIT_2, self.AUDIT_3): audit.update(v11) self.FIELDS.extend(['start_time', 'end_time']) self.FIELD_LABELS.extend(['Start Time', 'End Time']) class AuditShellTestv12(AuditShellTest): def setUp(self): super(AuditShellTestv12, self).setUp(os_infra_optim_api_version='1.2') v11 = dict(start_time=None, end_time=None) v12 = dict(force=False) for audit in (self.AUDIT_1, self.AUDIT_2, self.AUDIT_3): audit.update(v11) audit.update(v12) self.FIELDS.extend(['start_time', 'end_time', 'force']) self.FIELD_LABELS.extend(['Start Time', 'End Time', 'Force']) def test_do_audit_create_with_force(self): audit = resource.Audit(mock.Mock(), self.AUDIT_3) audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) self.m_audit_template_mgr.get.return_value = audit_template self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -a f8e47706-efcf-49a4-a5c4-af604eb492f2 --force') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2', audit_type='ONESHOT', auto_trigger=False, force=True ) def test_do_audit_create_with_audit_template_uuid(self): audit = resource.Audit(mock.Mock(), self.AUDIT_3) audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) self.m_audit_template_mgr.get.return_value = audit_template self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -a f8e47706-efcf-49a4-a5c4-af604eb492f2') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2', audit_type='ONESHOT', auto_trigger=False, force=False ) def test_do_audit_create_with_audit_template_name(self): audit = resource.Audit(mock.Mock(), self.AUDIT_3) audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) self.m_audit_template_mgr.get.return_value = audit_template self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd('audit create -a at1') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2', auto_trigger=False, audit_type='ONESHOT', force=False ) def test_do_audit_create_with_goal(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -g fc087747-61be-4aad-8126-b701731ae836') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', auto_trigger=False, audit_type='ONESHOT', force=False ) def test_do_audit_create_with_goal_and_strategy(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -g fc087747-61be-4aad-8126-b701731ae836 -s ' '2cf86250-d309-4b81-818e-1537f3dba6e5') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', strategy='2cf86250-d309-4b81-818e-1537f3dba6e5', auto_trigger=False, audit_type='ONESHOT', force=False ) def test_do_audit_create_with_type(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -g fc087747-61be-4aad-8126-b701731ae836 -t ONESHOT') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', auto_trigger=False, audit_type='ONESHOT', force=False ) def test_do_audit_create_with_parameter(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -g fc087747-61be-4aad-8126-b701731ae836 -p para1=10 ' '-p para2=20') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', audit_type='ONESHOT', auto_trigger=False, parameters={'para1': 10, 'para2': 20}, force=False ) def test_do_audit_create_with_type_continuous(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -g fc087747-61be-4aad-8126-b701731ae836 ' '-t CONTINUOUS -i 3600') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', audit_type='CONTINUOUS', auto_trigger=False, interval='3600', force=False ) def test_do_audit_create_with_type_event(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -g fc087747-61be-4aad-8126-b701731ae836 -t EVENT') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', auto_trigger=False, audit_type='EVENT', force=False ) def test_do_audit_create_with_name(self): audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( 'audit create -g fc087747-61be-4aad-8126-b701731ae836 ' '-t CONTINUOUS -i 3600 --name my_audit') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', audit_type='CONTINUOUS', auto_trigger=False, interval='3600', name='my_audit', force=False ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_audit_template.py0000664000175000017500000003603600000000000027730 0ustar00zuulzuul00000000000000# Copyright 2013 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 copy from urllib import parse as urlparse from testtools import matchers from watcherclient.tests.unit import utils import watcherclient.v1.audit_template AUDIT_TMPL1 = { 'id': 1, 'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', 'name': 'Audit Template 1', 'description': 'Audit Template 1 description', 'goal_uuid': '7568667b-51fe-4087-9eb1-29b26891036f', 'goal_name': 'SERVER_CONSOLIDATION', 'strategy_uuid': 'bbe6b966-f98e-439b-a01a-17b9b3b8478b', 'strategy_name': 'server_consolidation', } AUDIT_TMPL2 = { 'id': 2, 'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea', 'name': 'Audit Template 2', 'description': 'Audit Template 2 description', 'goal_uuid': 'e75ee410-b32b-465f-88b5-4397705f9473', 'goal_name': 'DUMMY', 'strategy_uuid': 'ae99a4a4-acbc-4c67-abe1-e37128fac45d', 'strategy_name': 'dummy', } AUDIT_TMPL3 = { 'id': 3, 'uuid': '770ef053-ecb3-48b0-85b5-d55a2dbc6588', 'name': 'Audit Template 3', 'description': 'Audit Template 3 description', 'goal_uuid': '7568667b-51fe-4087-9eb1-29b26891036f', 'goal_name': 'SERVER_CONSOLIDATION', } CREATE_AUDIT_TEMPLATE = copy.deepcopy(AUDIT_TMPL1) del CREATE_AUDIT_TEMPLATE['id'] del CREATE_AUDIT_TEMPLATE['uuid'] del CREATE_AUDIT_TEMPLATE['goal_name'] del CREATE_AUDIT_TEMPLATE['strategy_name'] CREATE_AUDIT_TEMPLATE['goal'] = CREATE_AUDIT_TEMPLATE.pop('goal_uuid') CREATE_AUDIT_TEMPLATE['strategy'] = CREATE_AUDIT_TEMPLATE.pop('strategy_uuid') UPDATED_AUDIT_TMPL1 = copy.deepcopy(AUDIT_TMPL1) NEW_NAME = 'Audit Template_1 new name' UPDATED_AUDIT_TMPL1['name'] = NEW_NAME fake_responses = { '/v1/audit_templates': { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL1]}, ), 'POST': ( {}, CREATE_AUDIT_TEMPLATE, ), }, '/v1/audit_templates/detail': { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL1]}, ), }, '/v1/audit_templates/%s' % AUDIT_TMPL1['uuid']: { 'GET': ( {}, AUDIT_TMPL1, ), 'DELETE': ( {}, None, ), 'PATCH': ( {}, UPDATED_AUDIT_TMPL1, ), }, urlparse.quote('/v1/audit_templates/%s' % AUDIT_TMPL1['name']): { 'GET': ( {}, AUDIT_TMPL1, ), 'DELETE': ( {}, None, ), 'PATCH': ( {}, UPDATED_AUDIT_TMPL1, ), }, '/v1/audit_templates/detail?name=%s' % AUDIT_TMPL1['name']: { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL1]}, ), }, '/v1/audit_templates/?name=%s' % AUDIT_TMPL1['name']: { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL1]}, ), }, '/v1/audit_templates/detail?goal=%s' % AUDIT_TMPL1['goal_uuid']: { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL1, AUDIT_TMPL3]}, ), }, '/v1/audit_templates/?goal=%s' % AUDIT_TMPL1['goal_uuid']: { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL1, AUDIT_TMPL3]}, ), } } fake_responses_pagination = { '/v1/audit_templates': { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL1], "next": "http://127.0.0.1:9322/v1/audit_templates/?limit=1"} ), }, '/v1/audit_templates/?limit=1': { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL2]} ), }, } fake_responses_sorting = { '/v1/audit_templates/?sort_key=updated_at': { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL3, AUDIT_TMPL2, AUDIT_TMPL1]} ), }, '/v1/audit_templates/?sort_dir=desc': { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL3, AUDIT_TMPL2, AUDIT_TMPL1]} ), }, } fake_responses_marker = { '/v1/audit_templates/?marker=f8e47706-efcf-49a4-a5c4-af604eb492f2': { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL2, AUDIT_TMPL3]} ), }, } fake_responses_filter_by_goal_uuid = { '/v1/audit_templates/?goal=e75ee410-b32b-465f-88b5-4397705f9473': { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL2]} ), }, } fake_responses_filter_by_goal_name = { '/v1/audit_templates/?goal=DUMMY': { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL2]} ), }, } fake_responses_filter_by_strategy_uuid = { '/v1/audit_templates/?strategy=ae99a4a4-acbc-4c67-abe1-e37128fac45d': { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL2]} ), }, } fake_responses_filter_by_strategy_name = { '/v1/audit_templates/?strategy=dummy': { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL2]} ), }, } fake_responses_filter_by_strategy_and_goal_name = { '/v1/audit_templates/?goal=DUMMY&strategy=dummy': { 'GET': ( {}, {"audit_templates": [AUDIT_TMPL2]} ), }, } class AuditTemplateManagerTest(utils.BaseTestCase): def setUp(self): super(AuditTemplateManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = watcherclient.v1.audit_template.AuditTemplateManager( self.api) def test_audit_templates_list(self): audit_templates = self.mgr.list() expect = [ ('GET', '/v1/audit_templates', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(audit_templates)) def test_audit_templates_list_filter_by_name(self): audit_templates = self.mgr.list(name=AUDIT_TMPL1['name']) expect = [ ('GET', '/v1/audit_templates/?name=%s' % AUDIT_TMPL1['name'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(audit_templates)) def test_audit_templates_list_filter_by_goal_uuid(self): self.api = utils.FakeAPI(fake_responses_filter_by_goal_uuid) self.mgr = watcherclient.v1.audit_template.AuditTemplateManager( self.api) audit_templates = self.mgr.list( goal="e75ee410-b32b-465f-88b5-4397705f9473") expect = [ ('GET', '/v1/audit_templates/?goal=%s' % AUDIT_TMPL2['goal_uuid'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(audit_templates)) def test_audit_templates_list_filter_by_goal_name(self): self.api = utils.FakeAPI(fake_responses_filter_by_goal_name) self.mgr = watcherclient.v1.audit_template.AuditTemplateManager( self.api) audit_templates = self.mgr.list(goal="DUMMY") expect = [ ('GET', '/v1/audit_templates/?goal=%s' % AUDIT_TMPL2['goal_name'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(audit_templates)) def test_audit_templates_list_filter_by_strategy_uuid(self): self.api = utils.FakeAPI(fake_responses_filter_by_strategy_uuid) self.mgr = watcherclient.v1.audit_template.AuditTemplateManager( self.api) audit_templates = self.mgr.list( strategy="ae99a4a4-acbc-4c67-abe1-e37128fac45d") expect = [ ('GET', '/v1/audit_templates/?strategy=%s' % ( AUDIT_TMPL2['strategy_uuid']), {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(audit_templates)) def test_audit_templates_list_filter_by_strategy_name(self): self.api = utils.FakeAPI(fake_responses_filter_by_strategy_name) self.mgr = watcherclient.v1.audit_template.AuditTemplateManager( self.api) audit_templates = self.mgr.list(strategy="dummy") expect = [ ('GET', '/v1/audit_templates/?strategy=%s' % ( AUDIT_TMPL2['strategy_name']), {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(audit_templates)) def test_audit_templates_list_filter_by_goal_and_strategy_name(self): self.api = utils.FakeAPI( fake_responses_filter_by_strategy_and_goal_name) self.mgr = watcherclient.v1.audit_template.AuditTemplateManager( self.api) audit_templates = self.mgr.list(goal="DUMMY", strategy="dummy") expect = [ ('GET', '/v1/audit_templates/?goal=%s&strategy=%s' % ( AUDIT_TMPL2['goal_name'], AUDIT_TMPL2['strategy_name']), {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(audit_templates)) def test_audit_templates_list_detail(self): audit_templates = self.mgr.list(detail=True) expect = [ ('GET', '/v1/audit_templates/detail', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(audit_templates)) def test_audit_templates_list_by_name_detail(self): audit_templates = self.mgr.list(name=AUDIT_TMPL1['name'], detail=True) expect = [ ('GET', '/v1/audit_templates/detail?name=%s' % AUDIT_TMPL1['name'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(audit_templates)) def test_audit_templates_list_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.audit_template.AuditTemplateManager( self.api) audit_templates = self.mgr.list(limit=1) expect = [ ('GET', '/v1/audit_templates/?limit=1', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(audit_templates, matchers.HasLength(1)) def test_audit_templates_list_pagination_no_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.audit_template.AuditTemplateManager( self.api) audit_templates = self.mgr.list(limit=0) expect = [ ('GET', '/v1/audit_templates', {}, None), ('GET', '/v1/audit_templates/?limit=1', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertThat(audit_templates, matchers.HasLength(2)) def test_audit_templates_list_sort_key(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = watcherclient.v1.audit_template.AuditTemplateManager( self.api) audit_templates = self.mgr.list(sort_key='updated_at') expect = [ ('GET', '/v1/audit_templates/?sort_key=updated_at', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(3, len(audit_templates)) def test_audit_templates_list_sort_dir(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = watcherclient.v1.audit_template.AuditTemplateManager( self.api) audit_templates = self.mgr.list(sort_dir='desc') expect = [ ('GET', '/v1/audit_templates/?sort_dir=desc', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(3, len(audit_templates)) def test_audit_templates_list_marker(self): self.api = utils.FakeAPI(fake_responses_marker) self.mgr = watcherclient.v1.audit_template.AuditTemplateManager( self.api) audit_templates = self.mgr.list(marker=AUDIT_TMPL1['uuid']) expect_url = '/v1/audit_templates/?marker=%s' % AUDIT_TMPL1['uuid'] expect = [ ('GET', expect_url, {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(audit_templates)) def test_audit_templates_show(self): audit_template = self.mgr.get(AUDIT_TMPL1['uuid']) expect = [ ('GET', '/v1/audit_templates/%s' % AUDIT_TMPL1['uuid'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(AUDIT_TMPL1['uuid'], audit_template.uuid) self.assertEqual(AUDIT_TMPL1['name'], audit_template.name) self.assertEqual(AUDIT_TMPL1['description'], audit_template.description) self.assertEqual(AUDIT_TMPL1['goal_uuid'], audit_template.goal_uuid) self.assertEqual(AUDIT_TMPL1['strategy_uuid'], audit_template.strategy_uuid) def test_audit_templates_show_by_name(self): audit_template = self.mgr.get(urlparse.quote(AUDIT_TMPL1['name'])) expect = [ ('GET', urlparse.quote('/v1/audit_templates/%s' % AUDIT_TMPL1['name']), {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(AUDIT_TMPL1['uuid'], audit_template.uuid) self.assertEqual(AUDIT_TMPL1['name'], audit_template.name) self.assertEqual(AUDIT_TMPL1['description'], audit_template.description) self.assertEqual(AUDIT_TMPL1['goal_uuid'], audit_template.goal_uuid) self.assertEqual(AUDIT_TMPL1['strategy_uuid'], audit_template.strategy_uuid) def test_create(self): audit_template = self.mgr.create(**CREATE_AUDIT_TEMPLATE) expect = [ ('POST', '/v1/audit_templates', {}, CREATE_AUDIT_TEMPLATE), ] self.assertEqual(expect, self.api.calls) self.assertTrue(audit_template) def test_delete(self): audit_template = self.mgr.delete(audit_template_id=AUDIT_TMPL1['uuid']) expect = [ ('DELETE', '/v1/audit_templates/%s' % AUDIT_TMPL1['uuid'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertIsNone(audit_template) def test_update(self): patch = {'op': 'replace', 'value': NEW_NAME, 'path': '/name'} audit_template = self.mgr.update(audit_template_id=AUDIT_TMPL1['uuid'], patch=patch) expect = [ ('PATCH', '/v1/audit_templates/%s' % AUDIT_TMPL1['uuid'], {}, patch), ] self.assertEqual(expect, self.api.calls) self.assertEqual(NEW_NAME, audit_template.name) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_audit_template_shell.py0000664000175000017500000003525500000000000031121 0ustar00zuulzuul00000000000000# # Copyright 2013 IBM Corp # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import io from unittest import mock from watcherclient import shell from watcherclient.tests.unit.v1 import base from watcherclient import v1 as resource from watcherclient.v1 import resource_fields GOAL_1 = { 'uuid': "fc087747-61be-4aad-8126-b701731ae836", 'name': "SERVER_CONSOLIDATION", 'display_name': 'Server Consolidation', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } STRATEGY_1 = { 'uuid': '2cf86250-d309-4b81-818e-1537f3dba6e5', 'name': 'basic', 'display_name': 'Basic consolidation', 'goal_uuid': 'fc087747-61be-4aad-8126-b701731ae836', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } AUDIT_TEMPLATE_1 = { 'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', 'name': 'at1', 'description': 'Audit Template 1 description', 'goal_uuid': 'fc087747-61be-4aad-8126-b701731ae836', 'goal_name': 'SERVER_CONSOLIDATION', 'strategy_uuid': '2cf86250-d309-4b81-818e-1537f3dba6e5', 'strategy_name': 'basic', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, 'scope': [] } AUDIT_TEMPLATE_2 = { 'uuid': '2a60ca9b-09b0-40ff-8674-de8a36fc4bc8', 'name': 'at2', 'description': 'Audit Template 2', 'goal_uuid': 'fc087747-61be-4aad-8126-b701731ae836', 'goal_name': 'SERVER_CONSOLIDATION', 'strategy_uuid': None, 'strategy_name': None, 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, 'scope': [] } class AuditTemplateShellTest(base.CommandTestCase): SHORT_LIST_FIELDS = resource_fields.AUDIT_TEMPLATE_SHORT_LIST_FIELDS SHORT_LIST_FIELD_LABELS = ( resource_fields.AUDIT_TEMPLATE_SHORT_LIST_FIELD_LABELS) FIELDS = resource_fields.AUDIT_TEMPLATE_FIELDS FIELD_LABELS = resource_fields.AUDIT_TEMPLATE_FIELD_LABELS def setUp(self): super(self.__class__, self).setUp() # goal mock p_goal_manager = mock.patch.object(resource, 'GoalManager') self.m_goal_mgr_cls = p_goal_manager.start() self.addCleanup(p_goal_manager.stop) self.m_goal_mgr = mock.Mock() self.m_goal_mgr_cls.return_value = self.m_goal_mgr # strategy mock p_strategy_manager = mock.patch.object(resource, 'StrategyManager') self.m_strategy_mgr_cls = p_strategy_manager.start() self.addCleanup(p_strategy_manager.stop) self.m_strategy_mgr = mock.Mock() self.m_strategy_mgr_cls.return_value = self.m_strategy_mgr # audit template mock p_audit_template_manager = mock.patch.object( resource, 'AuditTemplateManager') self.m_audit_template_mgr_cls = p_audit_template_manager.start() self.addCleanup(p_audit_template_manager.stop) self.m_audit_template_mgr = mock.Mock() self.m_audit_template_mgr_cls.return_value = self.m_audit_template_mgr # stdout mock self.stdout = io.StringIO() self.cmd = shell.WatcherShell(stdout=self.stdout) def test_do_audit_template_list(self): audit_template1 = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) audit_template2 = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_2) self.m_audit_template_mgr.list.return_value = [ audit_template1, audit_template2] exit_code, results = self.run_cmd('audittemplate list') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(audit_template1, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS), self.resource_as_dict(audit_template2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_audit_template_mgr.list.assert_called_once_with(detail=False) def test_do_audit_template_list_marker(self): audit_template2 = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_2) self.m_audit_template_mgr.list.return_value = [audit_template2] exit_code, results = self.run_cmd( 'audittemplate list --marker ' 'f8e47706-efcf-49a4-a5c4-af604eb492f2') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(audit_template2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_audit_template_mgr.list.assert_called_once_with( detail=False, marker='f8e47706-efcf-49a4-a5c4-af604eb492f2') def test_do_audit_template_list_detail(self): audit_template1 = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) audit_template2 = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_2) self.m_audit_template_mgr.list.return_value = [ audit_template1, audit_template2] exit_code, results = self.run_cmd('audittemplate list --detail') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(audit_template1, self.FIELDS, self.FIELD_LABELS), self.resource_as_dict(audit_template2, self.FIELDS, self.FIELD_LABELS)], results) self.m_audit_template_mgr.list.assert_called_once_with(detail=True) def test_do_audit_template_list_filter_by_goal_uuid(self): audit_template1 = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) audit_template2 = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_2) self.m_audit_template_mgr.list.return_value = [ audit_template1, audit_template2] exit_code, results = self.run_cmd( 'audittemplate list --goal ' 'fc087747-61be-4aad-8126-b701731ae836') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(audit_template1, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS), self.resource_as_dict(audit_template2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_audit_template_mgr.list.assert_called_once_with( detail=False, goal='fc087747-61be-4aad-8126-b701731ae836', ) def test_do_audit_template_list_filter_by_goal_name(self): goal1 = resource.Goal(mock.Mock(), GOAL_1) strategy1 = resource.Strategy(mock.Mock(), STRATEGY_1) audit_template1 = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) audit_template2 = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_2) self.m_goal_mgr.get.return_value = goal1 self.m_strategy_mgr.get.return_value = strategy1 self.m_audit_template_mgr.list.return_value = [ audit_template1, audit_template2] exit_code, results = self.run_cmd( 'audittemplate list --goal SERVER_CONSOLIDATION') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(audit_template1, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS), self.resource_as_dict(audit_template2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_audit_template_mgr.list.assert_called_once_with( detail=False, goal='SERVER_CONSOLIDATION', ) def test_do_audit_template_list_filter_by_strategy_uuid(self): goal1 = resource.Goal(mock.Mock(), GOAL_1) strategy1 = resource.Strategy(mock.Mock(), STRATEGY_1) audit_template1 = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) self.m_goal_mgr.get.return_value = goal1 self.m_strategy_mgr.get.return_value = strategy1 self.m_audit_template_mgr.list.return_value = [audit_template1] exit_code, results = self.run_cmd( 'audittemplate list --strategy ' '2cf86250-d309-4b81-818e-1537f3dba6e5') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(audit_template1, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_audit_template_mgr.list.assert_called_once_with( detail=False, strategy='2cf86250-d309-4b81-818e-1537f3dba6e5', ) def test_do_audit_template_list_filter_by_strategy_name(self): audit_template1 = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) self.m_audit_template_mgr.list.return_value = [audit_template1] exit_code, results = self.run_cmd( 'audittemplate list --strategy ' 'basic') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(audit_template1, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_audit_template_mgr.list.assert_called_once_with( detail=False, strategy='basic', ) def test_do_audit_template_show_by_name(self): audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) self.m_audit_template_mgr.get.return_value = audit_template exit_code, result = self.run_cmd('audittemplate show at1') self.assertEqual(0, exit_code) self.assertEqual(self.resource_as_dict(audit_template, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_template_mgr.get.assert_called_once_with('at1') def test_do_audit_template_show_by_uuid(self): audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) self.m_audit_template_mgr.get.return_value = audit_template exit_code, result = self.run_cmd( 'audittemplate show f8e47706-efcf-49a4-a5c4-af604eb492f2') self.assertEqual(0, exit_code) self.assertEqual(self.resource_as_dict(audit_template, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_template_mgr.get.assert_called_once_with( 'f8e47706-efcf-49a4-a5c4-af604eb492f2') def test_do_audit_template_delete(self): self.m_audit_template_mgr.delete.return_value = '' exit_code, result = self.run_cmd( 'audittemplate delete f8e47706-efcf-49a4-a5c4-af604eb492f2', formatting=None) self.assertEqual(0, exit_code) self.assertEqual('', result) self.m_audit_template_mgr.delete.assert_called_once_with( 'f8e47706-efcf-49a4-a5c4-af604eb492f2') def test_do_audit_template_delete_multiple(self): self.m_audit_template_mgr.delete.return_value = '' exit_code, result = self.run_cmd( 'audittemplate delete f8e47706-efcf-49a4-a5c4-af604eb492f2 ' '92dfce2f-0a5e-473f-92b7-d92e21839e4d', formatting=None) self.assertEqual(0, exit_code) self.assertEqual('', result) self.m_audit_template_mgr.delete.assert_any_call( 'f8e47706-efcf-49a4-a5c4-af604eb492f2') self.m_audit_template_mgr.delete.assert_any_call( '92dfce2f-0a5e-473f-92b7-d92e21839e4d') def test_do_audit_template_update(self): audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) self.m_audit_template_mgr.update.return_value = audit_template exit_code, result = self.run_cmd( 'audittemplate update at1 replace description="New description"') self.assertEqual(0, exit_code) self.assertEqual(self.resource_as_dict(audit_template, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_template_mgr.update.assert_called_once_with( 'at1', [{'op': 'replace', 'path': '/description', 'value': 'New description'}]) def test_do_audit_template_create(self): audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) self.m_audit_template_mgr.create.return_value = audit_template exit_code, result = self.run_cmd( 'audittemplate create at1 fc087747-61be-4aad-8126-b701731ae836') self.assertEqual(0, exit_code) self.assertEqual(self.resource_as_dict(audit_template, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_template_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', name='at1') def test_do_audit_template_create_with_description(self): audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) self.m_audit_template_mgr.create.return_value = audit_template exit_code, result = self.run_cmd( 'audittemplate create at1 fc087747-61be-4aad-8126-b701731ae836 ' '-d "Audit Template 1 description"') self.assertEqual(0, exit_code) self.assertEqual(self.resource_as_dict(audit_template, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_template_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', name='at1', description='Audit Template 1 description') def test_do_audit_template_create_with_aggregate(self): audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) self.m_audit_template_mgr.create.return_value = audit_template exit_code, result = self.run_cmd( 'audittemplate create at1 fc087747-61be-4aad-8126-b701731ae836') self.assertEqual(0, exit_code) self.assertEqual(self.resource_as_dict(audit_template, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_template_mgr.create.assert_called_once_with( goal='fc087747-61be-4aad-8126-b701731ae836', name='at1') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_data_model.py0000664000175000017500000000437300000000000027017 0ustar00zuulzuul00000000000000# Copyright 2019 ZTE corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import testtools from watcherclient.tests.unit import utils import watcherclient.v1.data_model DATA_MODEL = { 'context': [{ "server_uuid": "1bf91464-9b41-428d-a11e-af691e5563bb", "server_name": "fake-name", "server_state": "active", "node_uuid": "253e5dd0-9384-41ab-af13-4f2c2ce26112", "node_hostname": "localhost.localdomain", }] } AUDIT = "81332bfc-36f8-444d-99e2-b7285d602528" fake_responses = { '/v1/data_model/?data_model_type=compute': { 'GET': ( {}, DATA_MODEL, ), }, '/v1/data_model/?audit_uuid=%s&data_model_type=compute' % AUDIT: { 'GET': ( {}, DATA_MODEL, ), }, } class DataModelManagerTest(testtools.TestCase): def setUp(self): super(DataModelManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = watcherclient.v1.data_model.DataModelManager(self.api) def test_data_model_list(self): data_model = self.mgr.list() expect = [ ('GET', '/v1/data_model/?data_model_type=compute', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(data_model.context)) def test_data_model_list_audit(self): data_model = self.mgr.list( audit='%s' % AUDIT) expect = [ ('GET', '/v1/data_model/?' 'audit_uuid=81332bfc-36f8-444d-99e2-b7285d602528' '&data_model_type=compute', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(data_model.context)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_data_model_shell.py0000664000175000017500000001043500000000000030202 0ustar00zuulzuul00000000000000# Copyright 2019 ZTE Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import io from unittest import mock from watcherclient import shell from watcherclient.tests.unit.v1 import base from watcherclient import v1 as resource from watcherclient.v1 import resource_fields DATA_MODEL = { 'context': [{ "server_uuid": "1bf91464-9b41-428d-a11e-af691e5563bb", "server_name": "fake-name", "server_state": "active", "server_vcpus": "1", "server_memory": "512", "server_disk": "1", "node_uuid": "253e5dd0-9384-41ab-af13-4f2c2ce26112", "node_hostname": "localhost.localdomain", "node_vcpus": "4", "node_vcpu_ratio": "16.0", "node_memory": "16383", "node_memory_ratio": "1.5", "node_disk": "37", "node_disk_ratio": "1.0", "node_state": "up", }] } LIST_RESULT = [{ "Server UUID": "1bf91464-9b41-428d-a11e-af691e5563bb", "Server Name": "fake-name", "Server Vcpus": "1", "Server Memory": "512", "Server Disk": "1", "Server State": "active", "Node UUID": "253e5dd0-9384-41ab-af13-4f2c2ce26112", "Node Host Name": "localhost.localdomain", "Node Vcpus": "4", "Node Vcpu Ratio": "16.0", "Node Memory": "16383", "Node Memory Ratio": "1.5", "Node Disk": "37", "Node Disk Ratio": "1.0", "Node State": "up", }] SHORT_LIST_RESULT = [{ "Server UUID": "1bf91464-9b41-428d-a11e-af691e5563bb", "Server Name": "fake-name", "Server State": "active", "Node UUID": "253e5dd0-9384-41ab-af13-4f2c2ce26112", "Node Host Name": "localhost.localdomain", }] class DataModelShellTest(base.CommandTestCase): SHORT_LIST_FIELDS = resource_fields.COMPUTE_MODEL_SHORT_LIST_FIELDS SHORT_LIST_FIELD_LABELS = ( resource_fields.COMPUTE_MODEL_SHORT_LIST_FIELD_LABELS) FIELDS = resource_fields.COMPUTE_MODEL_LIST_FIELDS FIELD_LABELS = resource_fields.COMPUTE_MODEL_LIST_FIELD_LABELS def setUp(self): super(self.__class__, self).setUp() p_data_model_manager = mock.patch.object( resource, 'DataModelManager') self.m_data_model_mgr_cls = p_data_model_manager.start() self.addCleanup(p_data_model_manager.stop) self.m_data_model_mgr = mock.Mock() self.m_data_model_mgr_cls.return_value = self.m_data_model_mgr self.stdout = io.StringIO() self.cmd = shell.WatcherShell(stdout=self.stdout) def test_do_data_model_list(self): data_model = resource.DataModel(mock.Mock(), DATA_MODEL) self.m_data_model_mgr.list.return_value = data_model exit_code, results = self.run_cmd('datamodel list') self.assertEqual(0, exit_code) expect_values = sorted(SHORT_LIST_RESULT[0].values()) result_values = sorted(results[0].values()) self.assertEqual(expect_values, result_values) def test_do_data_model_list_detail(self): data_model = resource.DataModel(mock.Mock(), DATA_MODEL) self.m_data_model_mgr.list.return_value = data_model exit_code, results = self.run_cmd('datamodel list --detail') self.assertEqual(0, exit_code) expect_values = sorted(LIST_RESULT[0].values()) result_values = sorted(results[0].values()) self.assertEqual(expect_values, result_values) def test_do_data_model_list_filter_by_audit(self): data_model = resource.DataModel(mock.Mock(), DATA_MODEL) self.m_data_model_mgr.list.return_value = data_model exit_code, results = self.run_cmd( 'datamodel list --audit ' '770ef053-ecb3-48b0-85b5-d55a2dbc6588') self.assertEqual(0, exit_code) expect_values = sorted(SHORT_LIST_RESULT[0].values()) result_values = sorted(results[0].values()) self.assertEqual(expect_values, result_values) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_goal.py0000664000175000017500000001264200000000000025646 0ustar00zuulzuul00000000000000# Copyright 2013 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 testtools from testtools import matchers from watcherclient.tests.unit import utils import watcherclient.v1.goal GOAL1 = { 'uuid': "fc087747-61be-4aad-8126-b701731ae836", 'name': "SERVER_CONSOLIDATION", 'display_name': 'Server Consolidation' } GOAL2 = { 'uuid': "407b03b1-63c6-49b2-adaf-4df5c0090047", 'name': "COST_OPTIMIZATION", 'display_name': 'Cost Optimization' } fake_responses = { '/v1/goals': { 'GET': ( {}, {"goals": [GOAL1]}, ), }, '/v1/goals/detail': { 'GET': ( {}, {"goals": [GOAL1]}, ) }, '/v1/goals/%s' % GOAL1['uuid']: { 'GET': ( {}, GOAL1, ), }, '/v1/goals/%s' % GOAL1['name']: { 'GET': ( {}, GOAL1, ), }, } fake_responses_pagination = { '/v1/goals': { 'GET': ( {}, {"goals": [GOAL1], "next": "http://127.0.0.1:9322/v1/goals/?limit=1"} ), }, '/v1/goals/?limit=1': { 'GET': ( {}, {"goals": [GOAL2]} ), }, } fake_responses_sorting = { '/v1/goals/?sort_key=id': { 'GET': ( {}, {"goals": [GOAL1, GOAL2]} ), }, '/v1/goals/?sort_dir=desc': { 'GET': ( {}, {"goals": [GOAL2, GOAL1]} ), }, } fake_responses_marker = { '/v1/goals/?marker=fc087747-61be-4aad-8126-b701731ae836': { 'GET': ( {}, {"goals": [GOAL2]} ), }, } class GoalManagerTest(testtools.TestCase): def setUp(self): super(GoalManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = watcherclient.v1.goal.GoalManager(self.api) def test_goals_list(self): goals = self.mgr.list() expect = [ ('GET', '/v1/goals', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(goals)) def test_goals_list_detail(self): goals = self.mgr.list(detail=True) expect = [ ('GET', '/v1/goals/detail', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(goals)) def test_goals_list_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.goal.GoalManager(self.api) goals = self.mgr.list(limit=1) expect = [ ('GET', '/v1/goals/?limit=1', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(goals, matchers.HasLength(1)) def test_goals_list_marker(self): self.api = utils.FakeAPI(fake_responses_marker) self.mgr = watcherclient.v1.goal.GoalManager(self.api) goals = self.mgr.list(marker=GOAL1['uuid']) expect = [ ('GET', '/v1/goals/?marker=fc087747-61be-4aad-8126-b701731ae836', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(goals)) def test_goals_list_pagination_no_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.goal.GoalManager(self.api) goals = self.mgr.list(limit=0) expect = [ ('GET', '/v1/goals', {}, None), ('GET', '/v1/goals/?limit=1', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertThat(goals, matchers.HasLength(2)) def test_goals_list_sort_key(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = watcherclient.v1.goal.GoalManager(self.api) goals = self.mgr.list(sort_key='id') expect = [ ('GET', '/v1/goals/?sort_key=id', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(goals)) def test_goals_list_sort_dir(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = watcherclient.v1.goal.GoalManager(self.api) goals = self.mgr.list(sort_dir='desc') expect = [ ('GET', '/v1/goals/?sort_dir=desc', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(goals)) def test_goals_show(self): goal = self.mgr.get(GOAL1['uuid']) expect = [ ('GET', '/v1/goals/%s' % GOAL1['uuid'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(GOAL1['uuid'], goal.uuid) def test_goals_show_by_name(self): goal = self.mgr.get(GOAL1['name']) expect = [ ('GET', '/v1/goals/%s' % GOAL1['name'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(GOAL1['name'], goal.name) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_goal_shell.py0000664000175000017500000001242500000000000027034 0ustar00zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import datetime import io from unittest import mock from watcherclient import shell from watcherclient.tests.unit.v1 import base from watcherclient import v1 as resource from watcherclient.v1 import resource_fields GOAL_1 = { 'uuid': "fc087747-61be-4aad-8126-b701731ae836", 'name': "SERVER_CONSOLIDATION", 'display_name': 'Server Consolidation', 'efficacy_specification': [ {'description': 'Indicator 1', 'name': 'indicator1', 'schema': 'Range(min=0, max=100, min_included=True, ' 'max_included=True, msg=None)', 'unit': '%'} ], 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } GOAL_2 = { 'uuid': "407b03b1-63c6-49b2-adaf-4df5c0090047", 'name': "COST_OPTIMIZATION", 'display_name': 'Cost Optimization', 'efficacy_specification': [ {'description': 'Indicator 2', 'name': 'indicator2', 'schema': 'Range(min=0, max=100, min_included=True, ' 'max_included=True, msg=None)', 'unit': '%'} ], 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } class GoalShellTest(base.CommandTestCase): SHORT_LIST_FIELDS = resource_fields.GOAL_SHORT_LIST_FIELDS SHORT_LIST_FIELD_LABELS = ( resource_fields.GOAL_SHORT_LIST_FIELD_LABELS) FIELDS = resource_fields.GOAL_FIELDS FIELD_LABELS = resource_fields.GOAL_FIELD_LABELS def setUp(self): super(self.__class__, self).setUp() p_goal_manager = mock.patch.object( resource, 'GoalManager') self.m_goal_mgr_cls = p_goal_manager.start() self.addCleanup(p_goal_manager.stop) self.m_goal_mgr = mock.Mock() self.m_goal_mgr_cls.return_value = self.m_goal_mgr self.stdout = io.StringIO() self.cmd = shell.WatcherShell(stdout=self.stdout) def test_do_goal_list(self): goal1 = resource.Goal(mock.Mock(), GOAL_1) goal2 = resource.Goal(mock.Mock(), GOAL_2) self.m_goal_mgr.list.return_value = [ goal1, goal2] exit_code, results = self.run_cmd('goal list') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(goal1, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS), self.resource_as_dict(goal2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_goal_mgr.list.assert_called_once_with(detail=False) def test_do_goal_list_marker(self): goal2 = resource.Goal(mock.Mock(), GOAL_2) self.m_goal_mgr.list.return_value = [goal2] exit_code, results = self.run_cmd( 'goal list --marker fc087747-61be-4aad-8126-b701731ae836') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(goal2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_goal_mgr.list.assert_called_once_with( detail=False, marker='fc087747-61be-4aad-8126-b701731ae836') def test_do_goal_list_detail(self): goal1 = resource.Goal(mock.Mock(), GOAL_1) goal2 = resource.Goal(mock.Mock(), GOAL_2) self.m_goal_mgr.list.return_value = [ goal1, goal2] exit_code, results = self.run_cmd('goal list --detail') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(goal1, self.FIELDS, self.FIELD_LABELS), self.resource_as_dict(goal2, self.FIELDS, self.FIELD_LABELS)], results) self.m_goal_mgr.list.assert_called_once_with(detail=True) def test_do_goal_show_by_name(self): goal = resource.Goal(mock.Mock(), GOAL_1) self.m_goal_mgr.get.return_value = goal exit_code, result = self.run_cmd('goal show SERVER_CONSOLIDATION') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(goal, self.FIELDS, self.FIELD_LABELS), result) self.m_goal_mgr.get.assert_called_once_with('SERVER_CONSOLIDATION') def test_do_goal_show_by_uuid(self): goal = resource.Goal(mock.Mock(), GOAL_1) self.m_goal_mgr.get.return_value = goal exit_code, result = self.run_cmd( 'goal show fc087747-61be-4aad-8126-b701731ae836') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(goal, self.FIELDS, self.FIELD_LABELS), result) self.m_goal_mgr.get.assert_called_once_with( 'fc087747-61be-4aad-8126-b701731ae836') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_scoring_engine.py0000664000175000017500000001260700000000000027716 0ustar00zuulzuul00000000000000# # Copyright 2016 Intel # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import testtools from testtools import matchers from watcherclient.tests.unit import utils import watcherclient.v1.scoring_engine SE1 = { 'uuid': '5b558998-57ed-11e6-9ca8-08002722cb22', 'name': 'se-01', 'description': 'Some Scoring Engine' } SE2 = { 'uuid': '1f856554-57ee-11e6-ac72-08002722cb22', 'name': 'se-02', 'description': 'Some Other Scoring Engine' } fake_responses = { '/v1/scoring_engines': { 'GET': ( {}, {"scoring_engines": [SE1]}, ), }, '/v1/scoring_engines/detail': { 'GET': ( {}, {"scoring_engines": [SE1]}, ) }, '/v1/scoring_engines/%s' % SE1['uuid']: { 'GET': ( {}, SE1, ), }, '/v1/scoring_engines/%s' % SE1['name']: { 'GET': ( {}, SE1, ), }, } fake_responses_pagination = { '/v1/scoring_engines': { 'GET': ( {}, {"scoring_engines": [SE1], "next": "http://127.0.0.1:9322/v1/scoring_engines/?limit=1"} ), }, '/v1/scoring_engines/?limit=1': { 'GET': ( {}, {"scoring_engines": [SE2]} ), }, } fake_responses_sorting = { '/v1/scoring_engines/?sort_key=id': { 'GET': ( {}, {"scoring_engines": [SE1, SE2]} ), }, '/v1/scoring_engines/?sort_dir=desc': { 'GET': ( {}, {"scoring_engines": [SE2, SE1]} ), }, } class ScoringEngineManagerTest(testtools.TestCase): def setUp(self): super(ScoringEngineManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = \ watcherclient.v1.scoring_engine.ScoringEngineManager(self.api) def test_scoring_engines_list(self): scoring_engines = self.mgr.list() expect = [ ('GET', '/v1/scoring_engines', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(scoring_engines)) def test_scoring_engines_list_detail(self): scoring_engines = self.mgr.list(detail=True) expect = [ ('GET', '/v1/scoring_engines/detail', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(scoring_engines)) def test_scoring_engines_list_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = \ watcherclient.v1.scoring_engine.ScoringEngineManager(self.api) scoring_engines = self.mgr.list(limit=1) expect = [ ('GET', '/v1/scoring_engines/?limit=1', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(scoring_engines, matchers.HasLength(1)) def test_scoring_engines_list_pagination_no_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = \ watcherclient.v1.scoring_engine.ScoringEngineManager(self.api) scoring_engines = self.mgr.list(limit=0) expect = [ ('GET', '/v1/scoring_engines', {}, None), ('GET', '/v1/scoring_engines/?limit=1', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertThat(scoring_engines, matchers.HasLength(2)) def test_scoring_engines_list_sort_key(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = \ watcherclient.v1.scoring_engine.ScoringEngineManager(self.api) scoring_engines = self.mgr.list(sort_key='id') expect = [ ('GET', '/v1/scoring_engines/?sort_key=id', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(scoring_engines)) def test_scoring_engines_list_sort_dir(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = \ watcherclient.v1.scoring_engine.ScoringEngineManager(self.api) scoring_engines = self.mgr.list(sort_dir='desc') expect = [ ('GET', '/v1/scoring_engines/?sort_dir=desc', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(scoring_engines)) def test_scoring_engines_show(self): scoring_engine = self.mgr.get(SE1['uuid']) expect = [ ('GET', '/v1/scoring_engines/%s' % SE1['uuid'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(SE1['uuid'], scoring_engine.uuid) def test_scoring_engines_show_by_name(self): scoring_engine = self.mgr.get(SE1['name']) expect = [ ('GET', '/v1/scoring_engines/%s' % SE1['name'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(SE1['name'], scoring_engine.name) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_scoring_engine_shell.py0000664000175000017500000001073300000000000031103 0ustar00zuulzuul00000000000000# Copyright (c) 2016 Intel # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import datetime import io from unittest import mock from watcherclient import shell from watcherclient.tests.unit.v1 import base from watcherclient import v1 as resource from watcherclient.v1 import resource_fields SCORING_ENGINE_1 = { 'uuid': '5b558998-57ed-11e6-9ca8-08002722cb22', 'name': 'se-01', 'description': 'Scoring Engine 0.1', 'metainfo': '{ "columns": ["cpu", "mem", "pci"] }', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } SCORING_ENGINE_2 = { 'uuid': '1f856554-57ee-11e6-ac72-08002722cb22', 'name': 'se-02', 'description': 'Some other Scoring Engine', 'metainfo': 'mode=simplified', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } class ScoringEngineShellTest(base.CommandTestCase): SHORT_LIST_FIELDS = resource_fields.SCORING_ENGINE_SHORT_LIST_FIELDS SHORT_LIST_FIELD_LABELS = ( resource_fields.SCORING_ENGINE_SHORT_LIST_FIELD_LABELS) FIELDS = resource_fields.SCORING_ENGINE_FIELDS FIELD_LABELS = resource_fields.SCORING_ENGINE_FIELD_LABELS def setUp(self): super(self.__class__, self).setUp() p_se_manager = mock.patch.object( resource, 'ScoringEngineManager') self.m_se_mgr_cls = p_se_manager.start() self.addCleanup(p_se_manager.stop) self.m_se_mgr = mock.Mock() self.m_se_mgr_cls.return_value = self.m_se_mgr self.stdout = io.StringIO() self.cmd = shell.WatcherShell(stdout=self.stdout) def test_do_scoringengine_list(self): se1 = resource.ScoringEngine(mock.Mock(), SCORING_ENGINE_1) se2 = resource.ScoringEngine(mock.Mock(), SCORING_ENGINE_2) self.m_se_mgr.list.return_value = [ se1, se2] exit_code, results = self.run_cmd('scoringengine list') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(se1, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS), self.resource_as_dict(se2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_se_mgr.list.assert_called_once_with(detail=False) def test_do_scoringengine_list_detail(self): se1 = resource.Goal(mock.Mock(), SCORING_ENGINE_1) se2 = resource.Goal(mock.Mock(), SCORING_ENGINE_2) self.m_se_mgr.list.return_value = [ se1, se2] exit_code, results = self.run_cmd('scoringengine list --detail') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(se1, self.FIELDS, self.FIELD_LABELS), self.resource_as_dict(se2, self.FIELDS, self.FIELD_LABELS)], results) self.m_se_mgr.list.assert_called_once_with(detail=True) def test_do_scoringengine_show_by_name(self): scoringengine = resource.Goal(mock.Mock(), SCORING_ENGINE_1) self.m_se_mgr.get.return_value = scoringengine exit_code, result = self.run_cmd('scoringengine show se-01') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(scoringengine, self.FIELDS, self.FIELD_LABELS), result) self.m_se_mgr.get.assert_called_once_with('se-01') def test_do_scoringengine_show_by_uuid(self): scoringengine = resource.Goal(mock.Mock(), SCORING_ENGINE_1) self.m_se_mgr.get.return_value = scoringengine exit_code, result = self.run_cmd( 'scoringengine show 5b558998-57ed-11e6-9ca8-08002722cb22') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(scoringengine, self.FIELDS, self.FIELD_LABELS), result) self.m_se_mgr.get.assert_called_once_with( '5b558998-57ed-11e6-9ca8-08002722cb22') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_service.py0000664000175000017500000001175600000000000026371 0ustar00zuulzuul00000000000000# Copyright 2013 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 testtools from testtools import matchers from watcherclient.tests.unit import utils import watcherclient.v1.service SERVICE1 = { 'id': 1, 'name': 'watcher-applier', 'host': 'controller', 'status': 'ACTIVE', } SERVICE2 = { 'id': 2, 'name': 'watcher-decision-engine', 'host': 'controller', 'status': 'FAILED', } fake_responses = { '/v1/services': { 'GET': ( {}, {"services": [SERVICE1]}, ), }, '/v1/services/detail': { 'GET': ( {}, {"services": [SERVICE1]}, ) }, '/v1/services/%s' % SERVICE1['id']: { 'GET': ( {}, SERVICE1, ), }, '/v1/services/%s' % SERVICE1['name']: { 'GET': ( {}, SERVICE1, ), }, } fake_responses_pagination = { '/v1/services': { 'GET': ( {}, {"services": [SERVICE1], "next": "http://127.0.0.1:6385/v1/services/?limit=1"} ), }, '/v1/services/?limit=1': { 'GET': ( {}, {"services": [SERVICE2]} ), }, } fake_responses_sorting = { '/v1/services/?sort_key=id': { 'GET': ( {}, {"services": [SERVICE1, SERVICE2]} ), }, '/v1/services/?sort_dir=desc': { 'GET': ( {}, {"services": [SERVICE2, SERVICE1]} ), }, } class ServiceManagerTest(testtools.TestCase): def setUp(self): super(ServiceManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = watcherclient.v1.service.ServiceManager(self.api) def test_services_list(self): services = self.mgr.list() expect = [ ('GET', '/v1/services', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(services)) def test_services_list_detail(self): services = self.mgr.list(detail=True) expect = [ ('GET', '/v1/services/detail', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(services)) def test_services_list_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.service.ServiceManager(self.api) services = self.mgr.list(limit=1) expect = [ ('GET', '/v1/services/?limit=1', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(services, matchers.HasLength(1)) def test_services_list_pagination_no_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.service.ServiceManager(self.api) services = self.mgr.list(limit=0) expect = [ ('GET', '/v1/services', {}, None), ('GET', '/v1/services/?limit=1', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertThat(services, matchers.HasLength(2)) def test_services_list_sort_key(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = watcherclient.v1.service.ServiceManager(self.api) services = self.mgr.list(sort_key='id') expect = [ ('GET', '/v1/services/?sort_key=id', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(services)) def test_services_list_sort_dir(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = watcherclient.v1.service.ServiceManager(self.api) services = self.mgr.list(sort_dir='desc') expect = [ ('GET', '/v1/services/?sort_dir=desc', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(services)) def test_services_show(self): service = self.mgr.get(SERVICE1['id']) expect = [ ('GET', '/v1/services/%s' % SERVICE1['id'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(SERVICE1['id'], service.id) def test_services_show_by_name(self): service = self.mgr.get(SERVICE1['name']) expect = [ ('GET', '/v1/services/%s' % SERVICE1['name'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(SERVICE1['name'], service.name) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_service_shell.py0000664000175000017500000000770400000000000027556 0ustar00zuulzuul00000000000000# Copyright (c) 2016 Servionica # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import datetime import io from unittest import mock from watcherclient import shell from watcherclient.tests.unit.v1 import base from watcherclient import v1 as resource from watcherclient.v1 import resource_fields SERVICE_1 = { 'name': 'watcher-applier', 'host': 'controller', 'status': 'ACTIVE', 'last_seen_up': None, 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } SERVICE_2 = { 'name': 'watcher-decision-engine', 'host': 'controller', 'status': 'FAILED', 'last_seen_up': None, 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, } class ServiceShellTest(base.CommandTestCase): SHORT_LIST_FIELDS = resource_fields.SERVICE_SHORT_LIST_FIELDS SHORT_LIST_FIELD_LABELS = ( resource_fields.SERVICE_SHORT_LIST_FIELD_LABELS) FIELDS = resource_fields.SERVICE_FIELDS FIELD_LABELS = resource_fields.SERVICE_FIELD_LABELS def setUp(self): super(self.__class__, self).setUp() p_service_manager = mock.patch.object(resource, 'ServiceManager') self.m_service_mgr_cls = p_service_manager.start() self.addCleanup(p_service_manager.stop) self.m_service_mgr = mock.Mock() self.m_service_mgr_cls.return_value = self.m_service_mgr self.stdout = io.StringIO() self.cmd = shell.WatcherShell(stdout=self.stdout) def test_do_service_list(self): service1 = resource.Service(mock.Mock(), SERVICE_1) service2 = resource.Service(mock.Mock(), SERVICE_2) self.m_service_mgr.list.return_value = [ service1, service2] exit_code, results = self.run_cmd('service list') for res in results: del res['ID'] self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(service1, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS), self.resource_as_dict(service2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_service_mgr.list.assert_called_once_with(detail=False) def test_do_service_list_detail(self): service1 = resource.Service(mock.Mock(), SERVICE_1) service2 = resource.Service(mock.Mock(), SERVICE_2) self.m_service_mgr.list.return_value = [ service1, service2] exit_code, results = self.run_cmd('service list --detail') for res in results: del res['ID'] self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(service1, self.FIELDS, self.FIELD_LABELS), self.resource_as_dict(service2, self.FIELDS, self.FIELD_LABELS)], results) self.m_service_mgr.list.assert_called_once_with(detail=True) def test_do_service_show_by_name(self): service = resource.Service(mock.Mock(), SERVICE_1) self.m_service_mgr.get.return_value = service exit_code, result = self.run_cmd('service show watcher-applier') del result['ID'] self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(service, self.FIELDS, self.FIELD_LABELS), result) self.m_service_mgr.get.assert_called_once_with('watcher-applier') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_strategy.py0000664000175000017500000001435200000000000026566 0ustar00zuulzuul00000000000000# Copyright 2013 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 testtools from testtools import matchers from watcherclient.tests.unit import utils import watcherclient.v1.strategy STRATEGY1 = { 'uuid': '2cf86250-d309-4b81-818e-1537f3dba6e5', 'name': 'basic', 'display_name': 'Basic consolidation', 'strategy_id': 'SERVER_CONSOLIDATION', } STRATEGY2 = { 'uuid': 'b20bb987-ea8f-457a-a4ea-ab3ffdfeff8b', 'name': 'dummy', 'display_name': 'Dummy', 'strategy_id': 'DUMMY', } fake_responses = { '/v1/strategies': { 'GET': ( {}, {"strategies": [STRATEGY1]}, ), }, '/v1/strategies/detail': { 'GET': ( {}, {"strategies": [STRATEGY1]}, ) }, '/v1/strategies/%s' % STRATEGY1['uuid']: { 'GET': ( {}, STRATEGY1, ), }, '/v1/strategies/%s' % STRATEGY1['name']: { 'GET': ( {}, STRATEGY1, ), }, '/v1/strategies/%s/state' % STRATEGY1['name']: { 'GET': ( {}, STRATEGY1, ), }, } fake_responses_pagination = { '/v1/strategies': { 'GET': ( {}, {"strategies": [STRATEGY1], "next": "http://127.0.0.1:9322/v1/strategies/?limit=1"} ), }, '/v1/strategies/?limit=1': { 'GET': ( {}, {"strategies": [STRATEGY2]} ), }, } fake_responses_sorting = { '/v1/strategies/?sort_key=id': { 'GET': ( {}, {"strategies": [STRATEGY1, STRATEGY2]} ), }, '/v1/strategies/?sort_dir=desc': { 'GET': ( {}, {"strategies": [STRATEGY2, STRATEGY1]} ), }, } fake_responses_marker = { '/v1/strategies/?marker=2cf86250-d309-4b81-818e-1537f3dba6e5': { 'GET': ( {}, {"strategies": [STRATEGY2]} ), }, } class StrategyManagerTest(testtools.TestCase): def setUp(self): super(StrategyManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = watcherclient.v1.strategy.StrategyManager(self.api) def test_strategies_list(self): strategies = self.mgr.list() expect = [ ('GET', '/v1/strategies', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(strategies)) def test_strategies_list_detail(self): strategies = self.mgr.list(detail=True) expect = [ ('GET', '/v1/strategies/detail', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(strategies)) def test_strategies_list_marker(self): self.api = utils.FakeAPI(fake_responses_marker) self.mgr = watcherclient.v1.strategy.StrategyManager(self.api) strategies = self.mgr.list(marker=STRATEGY1['uuid']) expect = [ ('GET', '/v1/strategies/?marker=2cf86250-d309-4b81-818e-1537f3dba6e5', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(1, len(strategies)) def test_strategies_list_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.strategy.StrategyManager(self.api) strategies = self.mgr.list(limit=1) expect = [ ('GET', '/v1/strategies/?limit=1', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(strategies, matchers.HasLength(1)) def test_strategies_list_pagination_no_limit(self): self.api = utils.FakeAPI(fake_responses_pagination) self.mgr = watcherclient.v1.strategy.StrategyManager(self.api) strategies = self.mgr.list(limit=0) expect = [ ('GET', '/v1/strategies', {}, None), ('GET', '/v1/strategies/?limit=1', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertThat(strategies, matchers.HasLength(2)) def test_strategies_list_sort_key(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = watcherclient.v1.strategy.StrategyManager(self.api) strategies = self.mgr.list(sort_key='id') expect = [ ('GET', '/v1/strategies/?sort_key=id', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(strategies)) def test_strategies_list_sort_dir(self): self.api = utils.FakeAPI(fake_responses_sorting) self.mgr = watcherclient.v1.strategy.StrategyManager(self.api) strategies = self.mgr.list(sort_dir='desc') expect = [ ('GET', '/v1/strategies/?sort_dir=desc', {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(strategies)) def test_strategies_show(self): strategy = self.mgr.get(STRATEGY1['uuid']) expect = [ ('GET', '/v1/strategies/%s' % STRATEGY1['uuid'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(STRATEGY1['uuid'], strategy.uuid) def test_strategies_show_by_name(self): strategy = self.mgr.get(STRATEGY1['name']) expect = [ ('GET', '/v1/strategies/%s' % STRATEGY1['name'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertEqual(STRATEGY1['name'], strategy.name) def test_strategies_state(self): self.mgr.state(STRATEGY1['name']) expect = [ ('GET', '/v1/strategies/%s/state' % STRATEGY1['name'], {}, None), ] self.assertEqual(expect, self.api.calls) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/tests/unit/v1/test_strategy_shell.py0000664000175000017500000001711100000000000027751 0ustar00zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import datetime import io from unittest import mock from oslo_serialization import jsonutils from watcherclient import shell from watcherclient.tests.unit.v1 import base from watcherclient import v1 as resource from watcherclient.v1 import resource_fields STRATEGY_1 = { 'uuid': '2cf86250-d309-4b81-818e-1537f3dba6e5', 'name': 'basic', 'display_name': 'Basic consolidation', 'goal_uuid': 'fc087747-61be-4aad-8126-b701731ae836', 'goal_name': 'SERVER_CONSOLIDATION', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, 'parameters_spec': {}, } STRATEGY_2 = { 'uuid': 'b20bb987-ea8f-457a-a4ea-ab3ffdfeff8b', 'name': 'dummy', 'display_name': 'Dummy', 'goal_uuid': '407b03b1-63c6-49b2-adaf-4df5c0090047', 'goal_name': 'DUMMY', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, 'parameters_spec': {}, } class StrategyShellTest(base.CommandTestCase): SHORT_LIST_FIELDS = resource_fields.STRATEGY_SHORT_LIST_FIELDS SHORT_LIST_FIELD_LABELS = ( resource_fields.STRATEGY_SHORT_LIST_FIELD_LABELS) FIELDS = resource_fields.STRATEGY_FIELDS FIELD_LABELS = resource_fields.STRATEGY_FIELD_LABELS STATE_FIELDS = resource_fields.STRATEGY_STATE_FIELDS STATE_FIELD_LABELS = resource_fields.STRATEGY_STATE_FIELD_LABELS def setUp(self): super(self.__class__, self).setUp() p_strategy_manager = mock.patch.object(resource, 'StrategyManager') self.m_strategy_mgr_cls = p_strategy_manager.start() self.addCleanup(p_strategy_manager.stop) self.m_strategy_mgr = mock.Mock() self.m_strategy_mgr_cls.return_value = self.m_strategy_mgr self.stdout = io.StringIO() self.cmd = shell.WatcherShell(stdout=self.stdout) def test_do_strategy_list(self): strategy1 = resource.Strategy(mock.Mock(), STRATEGY_1) strategy2 = resource.Strategy(mock.Mock(), STRATEGY_2) self.m_strategy_mgr.list.return_value = [ strategy1, strategy2] exit_code, results = self.run_cmd('strategy list') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(strategy1, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS), self.resource_as_dict(strategy2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_strategy_mgr.list.assert_called_once_with(detail=False) def test_do_strategy_list_marker(self): strategy2 = resource.Strategy(mock.Mock(), STRATEGY_2) self.m_strategy_mgr.list.return_value = [strategy2] exit_code, results = self.run_cmd( 'strategy list --marker 2cf86250-d309-4b81-818e-1537f3dba6e5') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(strategy2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_strategy_mgr.list.assert_called_once_with( detail=False, marker='2cf86250-d309-4b81-818e-1537f3dba6e5') def test_do_strategy_list_detail(self): strategy1 = resource.Strategy(mock.Mock(), STRATEGY_1) strategy2 = resource.Strategy(mock.Mock(), STRATEGY_2) self.m_strategy_mgr.list.return_value = [ strategy1, strategy2] exit_code, results = self.run_cmd('strategy list --detail') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(strategy1, self.FIELDS, self.FIELD_LABELS), self.resource_as_dict(strategy2, self.FIELDS, self.FIELD_LABELS)], results) self.m_strategy_mgr.list.assert_called_once_with(detail=True) def test_do_strategy_list_filter_by_goal_name(self): strategy2 = resource.Strategy(mock.Mock(), STRATEGY_2) self.m_strategy_mgr.list.return_value = [strategy2] exit_code, results = self.run_cmd( 'strategy list --goal ' 'DUMMY') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(strategy2, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_strategy_mgr.list.assert_called_once_with( detail=False, goal='DUMMY', ) def test_do_strategy_list_filter_by_goal_uuid(self): strategy1 = resource.Strategy(mock.Mock(), STRATEGY_1) self.m_strategy_mgr.list.return_value = [strategy1] exit_code, results = self.run_cmd( 'strategy list --goal ' 'fc087747-61be-4aad-8126-b701731ae836') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(strategy1, self.SHORT_LIST_FIELDS, self.SHORT_LIST_FIELD_LABELS)], results) self.m_strategy_mgr.list.assert_called_once_with( detail=False, goal='fc087747-61be-4aad-8126-b701731ae836', ) def test_do_strategy_show_by_uuid(self): strategy = resource.Strategy(mock.Mock(), STRATEGY_1) self.m_strategy_mgr.get.return_value = strategy exit_code, result = self.run_cmd( 'strategy show f8e47706-efcf-49a4-a5c4-af604eb492f2') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(strategy, self.FIELDS, self.FIELD_LABELS), result) self.m_strategy_mgr.get.assert_called_once_with( 'f8e47706-efcf-49a4-a5c4-af604eb492f2') def test_do_strategy_state(self): strategy1 = resource.Strategy(mock.Mock(), STRATEGY_1) strategy_req = [ {'type': 'Datasource', 'mandatory': True, 'comment': '', 'state': 'gnocchi: True'}, {'type': 'Metrics', 'mandatory': False, 'comment': '', 'state': jsonutils.dumps([ {'compute.node.cpu.percent': 'available'}, {'cpu_util': 'available'}, {'memory.resident': 'available'}, {'hardware.memory.used': 'not available'}])}, {'type': 'CDM', 'mandatory': True, 'comment': '', 'state': jsonutils.dumps([{'compute_model': 'available'}, {'storage_model': 'not available'}])}, {'type': 'Name', 'mandatory': '', 'comment': '', 'state': strategy1.name}] requirements = [resource.Strategy(mock.Mock(), req) for req in strategy_req] self.m_strategy_mgr.state.return_value = requirements exit_code, results = self.run_cmd('strategy state basic') self.assertEqual(0, exit_code) self.assertEqual( [self.resource_as_dict(req, self.STATE_FIELDS, self.STATE_FIELD_LABELS) for req in requirements], results) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1740768124.8590243 python_watcherclient-4.8.0/watcherclient/v1/0000775000175000017500000000000000000000000021165 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/__init__.py0000664000175000017500000000357300000000000023306 0ustar00zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from watcherclient.v1 import action from watcherclient.v1 import action_plan from watcherclient.v1 import audit from watcherclient.v1 import audit_template from watcherclient.v1 import data_model from watcherclient.v1 import goal from watcherclient.v1 import scoring_engine from watcherclient.v1 import service from watcherclient.v1 import strategy Action = action.Action ActionManager = action.ActionManager ActionPlan = action_plan.ActionPlan ActionPlanManager = action_plan.ActionPlanManager Audit = audit.Audit AuditManager = audit.AuditManager AuditTemplate = audit_template.AuditTemplate AuditTemplateManager = audit_template.AuditTemplateManager Goal = goal.Goal GoalManager = goal.GoalManager ScoringEngine = scoring_engine.ScoringEngine ScoringEngineManager = scoring_engine.ScoringEngineManager Service = service.Service ServiceManager = service.ServiceManager Strategy = strategy.Strategy StrategyManager = strategy.StrategyManager DataModel = data_model.DataModel DataModelManager = data_model.DataModelManager __all__ = ( "Action", "ActionManager", "ActionPlan", "ActionPlanManager", "Audit", "AuditManager", "AuditTemplate", "AuditTemplateManager", "Goal", "GoalManager", "ScoringEngine", "ScoringEngineManager", "Service", "ServiceManager", "Strategy", "StrategyManager", "DataModel", "DataModelManager") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/action.py0000664000175000017500000000552200000000000023020 0ustar00zuulzuul00000000000000# # Copyright 2013 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 watcherclient.common import base from watcherclient.common import utils class Action(base.Resource): def __repr__(self): return "" % self._info class ActionManager(base.Manager): resource_class = Action @staticmethod def _path(id=None): return '/v1/actions/%s' % id if id else '/v1/actions' def list(self, action_plan=None, audit=None, limit=None, sort_key=None, sort_dir=None, detail=False, marker=None): """Retrieve a list of action. :param action_plan: UUID of the action plan :param audit: UUID of the audit :param limit: The maximum number of results to return per request, if: 1) limit > 0, the maximum number of actions to return. 2) limit == 0, return the entire list of actions. 3) limit param is NOT specified (None), the number of items returned respect the maximum imposed by the Watcher API (see Watcher's api.max_limit option). :param sort_key: Optional, field used for sorting. :param sort_dir: Optional, direction of sorting, either 'asc' (the default) or 'desc'. :param detail: Optional, boolean whether to return detailed information about actions. :param marker: Optional, UUID of the last action in the previous page. :returns: A list of actions. """ if limit is not None: limit = int(limit) filters = utils.common_filters(limit, sort_key, sort_dir, marker) if action_plan is not None: filters.append('action_plan_uuid=%s' % action_plan) if audit is not None: filters.append('audit_uuid=%s' % audit) path = '' if detail: path += 'detail' if filters: path += '?' + '&'.join(filters) if limit is None: return self._list(self._path(path), "actions") else: return self._list_pagination(self._path(path), "actions", limit=limit) def get(self, action_id): try: return self._list(self._path(action_id))[0] except IndexError: return None ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/action_plan.py0000664000175000017500000000723700000000000024037 0ustar00zuulzuul00000000000000# # Copyright 2013 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 watcherclient.common import base from watcherclient.common import utils # from watcherclient import exceptions as exc class ActionPlan(base.Resource): def __repr__(self): return "" % self._info class ActionPlanManager(base.Manager): resource_class = ActionPlan @staticmethod def _path(id=None, q_param=None): if id and q_param: return '/v1/action_plans/%s/%s' % (id, q_param) elif id: return '/v1/action_plans/%s' % id else: return '/v1/action_plans' def list(self, audit=None, limit=None, sort_key=None, sort_dir=None, detail=False, marker=None): """Retrieve a list of action plan. :param audit: Name of the audit :param limit: The maximum number of results to return per request, if: 1) limit > 0, the maximum number of action plans to return. 2) limit == 0, return the entire list of action plans. 3) limit param is NOT specified (None), the number of items returned respect the maximum imposed by the Watcher API (see Watcher's api.max_limit option). :param sort_key: Optional, field used for sorting. :param sort_dir: Optional, direction of sorting, either 'asc' (the default) or 'desc'. :param detail: Optional, boolean whether to return detailed information about action plans. :param marker: The last actionplan UUID of the previous page; displays list of actionplans after "marker". :returns: A list of action plans. """ if limit is not None: limit = int(limit) filters = utils.common_filters(limit, sort_key, sort_dir, marker) if audit is not None: filters.append('audit_uuid=%s' % audit) path = '' if detail: path += 'detail' if filters: path += '?' + '&'.join(filters) if limit is None: return self._list(self._path(path), "action_plans") else: return self._list_pagination(self._path(path), "action_plans", limit=limit) def get(self, action_plan_id): try: return self._list(self._path(action_plan_id))[0] except IndexError: return None def delete(self, action_plan_id): return self._delete(self._path(action_plan_id)) def update(self, action_plan_id, patch): return self._update(self._path(action_plan_id), patch) def start(self, action_plan_id): return self._start(self._path(action_plan_id, 'start')) def cancel(self, action_plan_id): action_plan = self.get(action_plan_id) if action_plan.state == "ONGOING": patch = [{'op': 'replace', 'value': 'CANCELLING', 'path': '/state'}] else: patch = [{'op': 'replace', 'value': 'CANCELLED', 'path': '/state'}] return self._update(self._path(action_plan_id), patch) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/action_plan_shell.py0000664000175000017500000002654000000000000025224 0ustar00zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import io from cliff.formatters import yaml_format from osc_lib import utils from oslo_utils import uuidutils from watcherclient._i18n import _ from watcherclient.common import command from watcherclient.common import utils as common_utils from watcherclient import exceptions from watcherclient.v1 import resource_fields as res_fields def format_global_efficacy(global_efficacy): formatted_global_eff = {} for eff in global_efficacy: eff_name = eff.get('name') if (eff.get('value') is not None and eff.get('unit')): formatted_global_eff[eff_name] = "%(value).2f %(unit)s" % dict( unit=eff.get('unit'), value=eff.get('value')) elif eff.get('value') is not None: formatted_global_eff[eff_name] = eff.get('value') return formatted_global_eff class ShowActionPlan(command.ShowOne): """Show detailed information about a given action plan.""" def get_parser(self, prog_name): parser = super(ShowActionPlan, self).get_parser(prog_name) parser.add_argument( 'action_plan', metavar='', help=_('UUID of the action plan'), ) return parser def _format_indicators(self, action_plan, parsed_args): out = io.StringIO() efficacy_indicators = action_plan.efficacy_indicators fields = ['name', 'description', 'value', 'unit'] yaml_format.YAMLFormatter().emit_list( column_names=list(field.capitalize() for field in fields), data=[utils.get_dict_properties(spec, fields) for spec in efficacy_indicators], stdout=out, parsed_args=parsed_args, ) return out.getvalue() or '' def _format_global_efficacy(self, global_efficacy, parsed_args): formatted_global_efficacy = format_global_efficacy(global_efficacy) out = io.StringIO() yaml_format.YAMLFormatter().emit_one( column_names=list(resource.capitalize() for resource in formatted_global_efficacy), data=[value for value in formatted_global_efficacy.values()], stdout=out, parsed_args=parsed_args, ) return out.getvalue() or '' def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") action_plan_uuid = parsed_args.action_plan if not uuidutils.is_uuid_like(action_plan_uuid): raise exceptions.ValidationError() try: action_plan = client.action_plan.get(action_plan_uuid) except exceptions.HTTPNotFound as exc: raise exceptions.CommandError(str(exc)) if parsed_args.formatter == 'table': # Update the raw efficacy indicators with the formatted ones action_plan.efficacy_indicators = ( self._format_indicators(action_plan, parsed_args)) # Update the raw global efficacy with the formatted one action_plan.global_efficacy = self._format_global_efficacy( action_plan.global_efficacy, parsed_args) columns = res_fields.ACTION_PLAN_FIELDS column_headers = res_fields.ACTION_PLAN_FIELD_LABELS return column_headers, utils.get_item_properties(action_plan, columns) class ListActionPlan(command.Lister): """List information on retrieved action plans.""" def get_parser(self, prog_name): parser = super(ListActionPlan, self).get_parser(prog_name) parser.add_argument( '--audit', metavar='', help=_('UUID of an audit used for filtering.')) parser.add_argument( '--detail', dest='detail', action='store_true', default=False, help=_("Show detailed information about action plans.")) parser.add_argument( '--limit', metavar='', type=int, help=_('Maximum number of action plans to return per request, ' '0 for no limit. Default is the maximum number used ' 'by the Watcher API Service.')) parser.add_argument( '--marker', metavar='', help=_('The last actionplan UUID of the previous page; ' 'displays list of actionplans after "marker".')) parser.add_argument( '--sort-key', metavar='', help=_('Action Plan field that will be used for sorting.')) parser.add_argument( '--sort-dir', metavar='', choices=['asc', 'desc'], help=_('Sort direction: "asc" (the default) or "desc".')) return parser def _format_indicators(self, action_plan, parsed_args): out = io.StringIO() efficacy_indicators = action_plan.efficacy_indicators fields = ['name', 'value', 'unit'] yaml_format.YAMLFormatter().emit_list( column_names=list(field.capitalize() for field in fields), data=[utils.get_dict_properties(spec, fields) for spec in efficacy_indicators], stdout=out, parsed_args=parsed_args, ) return out.getvalue() or '' def _format_global_efficacy(self, global_efficacy, parsed_args): formatted_global_efficacy = format_global_efficacy(global_efficacy) out = io.StringIO() yaml_format.YAMLFormatter().emit_one( column_names=list(resource.capitalize() for resource in formatted_global_efficacy), data=[value for value in formatted_global_efficacy.values()], stdout=out, parsed_args=parsed_args, ) return out.getvalue() or '' def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") params = {} if parsed_args.audit is not None: params['audit'] = parsed_args.audit if parsed_args.detail: fields = res_fields.ACTION_PLAN_FIELDS field_labels = res_fields.ACTION_PLAN_FIELD_LABELS else: fields = res_fields.ACTION_PLAN_SHORT_LIST_FIELDS field_labels = res_fields.ACTION_PLAN_SHORT_LIST_FIELD_LABELS params.update(common_utils.common_params_for_list( parsed_args, fields, field_labels)) data = client.action_plan.list(**params) if parsed_args.formatter == 'table': for action_plan in data: # Update the raw efficacy indicators with the formatted ones action_plan.efficacy_indicators = ( self._format_indicators(action_plan, parsed_args)) # Update the raw global efficacy with the formatted one action_plan.global_efficacy = self._format_global_efficacy( action_plan.global_efficacy, parsed_args) return (field_labels, (utils.get_item_properties(item, fields) for item in data)) class UpdateActionPlan(command.ShowOne): """Update action plan command.""" def get_parser(self, prog_name): parser = super(UpdateActionPlan, self).get_parser(prog_name) parser.add_argument( 'action_plan', metavar='', help=_("UUID of the action_plan.")) parser.add_argument( 'op', metavar='', choices=['add', 'replace', 'remove'], help=_("Operation: 'add', 'replace', or 'remove'.")) parser.add_argument( 'attributes', metavar='', nargs='+', action='append', default=[], help=_("Attribute to add, replace, or remove. Can be specified " "multiple times. For 'remove', only is necessary.")) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") if not uuidutils.is_uuid_like(parsed_args.action_plan): raise exceptions.ValidationError() patch = common_utils.args_array_to_patch( parsed_args.op, parsed_args.attributes[0]) action_plan = client.action_plan.update(parsed_args.action_plan, patch) columns = res_fields.ACTION_PLAN_FIELDS column_headers = res_fields.ACTION_PLAN_FIELD_LABELS return column_headers, utils.get_item_properties(action_plan, columns) class StartActionPlan(command.ShowOne): """Start action plan command.""" def get_parser(self, prog_name): parser = super(StartActionPlan, self).get_parser(prog_name) parser.add_argument( 'action_plan', metavar='', help=_("UUID of the action_plan.")) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") if not uuidutils.is_uuid_like(parsed_args.action_plan): raise exceptions.ValidationError() action_plan = client.action_plan.start(parsed_args.action_plan) columns = res_fields.ACTION_PLAN_FIELDS column_headers = res_fields.ACTION_PLAN_FIELD_LABELS return column_headers, utils.get_item_properties(action_plan, columns) class DeleteActionPlan(command.Command): """Delete action plan command.""" def get_parser(self, prog_name): parser = super(DeleteActionPlan, self).get_parser(prog_name) parser.add_argument( 'action_plans', metavar='', nargs='+', help=_('UUID of the action plan'), ) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") for action_plan in parsed_args.action_plans: if not uuidutils.is_uuid_like(action_plan): raise exceptions.ValidationError() client.action_plan.delete(action_plan) class CancelActionPlan(command.ShowOne): """Cancel action plan command.""" def get_parser(self, prog_name): parser = super(CancelActionPlan, self).get_parser(prog_name) parser.add_argument( 'action_plan', metavar='', help=_("UUID of the action_plan.")) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") if not uuidutils.is_uuid_like(parsed_args.action_plan): raise exceptions.ValidationError() action_plan = client.action_plan.cancel(parsed_args.action_plan) columns = res_fields.ACTION_PLAN_FIELDS column_headers = res_fields.ACTION_PLAN_FIELD_LABELS return column_headers, utils.get_item_properties(action_plan, columns) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/action_shell.py0000664000175000017500000001024700000000000024207 0ustar00zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES 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 from watcherclient._i18n import _ from watcherclient.common import command from watcherclient.common import utils as common_utils from watcherclient import exceptions from watcherclient.v1 import resource_fields as res_fields class ShowAction(command.ShowOne): """Show detailed information about a given action.""" def get_parser(self, prog_name): parser = super(ShowAction, self).get_parser(prog_name) parser.add_argument( 'action', metavar='', help=_('UUID of the action'), ) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") try: action = client.action.get(parsed_args.action) except exceptions.HTTPNotFound as exc: raise exceptions.CommandError(str(exc)) columns = res_fields.ACTION_FIELDS column_headers = res_fields.ACTION_FIELD_LABELS return column_headers, utils.get_item_properties(action, columns) class ListAction(command.Lister): """List information on retrieved actions.""" def get_parser(self, prog_name): parser = super(ListAction, self).get_parser(prog_name) parser.add_argument( '--action-plan', metavar='', help=_('UUID of the action plan used for filtering.')) parser.add_argument( '--audit', metavar='', help=_(' UUID of the audit used for filtering.')) parser.add_argument( '--detail', dest='detail', action='store_true', default=False, help=_("Show detailed information about actions.")) parser.add_argument( '--limit', metavar='', type=int, help=_('Maximum number of actions to return per request, ' '0 for no limit. Default is the maximum number used ' 'by the Watcher API Service.')) parser.add_argument( '--sort-key', metavar='', help=_('Action field that will be used for sorting.')) parser.add_argument( '--sort-dir', metavar='', choices=['asc', 'desc'], help=_('Sort direction: "asc" (the default) or "desc".')) parser.add_argument( '--marker', dest='marker', metavar='', default=None, help=_('UUID of the last action in the previous page; ' 'displays list of actions after "marker".')) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") params = {} if parsed_args.action_plan is not None: params['action_plan'] = parsed_args.action_plan if parsed_args.audit is not None: params['audit'] = parsed_args.audit if parsed_args.detail: fields = res_fields.ACTION_FIELDS field_labels = res_fields.ACTION_FIELD_LABELS else: fields = res_fields.ACTION_SHORT_LIST_FIELDS field_labels = res_fields.ACTION_SHORT_LIST_FIELD_LABELS params.update( common_utils.common_params_for_list( parsed_args, fields, field_labels)) try: data = client.action.list(**params) except exceptions.HTTPNotFound as ex: raise exceptions.CommandError(str(ex)) return (field_labels, (utils.get_item_properties(item, fields) for item in data)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/audit.py0000664000175000017500000000711300000000000022647 0ustar00zuulzuul00000000000000# # Copyright 2013 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 watcherclient.common import base from watcherclient.common import utils from watcherclient import exceptions as exc CREATION_ATTRIBUTES = ['audit_template_uuid', 'audit_type', 'interval', 'parameters', 'goal', 'strategy', 'auto_trigger', 'name', 'start_time', 'end_time', 'force'] class Audit(base.Resource): def __repr__(self): return "" % self._info class AuditManager(base.Manager): resource_class = Audit @staticmethod def _path(id=None): return '/v1/audits/%s' % id if id else '/v1/audits' def list(self, audit_template=None, limit=None, sort_key=None, sort_dir=None, detail=False, goal=None, strategy=None, marker=None): """Retrieve a list of audit. :param audit_template: Name of the audit template :param limit: The maximum number of results to return per request, if: 1) limit > 0, the maximum number of audits to return. 2) limit == 0, return the entire list of audits. 3) limit param is NOT specified (None), the number of items returned respect the maximum imposed by the Watcher API (see Watcher's api.max_limit option). :param sort_key: Optional, field used for sorting. :param sort_dir: Optional, direction of sorting, either 'asc' (the default) or 'desc'. :param detail: Optional, boolean whether to return detailed information about audits. :param marker: Optional, UUID of the last audit in the previous page. :returns: A list of audits. """ if limit is not None: limit = int(limit) filters = utils.common_filters(limit, sort_key, sort_dir, marker) if audit_template is not None: filters.append('audit_template=%s' % audit_template) if goal is not None: filters.append('goal=%s' % goal) if strategy is not None: filters.append('strategy=%s' % strategy) path = '' if detail: path += 'detail' if filters: path += '?' + '&'.join(filters) if limit is None: return self._list(self._path(path), "audits") else: return self._list_pagination(self._path(path), "audits", limit=limit) def create(self, **kwargs): new = {} for (key, value) in kwargs.items(): if key in CREATION_ATTRIBUTES: new[key] = value else: raise exc.InvalidAttribute() return self._create(self._path(), new) def get(self, audit): try: return self._list(self._path(audit))[0] except IndexError: return None def delete(self, audit): return self._delete(self._path(audit)) def update(self, audit, patch): return self._update(self._path(audit), patch) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/audit_shell.py0000664000175000017500000002710400000000000024040 0ustar00zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES 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 osc_lib import utils from oslo_utils import uuidutils from watcherclient._i18n import _ from watcherclient.common import api_versioning from watcherclient.common import command from watcherclient.common import utils as common_utils from watcherclient import exceptions from watcherclient.v1 import resource_fields as res_fields def drop_unsupported_field(app_args, fields, field_labels): fields = copy.copy(fields) field_labels = copy.copy(field_labels) api_ver = app_args.os_infra_optim_api_version if not api_versioning.allow_start_end_audit_time(api_ver): for field, label in zip(('start_time', 'end_time'), ('Start Time', 'End Time')): fields.remove(field) field_labels.remove(label) if not api_versioning.launch_audit_forced(api_ver): fields.remove('force') field_labels.remove('Force') return fields, field_labels class ShowAudit(command.ShowOne): """Show detailed information about a given audit.""" def get_parser(self, prog_name): parser = super(ShowAudit, self).get_parser(prog_name) parser.add_argument( 'audit', metavar='', help=_('UUID or name of the audit'), ) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") try: audit = client.audit.get(parsed_args.audit) if audit.strategy_name is None: audit.strategy_name = 'auto' except exceptions.HTTPNotFound as exc: raise exceptions.CommandError(str(exc)) columns = res_fields.AUDIT_FIELDS column_headers = res_fields.AUDIT_FIELD_LABELS columns, column_headers = drop_unsupported_field( self.app_args, columns, column_headers) return column_headers, utils.get_item_properties(audit, columns) class ListAudit(command.Lister): """List information on retrieved audits.""" def get_parser(self, prog_name): parser = super(ListAudit, self).get_parser(prog_name) parser.add_argument( '--detail', dest='detail', action='store_true', default=False, help=_("Show detailed information about audits.")) parser.add_argument( '--goal', dest='goal', metavar='', help=_('UUID or name of the goal used for filtering.')) parser.add_argument( '--strategy', dest='strategy', metavar='', help=_('UUID or name of the strategy used for filtering.')) parser.add_argument( '--limit', metavar='', type=int, help=_('Maximum number of audits to return per request, ' '0 for no limit. Default is the maximum number used ' 'by the Watcher API Service.')) parser.add_argument( '--sort-key', metavar='', help=_('Audit field that will be used for sorting.')) parser.add_argument( '--sort-dir', metavar='', choices=['asc', 'desc'], help=_('Sort direction: "asc" (the default) or "desc".')) parser.add_argument( '--marker', dest='marker', metavar='', default=None, help=_('UUID of the last audit in the previous page; ' 'displays list of audits after "marker".')) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") params = {} # Optional if parsed_args.goal: params['goal'] = parsed_args.goal # Optional if parsed_args.strategy: params['strategy'] = parsed_args.strategy if parsed_args.detail: fields = res_fields.AUDIT_FIELDS field_labels = res_fields.AUDIT_FIELD_LABELS else: fields = res_fields.AUDIT_SHORT_LIST_FIELDS field_labels = res_fields.AUDIT_SHORT_LIST_FIELD_LABELS if parsed_args.detail: fields, field_labels = drop_unsupported_field( self.app_args, fields, field_labels) params.update(common_utils.common_params_for_list( parsed_args, fields, field_labels)) try: data = client.audit.list(**params) for audit in data: if audit.strategy_name is None: audit.strategy_name = 'auto' except exceptions.HTTPNotFound as ex: raise exceptions.CommandError(str(ex)) return (field_labels, (utils.get_item_properties(item, fields) for item in data)) class CreateAudit(command.ShowOne): """Create new audit.""" def get_parser(self, prog_name): parser = super(CreateAudit, self).get_parser(prog_name) parser.add_argument( '-t', '--audit_type', dest='audit_type', metavar='', default='ONESHOT', choices=['ONESHOT', 'CONTINUOUS', 'EVENT'], help=_("Audit type. It must be ONESHOT, CONTINUOUS or EVENT. " "Default is ONESHOT.")) parser.add_argument( '-p', '--parameter', dest='parameters', metavar='', action='append', help=_("Record strategy parameter/value metadata. " "Can be specified multiple times.")) parser.add_argument( '-i', '--interval', dest='interval', metavar='', help=_('Audit interval (in seconds or cron format). ' 'Cron interval can be used like: ``*/5 * * * *``. ' 'Only used if the audit is CONTINUOUS.')) parser.add_argument( '-g', '--goal', dest='goal', metavar='', help=_('Goal UUID or name associated to this audit.')) parser.add_argument( '-s', '--strategy', dest='strategy', metavar='', help=_('Strategy UUID or name associated to this audit.')) parser.add_argument( '-a', '--audit-template', dest='audit_template_uuid', metavar='', help=_('Audit template used for this audit (name or uuid).')) parser.add_argument( '--auto-trigger', dest='auto_trigger', action='store_true', default=False, help=_('Trigger automatically action plan ' 'once audit is succeeded.')) parser.add_argument( '--name', dest='name', metavar='', help=_('Name for this audit.')) parser.add_argument( '--start-time', dest='start_time', metavar='', help=_('CONTINUOUS audit local start time. ' 'Format: YYYY-MM-DD hh:mm:ss')) parser.add_argument( '--end-time', dest='end_time', metavar='', help=_('CONTINUOUS audit local end time. ' 'Format: YYYY-MM-DD hh:mm:ss')) parser.add_argument( '--force', dest='force', action='store_true', help=_('Launch audit even if action plan ' 'is ongoing. default is False')) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") field_list = ['audit_template_uuid', 'audit_type', 'parameters', 'interval', 'goal', 'strategy', 'auto_trigger', 'name'] api_ver = self.app_args.os_infra_optim_api_version if api_versioning.allow_start_end_audit_time(api_ver): if parsed_args.start_time is not None: field_list.append('start_time') if parsed_args.end_time is not None: field_list.append('end_time') if api_versioning.launch_audit_forced(api_ver): if parsed_args.force is not None: field_list.append('force') fields = dict((k, v) for (k, v) in vars(parsed_args).items() if k in field_list and v is not None) fields = common_utils.args_array_to_dict(fields, 'parameters') if fields.get('audit_template_uuid'): if not uuidutils.is_uuid_like(fields['audit_template_uuid']): fields['audit_template_uuid'] = client.audit_template.get( fields['audit_template_uuid']).uuid audit = client.audit.create(**fields) if audit.strategy_name is None: audit.strategy_name = 'auto' columns = res_fields.AUDIT_FIELDS column_headers = res_fields.AUDIT_FIELD_LABELS columns, column_headers = drop_unsupported_field( self.app_args, columns, column_headers) return column_headers, utils.get_item_properties(audit, columns) class UpdateAudit(command.ShowOne): """Update audit command.""" def get_parser(self, prog_name): parser = super(UpdateAudit, self).get_parser(prog_name) parser.add_argument( 'audit', metavar='', help=_("UUID or name of the audit.")) parser.add_argument( 'op', metavar='', choices=['add', 'replace', 'remove'], help=_("Operation: 'add', 'replace', or 'remove'.")) parser.add_argument( 'attributes', metavar='', nargs='+', action='append', default=[], help=_("Attribute to add, replace, or remove. Can be specified " "multiple times. For 'remove', only is necessary.")) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") patch = common_utils.args_array_to_patch( parsed_args.op, parsed_args.attributes[0], exclude_fields=['/interval']) audit = client.audit.update(parsed_args.audit, patch) columns = res_fields.AUDIT_FIELDS column_headers = res_fields.AUDIT_FIELD_LABELS columns, column_headers = drop_unsupported_field( self.app_args, columns, column_headers) return column_headers, utils.get_item_properties(audit, columns) class DeleteAudit(command.Command): """Delete audit command.""" def get_parser(self, prog_name): parser = super(DeleteAudit, self).get_parser(prog_name) parser.add_argument( 'audits', metavar='', nargs='+', help=_('UUID or name of the audit'), ) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") for audit in parsed_args.audits: client.audit.delete(audit) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/audit_template.py0000664000175000017500000000713700000000000024550 0ustar00zuulzuul00000000000000# # Copyright 2013 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 watcherclient.common import base from watcherclient.common import utils from watcherclient import exceptions as exc CREATION_ATTRIBUTES = ['description', 'name', 'goal', 'strategy', 'scope'] class AuditTemplate(base.Resource): def __repr__(self): return "" % self._info class AuditTemplateManager(base.Manager): resource_class = AuditTemplate @staticmethod def _path(id_=None): return '/v1/audit_templates/%s' % id_ if id_ else '/v1/audit_templates' def list(self, name=None, goal=None, strategy=None, limit=None, sort_key=None, sort_dir=None, detail=False, marker=None): """Retrieve a list of audit template. :param name: Name of the audit template :param limit: The maximum number of results to return per request, if: 1) limit > 0, the maximum number of audit templates to return. 2) limit == 0, return the entire list of audit_templates. 3) limit param is NOT specified (None), the number of items returned respect the maximum imposed by the Watcher API (see Watcher's api.max_limit option). :param sort_key: Optional, field used for sorting. :param sort_dir: Optional, direction of sorting, either 'asc' (the default) or 'desc'. :param detail: Optional, boolean whether to return detailed information about audit_templates. :param marker: Optional, UUID of the last audit template of the previous page. :returns: A list of audit templates. """ if limit is not None: limit = int(limit) filters = utils.common_filters(limit, sort_key, sort_dir, marker) if name is not None: filters.append('name=%s' % name) if goal is not None: filters.append("goal=%s" % goal) if strategy is not None: filters.append("strategy=%s" % strategy) path = '' if detail: path += 'detail' if filters: path += '?' + '&'.join(filters) if limit is None: return self._list(self._path(path), "audit_templates") else: return self._list_pagination(self._path(path), "audit_templates", limit=limit) def get(self, audit_template_id): try: return self._list(self._path(audit_template_id))[0] except IndexError: return None def create(self, **kwargs): new = {} for (key, value) in kwargs.items(): if key in CREATION_ATTRIBUTES: new[key] = value else: raise exc.InvalidAttribute() return self._create(self._path(), new) def delete(self, audit_template_id): return self._delete(self._path(audit_template_id)) def update(self, audit_template_id, patch): return self._update(self._path(audit_template_id), patch) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/audit_template_shell.py0000664000175000017500000003071100000000000025731 0ustar00zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import argparse from osc_lib import utils from oslo_utils import uuidutils from watcherclient._i18n import _ from watcherclient.common import command from watcherclient.common import utils as common_utils from watcherclient import exceptions from watcherclient.v1 import resource_fields as res_fields class ShowAuditTemplate(command.ShowOne): """Show detailed information about a given audit template.""" def get_parser(self, prog_name): parser = super(ShowAuditTemplate, self).get_parser(prog_name) parser.add_argument( 'audit_template', metavar='', help=_('UUID or name of the audit template'), ) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") audit_template_uuid = parsed_args.audit_template try: audit_template = client.audit_template.get(audit_template_uuid) except exceptions.HTTPNotFound as exc: raise exceptions.CommandError(str(exc)) columns = res_fields.AUDIT_TEMPLATE_FIELDS column_headers = res_fields.AUDIT_TEMPLATE_FIELD_LABELS return column_headers, utils.get_item_properties( audit_template, columns) class ListAuditTemplate(command.Lister): """List information on retrieved audit templates.""" def get_parser(self, prog_name): parser = super(ListAuditTemplate, self).get_parser(prog_name) parser.add_argument( '--detail', dest='detail', action='store_true', default=False, help=_("Show detailed information about audit templates.")) parser.add_argument( '--goal', dest='goal', metavar='', help=_('UUID or name of the goal used for filtering.')) parser.add_argument( '--strategy', dest='strategy', metavar='', help=_('UUID or name of the strategy used for filtering.')) parser.add_argument( '--limit', metavar='', type=int, help=_('Maximum number of audit templates to return per request, ' '0 for no limit. Default is the maximum number used ' 'by the Watcher API Service.')) parser.add_argument( '--sort-key', metavar='', help=_('Audit template field that will be used for sorting.')) parser.add_argument( '--sort-dir', metavar='', choices=['asc', 'desc'], help=_('Sort direction: "asc" (the default) or "desc".')) parser.add_argument( '--marker', dest='marker', metavar='', default=None, help=_('UUID of the last audit template of the previous page; ' 'displays list of audit templates after "marker".')) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") params = {} # Optional if parsed_args.goal: params['goal'] = parsed_args.goal # Optional if parsed_args.strategy: params['strategy'] = parsed_args.strategy if parsed_args.detail: fields = res_fields.AUDIT_TEMPLATE_FIELDS field_labels = res_fields.AUDIT_TEMPLATE_FIELD_LABELS else: fields = res_fields.AUDIT_TEMPLATE_SHORT_LIST_FIELDS field_labels = res_fields.AUDIT_TEMPLATE_SHORT_LIST_FIELD_LABELS params.update(common_utils.common_params_for_list( parsed_args, fields, field_labels)) data = client.audit_template.list(**params) return (field_labels, (utils.get_item_properties(item, fields) for item in data)) class CreateAuditTemplate(command.ShowOne): """Create new audit template.""" def get_parser(self, prog_name): class SmartFormatter(argparse.HelpFormatter): def _split_lines(self, text, width): if '\n' in text: return text.splitlines() else: return argparse.HelpFormatter._split_lines( self, text, width) parser = super(CreateAuditTemplate, self).get_parser( prog_name, formatter_class=SmartFormatter) parser.add_argument( 'name', metavar='', help=_('Name for this audit template.')) parser.add_argument( 'goal', metavar='', help=_('Goal UUID or name associated to this audit template.')) parser.add_argument( '-s', '--strategy', dest='strategy', metavar='', help=_('Strategy UUID or name associated to this audit template.')) parser.add_argument( '-d', '--description', metavar='', help=_('Description of the audit template.')) parser.add_argument( '--scope', metavar='', help=_("Part of the cluster on which an audit will be done.\n" "Can be provided either in yaml or json file.\n" "YAML example::\n" "\n" " - compute:\n" " - host_aggregates:\n" " - id: 1\n" " - id: 2\n" " - id: 3\n" " - availability_zones:\n" " - name: AZ1\n" " - name: AZ2\n" " - exclude:\n" " - instances:\n" " - uuid: UUID1\n" " - uuid: UUID2\n" " - compute_nodes:\n" " - name: compute1\n" " - storage: \n" " - availability_zones:\n" " - name: AZ1\n" " - name: AZ2\n" " - volume_types:\n" " - name: lvm1\n" " - name: lvm2\n" " - exclude:\n" " - storage_pools:\n" " - name: host0@backend0#pool0\n" " - name: host1@backend1#pool1\n" " - volumes:\n" " - uuid: UUID1\n" " - uuid: UUID2\n" " - projects:\n" " - uuid: UUID1\n" " - uuid: UUID2\n" "\n" "JSON example::\n" "\n" " [\n" " {\"compute\":\n" " [{\"host_aggregates\": [\n" " {\"id\": 1},\n" " {\"id\": 2},\n" " {\"id\": 3}]},\n" " {\"availability_zones\": [\n" " {\"name\": \"AZ1\"},\n" " {\"name\": \"AZ2\"}]},\n" " {\"exclude\": [\n" " {\"instances\": [\n" " {\"uuid\": \"UUID1\"},\n" " {\"uuid\": \"UUID2\"}\n" " ]},\n" " {\"compute_nodes\": [\n" " {\"name\": \"compute1\"}\n" " ]}\n" " ]}]\n" " },\n" " {\"storage\":\n" " [{\"availability_zones\": [\n" " {\"name\": \"AZ1\"},\n" " {\"name\": \"AZ2\"}]},\n" " {\"volume_types\": [\n" " {\"name\": \"lvm1\"},\n" " {\"name\": \"lvm2\"}]},\n" " {\"exclude\": [\n" " {\"storage_pools\": [\n" " {\"name\": \"host0@backend0#pool0\"},\n" " {\"name\": \"host1@backend1#pool1\"}\n" " ]},\n" " {\"volumes\": [\n" " {\"uuid\": \"UUID1\"},\n" " {\"uuid\": \"UUID2\"}\n" " ]},\n" " {\"projects\": [\n" " {\"uuid\": \"UUID1\"},\n" " {\"uuid\": \"UUID2\"}\n" " ]},\n" " ]}]\n" " }\n" " ]\n" ) ) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") field_list = ['description', 'name', 'goal', 'strategy', 'scope'] fields = dict((k, v) for (k, v) in vars(parsed_args).items() if k in field_list and v is not None) # mandatory if not uuidutils.is_uuid_like(fields['goal']): fields['goal'] = client.goal.get(fields['goal']).uuid # optional if fields.get('strategy'): if not uuidutils.is_uuid_like(fields['strategy']): fields['strategy'] = client.strategy.get( fields['strategy']).uuid if fields.get('scope'): fields['scope'] = common_utils.serialize_file_to_dict( fields['scope']) audit_template = client.audit_template.create(**fields) columns = res_fields.AUDIT_TEMPLATE_FIELDS column_headers = res_fields.AUDIT_TEMPLATE_FIELD_LABELS return (column_headers, utils.get_item_properties(audit_template, columns)) class UpdateAuditTemplate(command.ShowOne): """Update audit template command.""" def get_parser(self, prog_name): parser = super(UpdateAuditTemplate, self).get_parser(prog_name) parser.add_argument( 'audit_template', metavar='', help=_("UUID or name of the audit_template.")) parser.add_argument( 'op', metavar='', choices=['add', 'replace', 'remove'], help=_("Operation: 'add', 'replace', or 'remove'.")) parser.add_argument( 'attributes', metavar='', nargs='+', action='append', default=[], help=_("Attribute to add, replace, or remove. Can be specified " "multiple times. For 'remove', only is necessary.")) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") patch = common_utils.args_array_to_patch( parsed_args.op, parsed_args.attributes[0]) audit_template = client.audit_template.update( parsed_args.audit_template, patch) columns = res_fields.AUDIT_TEMPLATE_FIELDS column_headers = res_fields.AUDIT_TEMPLATE_FIELD_LABELS return column_headers, utils.get_item_properties( audit_template, columns) class DeleteAuditTemplate(command.Command): """Delete audit template command.""" def get_parser(self, prog_name): parser = super(DeleteAuditTemplate, self).get_parser(prog_name) parser.add_argument( 'audit_templates', metavar='', nargs='+', help=_('UUID or name of the audit template'), ) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") for audit_template in parsed_args.audit_templates: client.audit_template.delete(audit_template) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/client.py0000664000175000017500000000477400000000000023031 0ustar00zuulzuul00000000000000# Copyright 2012 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from watcherclient._i18n import _ from watcherclient.common import httpclient from watcherclient import exceptions from watcherclient import v1 class Client(object): """Client for the Watcher v1 API. :param string endpoint: A user-supplied endpoint URL for the watcher service. :param function token: Provides token for authentication. :param integer timeout: Allows customization of the timeout for client http requests. (optional) """ def __init__(self, endpoint=None, *args, **kwargs): """Initialize a new client for the Watcher v1 API.""" if kwargs.get('os_infra_optim_api_version'): kwargs['api_version_select_state'] = "user" else: if not endpoint: raise exceptions.EndpointException( _("Must provide 'endpoint' if os_infra_optim_api_version " "isn't specified")) # If the user didn't specify a version, use a cached version if # one has been stored host, netport = httpclient.get_server(endpoint) kwargs['api_version_select_state'] = "default" kwargs['os_infra_optim_api_version'] = httpclient.DEFAULT_VER self.http_client = httpclient._construct_http_client( endpoint, *args, **kwargs) self.audit = v1.AuditManager(self.http_client) self.audit_template = v1.AuditTemplateManager(self.http_client) self.action = v1.ActionManager(self.http_client) self.action_plan = v1.ActionPlanManager(self.http_client) self.goal = v1.GoalManager(self.http_client) self.scoring_engine = v1.ScoringEngineManager(self.http_client) self.service = v1.ServiceManager(self.http_client) self.strategy = v1.StrategyManager(self.http_client) self.data_model = v1.DataModelManager(self.http_client) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/data_model.py0000664000175000017500000000352000000000000023630 0ustar00zuulzuul00000000000000# Copyright 2019 ZTE Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from watcherclient.common import base from watcherclient.common import utils class DataModel(base.Resource): def __repr__(self): return "" % self._info class DataModelManager(base.Manager): resource_class = DataModel @staticmethod def _path(filters=None): if filters: path = '/v1/data_model/%s' % filters else: path = '/v1/data_model' return path def list(self, data_model_type='compute', audit=None): """Retrieve a list of data model. :param data_model_type: The type of data model user wants to list. Supported values: compute. Future support values: storage, baremetal. The default value is compute. :param audit: The UUID of the audit, used to filter data model by the scope in audit. :returns: A list of data model. """ path = '' filters = utils.common_filters() if audit: filters.append('audit_uuid=%s' % audit) filters.append('data_model_type=%s' % data_model_type) path += '?' + '&'.join(filters) return self._list(self._path(path))[0] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/data_model_shell.py0000664000175000017500000000573700000000000025033 0ustar00zuulzuul00000000000000# Copyright 2019 ZTE Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from watcherclient._i18n import _ from watcherclient.common import command from watcherclient import exceptions from watcherclient.v1 import resource_fields as res_fields class ListDataModel(command.Lister): """List information on retrieved data model.""" def get_parser(self, prog_name): parser = super(ListDataModel, self).get_parser(prog_name) parser.add_argument( '--type', metavar='', dest='type', help=_('Type of Datamodel user want to list. ' 'Supported values: compute. ' 'Future support values: storage, baremetal. ' 'Default type is compute.')) parser.add_argument( '--audit', metavar='', dest='audit', help=_('UUID of the audit')) parser.add_argument( '--detail', dest='detail', action='store_true', default=False, help=_("Show detailed information about data model.")) return parser def get_tuple(self, dic, fields): ret_tup = [] for item in fields: ret_tup.append(dic.get(item)) return tuple(ret_tup) def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") allowed_type = ['compute', 'storage', 'baremetal'] params = {} if parsed_args.audit: params["audit"] = parsed_args.audit if parsed_args.type: if parsed_args.type not in allowed_type: raise exceptions.CommandError( 'Type %s error, ' 'Please check the valid type!' % parsed_args.type) params["data_model_type"] = parsed_args.type try: data_model = client.data_model.list(**params) except exceptions.HTTPNotFound as exc: raise exceptions.CommandError(str(exc)) # TODO(chenker) Add Storage MODEL_FIELDS when using Storage Datamodel. if parsed_args.detail: fields = res_fields.COMPUTE_MODEL_LIST_FIELDS field_labels = res_fields.COMPUTE_MODEL_LIST_FIELD_LABELS else: fields = res_fields.COMPUTE_MODEL_SHORT_LIST_FIELDS field_labels = res_fields.COMPUTE_MODEL_SHORT_LIST_FIELD_LABELS return (field_labels, (self.get_tuple(item, fields) for item in data_model.context)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/goal.py0000664000175000017500000000500400000000000022460 0ustar00zuulzuul00000000000000# # Copyright 2013 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 watcherclient.common import base from watcherclient.common import utils class Goal(base.Resource): def __repr__(self): return "" % self._info class GoalManager(base.Manager): resource_class = Goal @staticmethod def _path(goal=None): return '/v1/goals/%s' % goal if goal else '/v1/goals' def list(self, limit=None, sort_key=None, sort_dir=None, detail=False, marker=None): """Retrieve a list of goal. :param limit: The maximum number of results to return per request, if: 1) limit > 0, the maximum number of audits to return. 2) limit == 0, return the entire list of audits. 3) limit param is NOT specified (None), the number of items returned respect the maximum imposed by the Watcher API (see Watcher's api.max_limit option). :param sort_key: Optional, field used for sorting. :param sort_dir: Optional, direction of sorting, either 'asc' (the default) or 'desc'. :param detail: Optional, boolean whether to return detailed information about audits. :param marker: Optional, UUID of the last goal in the previous page. :returns: A list of goals. """ if limit is not None: limit = int(limit) filters = utils.common_filters(limit, sort_key, sort_dir, marker) path = '' if detail: path += 'detail' if filters: path += '?' + '&'.join(filters) if limit is None: return self._list(self._path(path), "goals") else: return self._list_pagination(self._path(path), "goals", limit=limit) def get(self, goal): try: return self._list(self._path(goal))[0] except IndexError: return None ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/goal_shell.py0000664000175000017500000001243000000000000023650 0ustar00zuulzuul00000000000000# # Copyright 2013 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 io from osc_lib import utils from watcherclient._i18n import _ from watcherclient.common import command from watcherclient.common import utils as common_utils from watcherclient import exceptions from watcherclient.v1 import resource_fields as res_fields class ShowGoal(command.ShowOne): """Show detailed information about a given goal.""" def get_parser(self, prog_name): parser = super(ShowGoal, self).get_parser(prog_name) parser.add_argument( 'goal', metavar='', help=_('UUID or name of the goal'), ) return parser def _format_indicator_spec_table(self, spec, parsed_args): out = io.StringIO() self.formatter.emit_one( column_names=list(field.capitalize() for field in spec.keys()), data=utils.get_dict_properties(spec, spec.keys()), stdout=out, parsed_args=parsed_args, ) return out.getvalue() or '' def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") try: goal = client.goal.get(parsed_args.goal) except exceptions.HTTPNotFound as exc: raise exceptions.CommandError(str(exc)) columns = res_fields.GOAL_FIELDS column_headers = res_fields.GOAL_FIELD_LABELS if parsed_args.formatter == 'table': indicator_specs = '' # Format complex data types: for indicator_spec in goal.efficacy_specification: indicator_specs += self._format_indicator_spec_table( indicator_spec, parsed_args) # Update the raw efficacy specs with the formatted one goal.efficacy_specification = indicator_specs return column_headers, utils.get_item_properties(goal, columns) class ListGoal(command.Lister): """List information on retrieved goals.""" def get_parser(self, prog_name): parser = super(ListGoal, self).get_parser(prog_name) parser.add_argument( '--detail', dest='detail', action='store_true', default=False, help=_("Show detailed information about each goal.")) parser.add_argument( '--limit', metavar='', type=int, help=_('Maximum number of goals to return per request, ' '0 for no limit. Default is the maximum number used ' 'by the Watcher API Service.')) parser.add_argument( '--sort-key', metavar='', help=_('Goal field that will be used for sorting.')) parser.add_argument( '--sort-dir', metavar='', choices=['asc', 'desc'], help=_('Sort direction: "asc" (the default) or "desc".')) parser.add_argument( '--marker', dest='marker', metavar='', default=None, help=_('UUID of the last goal in the previous page; ' 'displays list of goals after "marker".')) return parser def _format_indicator_spec_table(self, goal, parsed_args): out = io.StringIO() efficacy_specification = goal.efficacy_specification fields = ['name', 'unit'] self.formatter.emit_list( column_names=list(field.capitalize() for field in fields), data=[utils.get_dict_properties(spec, fields) for spec in efficacy_specification], stdout=out, parsed_args=parsed_args, ) return out.getvalue() or '' def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") if parsed_args.detail: fields = res_fields.GOAL_FIELDS field_labels = res_fields.GOAL_FIELD_LABELS else: fields = res_fields.GOAL_SHORT_LIST_FIELDS field_labels = res_fields.GOAL_SHORT_LIST_FIELD_LABELS params = {} params.update( common_utils.common_params_for_list( parsed_args, fields, field_labels)) try: data = client.goal.list(**params) except exceptions.HTTPNotFound as ex: raise exceptions.CommandError(str(ex)) if parsed_args.formatter == 'table': for goal in data: # Update the raw efficacy specs with the formatted one goal.efficacy_specification = ( self._format_indicator_spec_table(goal, parsed_args)) return (field_labels, (utils.get_item_properties(item, fields) for item in data)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/resource_fields.py0000775000175000017500000001412600000000000024723 0ustar00zuulzuul00000000000000# # Copyright 2015 b<>com # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # Audit Template AUDIT_TEMPLATE_FIELDS = [ 'uuid', 'created_at', 'updated_at', 'deleted_at', 'description', 'name', 'goal_name', 'strategy_name', 'scope'] AUDIT_TEMPLATE_FIELD_LABELS = [ 'UUID', 'Created At', 'Updated At', 'Deleted At', 'Description', 'Name', 'Goal', 'Strategy', 'Audit Scope'] AUDIT_TEMPLATE_SHORT_LIST_FIELDS = [ 'uuid', 'name', 'goal_name', 'strategy_name'] AUDIT_TEMPLATE_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Goal', 'Strategy'] # Audit AUDIT_FIELDS = ['uuid', 'name', 'created_at', 'updated_at', 'deleted_at', 'state', 'audit_type', 'parameters', 'interval', 'goal_name', 'strategy_name', 'scope', 'auto_trigger', 'next_run_time', 'hostname', 'start_time', 'end_time', 'force'] AUDIT_FIELD_LABELS = ['UUID', 'Name', 'Created At', 'Updated At', 'Deleted At', 'State', 'Audit Type', 'Parameters', 'Interval', 'Goal', 'Strategy', 'Audit Scope', 'Auto Trigger', 'Next Run Time', 'Hostname', 'Start Time', 'End Time', 'Force'] AUDIT_SHORT_LIST_FIELDS = ['uuid', 'name', 'audit_type', 'state', 'goal_name', 'strategy_name', 'auto_trigger'] AUDIT_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Audit Type', 'State', 'Goal', 'Strategy', 'Auto Trigger'] # Action Plan ACTION_PLAN_FIELDS = ['uuid', 'created_at', 'updated_at', 'deleted_at', 'audit_uuid', 'strategy_name', 'state', 'efficacy_indicators', 'global_efficacy', 'hostname'] ACTION_PLAN_FIELD_LABELS = ['UUID', 'Created At', 'Updated At', 'Deleted At', 'Audit', 'Strategy', 'State', 'Efficacy indicators', 'Global efficacy', 'Hostname'] ACTION_PLAN_SHORT_LIST_FIELDS = ['uuid', 'audit_uuid', 'state', 'updated_at', 'global_efficacy'] ACTION_PLAN_SHORT_LIST_FIELD_LABELS = ['UUID', 'Audit', 'State', 'Updated At', 'Global efficacy'] GLOBAL_EFFICACY_FIELDS = ['value', 'unit', 'name', 'description'] # Action ACTION_FIELDS = ['uuid', 'created_at', 'updated_at', 'deleted_at', 'parents', 'state', 'action_plan_uuid', 'action_type', 'input_parameters', 'description'] ACTION_FIELD_LABELS = ['UUID', 'Created At', 'Updated At', 'Deleted At', 'Parents', 'State', 'Action Plan', 'Action', 'Parameters', 'Description'] ACTION_SHORT_LIST_FIELDS = ['uuid', 'parents', 'state', 'action_plan_uuid', 'action_type'] ACTION_SHORT_LIST_FIELD_LABELS = ['UUID', 'Parents', 'State', 'Action Plan', 'Action'] # Goals GOAL_FIELDS = ['uuid', 'name', 'display_name', 'efficacy_specification'] GOAL_FIELD_LABELS = ['UUID', 'Name', 'Display name', 'Efficacy specification'] GOAL_SHORT_LIST_FIELDS = ['uuid', 'name', 'display_name'] GOAL_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Display name'] # Strategies STRATEGY_FIELDS = ['uuid', 'name', 'display_name', 'goal_name', 'parameters_spec'] STRATEGY_FIELD_LABELS = ['UUID', 'Name', 'Display name', 'Goal', 'Parameters spec'] # Data Model COMPUTE_MODEL_LIST_FIELDS = [ 'server_uuid', 'server_name', 'server_vcpus', 'server_memory', 'server_disk', 'server_state', 'node_uuid', 'node_hostname', 'node_vcpus', 'node_vcpu_ratio', 'node_memory', 'node_memory_ratio', 'node_disk', 'node_disk_ratio', 'node_state'] COMPUTE_MODEL_LIST_FIELD_LABELS = [ 'Server_UUID', 'Server Name', 'Server Vcpus', 'Server Memory', 'Server Disk', 'Server State', 'Node UUID', 'Node Host Name', 'Node Vcpus', 'Node Vcpu Ratio', 'Node Memory', 'Node Memory Ratio', 'Node Disk', 'Node Disk Ratio', 'Node State'] COMPUTE_MODEL_SHORT_LIST_FIELDS = [ 'server_uuid', 'server_name', 'server_state', 'node_uuid', 'node_hostname'] COMPUTE_MODEL_SHORT_LIST_FIELD_LABELS = [ 'Server UUID', 'Server Name', 'Server State', 'Node UUID', 'Node Host Name'] STRATEGY_SHORT_LIST_FIELDS = ['uuid', 'name', 'display_name', 'goal_name'] STRATEGY_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Display name', 'Goal'] STRATEGY_STATE_FIELDS = ['type', 'state', 'mandatory', 'comment'] STRATEGY_STATE_FIELD_LABELS = ['Type', 'State', 'Mandatory', 'Comment'] # Metric Collector METRIC_COLLECTOR_FIELDS = ['uuid', 'created_at', 'updated_at', 'deleted_at', 'endpoint', 'category'] METRIC_COLLECTOR_FIELD_LABELS = ['UUID', 'Created At', 'Updated At', 'Deleted At', 'Endpoint URL', 'Metric Category'] METRIC_COLLECTOR_SHORT_LIST_FIELDS = ['uuid', 'endpoint', 'category'] METRIC_COLLECTOR_SHORT_LIST_FIELD_LABELS = ['UUID', 'Endpoint URL', 'Metric Category'] # Scoring Engines SCORING_ENGINE_FIELDS = ['uuid', 'name', 'description', 'metainfo'] SCORING_ENGINE_FIELD_LABELS = ['UUID', 'Name', 'Description', 'Metainfo'] SCORING_ENGINE_SHORT_LIST_FIELDS = ['uuid', 'name', 'description'] SCORING_ENGINE_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Description'] # Services SERVICE_FIELDS = ['id', 'name', 'host', 'status', 'last_seen_up'] SERVICE_FIELD_LABELS = ['ID', 'Name', 'Host', 'Status', 'Last seen up'] SERVICE_SHORT_LIST_FIELDS = ['id', 'name', 'host', 'status'] SERVICE_SHORT_LIST_FIELD_LABELS = ['ID', 'Name', 'Host', 'Status'] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/scoring_engine.py0000664000175000017500000000512700000000000024535 0ustar00zuulzuul00000000000000# # Copyright 2016 Intel # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from watcherclient.common import base from watcherclient.common import utils class ScoringEngine(base.Resource): def __repr__(self): return "" % self._info class ScoringEngineManager(base.Manager): resource_class = ScoringEngine @staticmethod def _path(scoring_engine=None): return ('/v1/scoring_engines/%s' % scoring_engine if scoring_engine else '/v1/scoring_engines') def list(self, limit=None, sort_key=None, sort_dir=None, detail=False): """Retrieve a list of scoring engines. :param limit: The maximum number of results to return per request, if: 1) limit > 0, the maximum number of scoring engines to return. 2) limit == 0, return the entire list of scoring engines. 3) limit param is NOT specified (None), the number of items returned respect the maximum imposed by the Watcher API (see Watcher's api.max_limit option). :param sort_key: Optional, field used for sorting. :param sort_dir: Optional, direction of sorting, either 'asc' (the default) or 'desc'. :param detail: Optional, boolean whether to return detailed information about scoring engines. :returns: A list of scoring engines. """ if limit is not None: limit = int(limit) filters = utils.common_filters(limit, sort_key, sort_dir) path = '' if detail: path += 'detail' if filters: path += '?' + '&'.join(filters) if limit is None: return self._list(self._path(path), "scoring_engines") else: return self._list_pagination(self._path(path), "scoring_engines", limit=limit) def get(self, scoring_engine_name): try: return self._list(self._path(scoring_engine_name))[0] except IndexError: return None ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/scoring_engine_shell.py0000664000175000017500000000721500000000000025724 0ustar00zuulzuul00000000000000# Copyright (c) 2016 Intel # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES 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 from watcherclient._i18n import _ from watcherclient.common import command from watcherclient.common import utils as common_utils from watcherclient import exceptions from watcherclient.v1 import resource_fields as res_fields class ShowScoringEngine(command.ShowOne): """Show detailed information about a given scoring engine.""" def get_parser(self, prog_name): parser = super(ShowScoringEngine, self).get_parser(prog_name) parser.add_argument( 'scoring_engine', metavar='', help=_('Name of the scoring engine'), ) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") try: scoring_engine = client.scoring_engine.get( parsed_args.scoring_engine) except exceptions.HTTPNotFound as exc: raise exceptions.CommandError(str(exc)) columns = res_fields.SCORING_ENGINE_FIELDS column_headers = res_fields.SCORING_ENGINE_FIELD_LABELS return column_headers, utils.get_item_properties(scoring_engine, columns) class ListScoringEngine(command.Lister): """List information on retrieved scoring engines.""" def get_parser(self, prog_name): parser = super(ListScoringEngine, self).get_parser(prog_name) parser.add_argument( '--detail', dest='detail', action='store_true', default=False, help=_("Show detailed information about scoring engines.")) parser.add_argument( '--limit', metavar='', type=int, help=_('Maximum number of actions to return per request, ' '0 for no limit. Default is the maximum number used ' 'by the Watcher API Service.')) parser.add_argument( '--sort-key', metavar='', help=_('Action field that will be used for sorting.')) parser.add_argument( '--sort-dir', metavar='', choices=['asc', 'desc'], help=_('Sort direction: "asc" (the default) or "desc".')) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") params = {} if parsed_args.detail: fields = res_fields.SCORING_ENGINE_FIELDS field_labels = res_fields.SCORING_ENGINE_FIELD_LABELS else: fields = res_fields.SCORING_ENGINE_SHORT_LIST_FIELDS field_labels = res_fields.SCORING_ENGINE_SHORT_LIST_FIELD_LABELS params.update( common_utils.common_params_for_list( parsed_args, fields, field_labels)) try: data = client.scoring_engine.list(**params) except exceptions.HTTPNotFound as ex: raise exceptions.CommandError(str(ex)) return (field_labels, (utils.get_item_properties(item, fields) for item in data)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/service.py0000664000175000017500000000473000000000000023203 0ustar00zuulzuul00000000000000# # Copyright 2016 Servionica # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from watcherclient.common import base from watcherclient.common import utils class Service(base.Resource): def __repr__(self): return "" % self._info class ServiceManager(base.Manager): resource_class = Service @staticmethod def _path(service=None): return ('/v1/services/%s' % service if service else '/v1/services') def list(self, limit=None, sort_key=None, sort_dir=None, detail=False): """Retrieve a list of services. :param limit: The maximum number of results to return per request, if: 1) limit > 0, the maximum number of services to return. 2) limit == 0, return the entire list of services. 3) limit param is NOT specified (None), the number of items returned respect the maximum imposed by the Watcher API (see Watcher's api.max_limit option). :param sort_key: Optional, field used for sorting. :param sort_dir: Optional, direction of sorting, either 'asc' (the default) or 'desc'. :param detail: Optional, boolean whether to return detailed information about services. :returns: A list of services. """ if limit is not None: limit = int(limit) filters = utils.common_filters(limit, sort_key, sort_dir) path = '' if detail: path += 'detail' if filters: path += '?' + '&'.join(filters) if limit is None: return self._list(self._path(path), "services") else: return self._list_pagination(self._path(path), "services", limit=limit) def get(self, service): try: return self._list(self._path(service))[0] except IndexError: return None ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/service_shell.py0000664000175000017500000000667700000000000024406 0ustar00zuulzuul00000000000000# Copyright (c) 2016 Servionica # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES 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 from watcherclient._i18n import _ from watcherclient.common import command from watcherclient.common import utils as common_utils from watcherclient import exceptions from watcherclient.v1 import resource_fields as res_fields class ShowService(command.ShowOne): """Show detailed information about a given service.""" def get_parser(self, prog_name): parser = super(ShowService, self).get_parser(prog_name) parser.add_argument( 'service', metavar='', help=_('ID or name of the service'), ) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") try: service = client.service.get(parsed_args.service) except exceptions.HTTPNotFound as exc: raise exceptions.CommandError(str(exc)) columns = res_fields.SERVICE_FIELDS column_headers = res_fields.SERVICE_FIELD_LABELS return column_headers, utils.get_item_properties(service, columns) class ListService(command.Lister): """List information on retrieved services.""" def get_parser(self, prog_name): parser = super(ListService, self).get_parser(prog_name) parser.add_argument( '--detail', dest='detail', action='store_true', default=False, help=_("Show detailed information about each service.")) parser.add_argument( '--limit', metavar='', type=int, help=_('Maximum number of services to return per request, ' '0 for no limit. Default is the maximum number used ' 'by the Watcher API Service.')) parser.add_argument( '--sort-key', metavar='', help=_('Goal field that will be used for sorting.')) parser.add_argument( '--sort-dir', metavar='', choices=['asc', 'desc'], help='Sort direction: "asc" (the default) or "desc".') return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") params = {} if parsed_args.detail: fields = res_fields.SERVICE_FIELDS field_labels = res_fields.SERVICE_FIELD_LABELS else: fields = res_fields.SERVICE_SHORT_LIST_FIELDS field_labels = res_fields.SERVICE_SHORT_LIST_FIELD_LABELS params.update( common_utils.common_params_for_list( parsed_args, fields, field_labels)) try: data = client.service.list(**params) except exceptions.HTTPNotFound as ex: raise exceptions.CommandError(str(ex)) return (field_labels, (utils.get_item_properties(item, fields) for item in data)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/strategy.py0000664000175000017500000000577600000000000023420 0ustar00zuulzuul00000000000000# # Copyright 2013 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 urllib import parse from watcherclient.common import base from watcherclient.common import utils class Strategy(base.Resource): def __repr__(self): return "" % self._info class StrategyManager(base.Manager): resource_class = Strategy @staticmethod def _path(strategy=None, state=False): if strategy: path = '/v1/strategies/%s' % strategy if state: path = '/v1/strategies/%s/state' % strategy else: path = '/v1/strategies' return path def list(self, goal=None, limit=None, sort_key=None, sort_dir=None, detail=False, marker=None): """Retrieve a list of strategy. :param goal: The UUID of the goal to filter by :param limit: The maximum number of results to return per request, if: 1) limit > 0, the maximum number of audits to return. 2) limit == 0, return the entire list of audits. 3) limit param is NOT specified (None), the number of items returned respect the maximum imposed by the Watcher API (see Watcher's api.max_limit option). :param sort_key: Optional, field used for sorting. :param sort_dir: Optional, direction of sorting, either 'asc' (the default) or 'desc'. :param detail: Optional, boolean whether to return detailed information about audits. :param marker: Optional, UUID of the last strategy in the previous page. :returns: A list of audits. """ if limit is not None: limit = int(limit) filters = utils.common_filters(limit, sort_key, sort_dir, marker) if goal: filters.append(parse.urlencode(dict(goal=goal))) path = '' if detail: path += 'detail' if filters: path += '?' + '&'.join(filters) if limit is None: return self._list(self._path(path), "strategies") else: return self._list_pagination(self._path(path), "strategies", limit=limit) def get(self, strategy): try: return self._list(self._path(strategy))[0] except IndexError: return None def state(self, strategy): return self._list(self._path(strategy, state=True)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/v1/strategy_shell.py0000664000175000017500000001264700000000000024602 0ustar00zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES 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 from oslo_serialization import jsonutils from watcherclient._i18n import _ from watcherclient.common import command from watcherclient.common import utils as common_utils from watcherclient import exceptions from watcherclient.v1 import resource_fields as res_fields class ShowStrategy(command.ShowOne): """Show detailed information about a given strategy.""" def get_parser(self, prog_name): parser = super(ShowStrategy, self).get_parser(prog_name) parser.add_argument( 'strategy', metavar='', help=_('UUID or name of the strategy'), ) return parser def _format_spec(self, strategy): parameters_spec = strategy.parameters_spec.get('properties') if parameters_spec: return jsonutils.dumps(parameters_spec, indent=2) return {} def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") try: strategy = client.strategy.get(parsed_args.strategy) except exceptions.HTTPNotFound as exc: raise exceptions.CommandError(str(exc)) strategy.parameters_spec = self._format_spec(strategy) columns = res_fields.STRATEGY_FIELDS column_headers = res_fields.STRATEGY_FIELD_LABELS return column_headers, utils.get_item_properties(strategy, columns) class StateStrategy(command.Lister): """Retrieve information about strategy requirements.""" def get_parser(self, prog_name): parser = super(StateStrategy, self).get_parser(prog_name) parser.add_argument( 'strategy', metavar='', help=_('Name of the strategy'), ) return parser def _format_spec(self, requirements): for req in requirements: if isinstance(req.state, list): req.state = jsonutils.dumps(req.state, indent=2) return requirements def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") try: requirements = client.strategy.state(parsed_args.strategy) except exceptions.HTTPNotFound as exc: raise exceptions.CommandError(str(exc)) requirements = self._format_spec(requirements) columns = res_fields.STRATEGY_STATE_FIELDS column_headers = res_fields.STRATEGY_STATE_FIELD_LABELS return (column_headers, (utils.get_item_properties(item, columns) for item in requirements)) class ListStrategy(command.Lister): """List information on retrieved strategies.""" def get_parser(self, prog_name): parser = super(ListStrategy, self).get_parser(prog_name) parser.add_argument( '--goal', metavar='', dest='goal', help=_('UUID or name of the goal')) parser.add_argument( '--detail', dest='detail', action='store_true', default=False, help=_("Show detailed information about each strategy.")) parser.add_argument( '--limit', metavar='', type=int, help=_('Maximum number of strategies to return per request, ' '0 for no limit. Default is the maximum number used ' 'by the Watcher API Service.')) parser.add_argument( '--sort-key', metavar='', help=_('Goal field that will be used for sorting.')) parser.add_argument( '--sort-dir', metavar='', choices=['asc', 'desc'], help='Sort direction: "asc" (the default) or "desc".') parser.add_argument( '--marker', dest='marker', metavar='', default=None, help=_('UUID of the last strategy in the previous page; ' 'displays list of strategies after "marker".')) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") params = {} if parsed_args.detail: fields = res_fields.STRATEGY_FIELDS field_labels = res_fields.STRATEGY_FIELD_LABELS else: fields = res_fields.STRATEGY_SHORT_LIST_FIELDS field_labels = res_fields.STRATEGY_SHORT_LIST_FIELD_LABELS if parsed_args.goal: params["goal"] = parsed_args.goal params.update( common_utils.common_params_for_list( parsed_args, fields, field_labels)) try: data = client.strategy.list(**params) except exceptions.HTTPNotFound as ex: raise exceptions.CommandError(str(ex)) return (field_labels, (utils.get_item_properties(item, fields) for item in data)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1740768079.0 python_watcherclient-4.8.0/watcherclient/version.py0000664000175000017500000000131700000000000022700 0ustar00zuulzuul00000000000000# Copyright 2014 # The Cloudscaling Group, 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 pbr import version version_info = version.VersionInfo('python-watcherclient') __version__ = version_info.version_string()