././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5573387 python-designateclient-6.1.0/0000775000175000017500000000000000000000000016222 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/.coveragerc0000664000175000017500000000024600000000000020345 0ustar00zuulzuul00000000000000[run] branch = True source = designateclient omit = designateclient/tests/*,designateclient/functionaltests/*,designateclient/hacking/* [report] ignore_errors = True././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/.stestr.conf0000664000175000017500000000011100000000000020464 0ustar00zuulzuul00000000000000[DEFAULT] test_path=${OS_TEST_PATH:-./designateclient/tests} top_dir=./ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/.zuul.yaml0000664000175000017500000000037300000000000020166 0ustar00zuulzuul00000000000000- project: templates: - check-requirements - designate-devstack-jobs - openstack-cover-jobs - openstack-python3-jobs - openstackclient-plugin-jobs - publish-openstack-docs-pti - release-notes-jobs-python3 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031659.0 python-designateclient-6.1.0/AUTHORS0000664000175000017500000001040100000000000017266 0ustar00zuulzuul0000000000000098k <18552437190@163.com> Alex Monk Alok Jani Andreas Jaeger Andreas Jaeger Andreas Jaeger Andrew F. Ly Andrew Spiers Atsushi SAKAI Ben Nemec Betsy Luzader Brian DeHamer Cao Xuan Hoang Cedric Brandily Chaozhe.Chen Chen Christian Berendt Clenimar Filemon Corey Bryant Davide Guerri Deepak Dirk Mueller Doug Hellmann Dougal Matthews Dr. Jens Harbott Ekaterina Chernova Endre Karlson Endre Karlson Endre Karlson Eric Wehrmeister Erik Olof Gunnar Andersson Federico Ceratto Flavio Percoco Ghanshyam Mann Graham Hayes Graham Hayes Graham Hayes Graham Hayes Hangdong Zhang Hervé Beraud Igor Malinovskiy James Li Janonymous Jens Harbott Jeremy Stanley Joe Gordon Jordan Cazamias Joseph M KATO Tomoyuki Kiall Mac Innes Kiall Mac Innes Kiall Mac Innes Lakshmi N Sampath Manish Honap Marcus Furlong Michael Chapman Michael Johnson Mike Durnosvystov Monty Taylor Nguyen Hai Nicolas Bock Ondřej Nový OpenStack Release Bot Patrick Galbraith Paul Glass Pavel Kirpichyov Pradeep Kumar Singh Rahman Syed Reddy A, Penchal (pr8721) Rudolf Vriend Rui Chen Sascha Peilicke Sascha Peilicke Satyanarayana Patibandla Sean McGinnis Shane Wang Shuquan Huang Simon McCartney Stephen Finucane Swapnil Kulkarni (coolsvap) Takashi Kajinami Takashi Kajinami Tang Chen TerryHowe Tim Simmons Tony Breeds Tony Xu Venkata Mahesh Jonnalagadda Vieri <15050873171@163.com> Vu Cong Tuan abhishekkekane algerwang ashish-kumar-gupta cao.yuan dekehn ghanshyam howardlee jacky06 kangyufei kpdev likui lingyongxu liushuobj maaoyu melissaml miaoyuliang michaeltchapman niuke qingszhao ricolin ricolin rjrjr sonu.kumar venkatamahesh wu.shiming zhangboye ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/CONTRIBUTING.rst0000664000175000017500000000105400000000000020663 0ustar00zuulzuul00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps documented at: https://docs.openstack.org/infra/manual/developers.html Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: https://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad, not GitHub: https://bugs.launchpad.net/python-designateclient ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031659.0 python-designateclient-6.1.0/ChangeLog0000664000175000017500000004707700000000000020013 0ustar00zuulzuul00000000000000CHANGES ======= 6.1.0 ----- * reno: Update master for unmaintained/zed * Update master for stable/2024.1 * reno: Update master for unmaintained/xena * reno: Update master for unmaintained/wallaby * reno: Update master for unmaintained/victoria 6.0.1 ----- * reno: Update master for unmaintained/yoga * Bump hacking 6.0.0 ----- * Update python classifier in setup.cfg * Recordset: Remove deprecated --records argument * Use --record instead of deprecated --records * Remove translation sections from setup.cfg * Add coveragerc file * pyupgrade changes for Python3.8+ * Update master for stable/2023.2 5.3.0 ----- * Add python3.9 support in testing runtime * Add new API - zone pool move * Update master for stable/2023.1 * Add hacking check for line continuation backslash 5.2.0 ----- * Add shared zone commands * Provide option for delete zonefiles on zone delete * Update tox.ini for tox4 5.1.0 ----- * Update metadata in setup.cfg * Drop explicit queue configuration * Add Python3 antelope unit tests * Update master for stable/zed * Use TOX\_CONSTRAINTS\_FILE * Include SECONDARY zones into list command output 5.0.0 ----- * Fix quota list command for non-admin users * Update python testing as per zed cycle testing runtime * Fix missing --target-project-id for transfer req * Update zuul queue configuration * Update python jobs to zed template * Drop lower-constraints.txt and its testing * Update master for stable/yoga 4.5.0 ----- * Add profiling support to designateclient * Updating python testing classifier as per Yoga testing runtime * Add basic osc unit tests * Add Designate hacking checks to the client * Remove edit-managed from unsupported commands * Use py3 as the default runtime for tox * Cap jsonschema 3.2.0 as the minimal version * Remove H402 hacking as it is deprecated * Stop to use the \_\_future\_\_ module * Clarify that name or ID can be used on TLDs 4.4.0 ----- * Bump lower-constraint for decorator to 4.2.1 * Add Python3 yoga unit tests * Update master for stable/xena * setup.cfg: Replace dashes with underscores 4.3.0 ----- * Remove six and update lower-constraints appdirs * Add Python3 xena unit tests * Update master for stable/wallaby * Add Python3 wallaby unit tests * Remove install unnecessary packages * Fix lower-constrains job * Update readme.rst 4.2.0 ----- * bump py37 to py38 in tox.ini * Fixed zone transfer accept list command * Add auto generated OSC documentation * Fix doc reference builds * Add choices to \`--type\` command line argument * Increase hacking version * Update master for stable/victoria * requirements: Drop os-testr 4.1.0 ----- * Use unittest.mock instead of mock * Switch to newer openstackdocstheme and reno versions * Add Python3 victoria unit tests * Update master for stable/ussuri 4.0.0 ----- * Cleanup py27 support * Use unittest.mock instead of third party mock * Drop py27 support * Switch to Ussuri jobs 3.1.0 ----- * Update master for stable/train * Switch upper-constraints URL to static location * Fix "recordset list --all-projects" in interactive OSC * More cleanup * Removing unused code * Fix "zone list --all-projects" in interactive OSC 3.0.0 ----- * Removing v1 api support 2.12.0 ------ * Add Python 3 Train unit tests * Cap sphinx for py2 to match global requirements * OpenDev Migration Patch * Dropping the py35 testing * Uncap jsonschema * Update master for stable/stein * Update hacking version * Update json module to jsonutils * add python 3.7 unit test job * Add doc/requirements.txt to releasenotes tox environment * Change openstack-dev to openstack-discuss * Add Python 3.6 classifier to setup.cfg 2.11.0 ------ * Don't quote {posargs} in tox.ini * Use templates for cover and lower-constraints * add python 3.6 unit test job * switch documentation job to new PTI * import zuul job settings from project-config * Update reno for stable/rocky 2.10.0 ------ * Switch to stestr * Fix copy-pasta in quota command descriptions * server-get/update show wrong values about 'id' and 'update\_at' * Remove PyPI downloads * fix tox python3 overrides * Trivial: Update pypi url to new url * Updated from global requirements * Check item is one of six.string\_types not just str * add lower-constraints job * Updated from global requirements * Updated from global requirements * Add os-testr in test-requirements.txt * Updated from global requirements * Updated from global requirements * Updated from global requirements * Highlight V1 API removal * Improve the RecordSet Update UI * Update reno for stable/queens 2.9.0 ----- * Updated from global requirements * Partial Revert "Remove unneeded requirements file" * Updated from global requirements 2.8.0 ----- * Updated from global requirements * Split doc requirements into their own file * Remove unneeded requirements file * Improve recordset create UI * Avoid tox\_install.sh for constraints support * Use jobs from designate repo * Remove setting of version/release from releasenotes * Updated from global requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * Make remote error handling more robust * Updated from global requirements * Update the documentation link for doc migration * Update reno for stable/pike * Updated from global requirements * Update and optimize documentation links 2.7.0 ----- * Updated from global requirements * switch from oslosphinx to openstackdocstheme * import cli-reference material from openstack-manuals * rearrange the existing docs into the new standard layout * use setuptools to build the docs even for testing * Update cli docs * Add tsig key support to python-designateclient * Updated from global requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * tox: Whitelist rm * doc: Remove cruft from conf.py * Use Sphinx 1.5 warning-is-error * Explicitly set 'builders' option * Updated from global requirements * [Fix gate]Update test requirement * Updated from global requirements * Simplify OSC doc structure * Updated from global requirements * Set client module \_\_version\_\_ * Update reno for stable/ocata 2.6.0 ----- * Add attribute support to create zone cli * Show proper error on over quota commands * Fix broken link for modindex 2.5.0 ----- * Updated from global requirements * Add Python 3.5 classifier and venv * Updated from global requirements * Remove 'coding: utf-8' from license statement * Removes unnecessary utf-8 encoding * Add Constraints support * Updated from global requirements 2.4.0 ----- * Ensure X-Designate-Edit-Managed-Records header value is a string * Updated from global requirements * Show team and repo badges on README * Use tempest instead of Tempest-lib * Replace 'assertFalse(a in b)' with 'assertNotIn(a, b)' * Updated from global requirements * Updated from global requirements * Ensure X-Auth-All-Projects header value is a string * Updated from global requirements * Add plug-in summary for osc doc * Updated from global requirements * Updated from global requirements * Fix for bug 1627945 * Enable release notes translation * Changed the author and author-email value * Replace 'assertTrue(a in b)' with 'assertIn(a, b)' * Updated from global requirements * Add oslo.config to test-requirements * Updated from global requirements * Fix pep8 E501 line too long * record-create/update: Account for priority being 0 * Update reno for stable/newton 2.3.0 ----- * Updated from global requirements * Resolve FloatingIP PTR records clis argument's description * Docs update for FloatingIP's ptr records * Fix clis related to FloatingIP's ptr records * Updated from global requirements * Remove discover from test-requirements * Updated from global requirements * Updated from global requirements 2.2.0 ----- * Updated from global requirements * Updated from global requirements * Use osc\_lib instead of cliff * Updated from global requirements * Add quota set / list / reset commands * Mark the v1 API Client as deprecated * Add global flags to cli * Updated from global requirements * Use osc-lib instead of openstackclient * Revert "Add Global Flags for filtering results" * Updated from global requirements * Add pool listing to python designate client * Add \`openstack zone transfer accept list\` cli to designateclient * Allow cli to query "v2/recordsets" * Updated from global requirements * Add Global Flags for filtering results * Updated from global requirements * Use the correct capitalization of OpenStack * Updated from global requirements * Update the home-page with developer documentation * Add reno support for release notes * Switch to keystoneauth * Added CONTRIBUTING.rst file * Docs Update * Updated from global requirements * Don't use the 'keystone' client (functional tests) * Update doc examples to use keystoneauth * Update designate cli page with more examples Closes-Bug: #1578207 * Add python-openstackclient to test-requirements * Improve python-designateclient docs * Updated from global requirements * Add support for service status * Fix a race condition in one of the zone import tests * Run the functional tests in a more verbose mode * Show responses on zone and recordset deletes * Implement zone import * Doc changes for installation webpage * Fix an issue causing a blacklist test to fail * Add filtering to recordsets CLI * Show "status" and "action" for recordsets * Move all V2 Controllers to inherit from the v2 * Fixes 'openstack tld list' command's exception * Implement zone export * Updated from global requirements * Improved TestRecordsetNegative * Add a service catalog override to the functional tests * Updated from global requirements * Stop using non-existent method of Mock * Log stacktraces of MultipleExceptions in functionaltests * Updated from global requirements * Updated from global requirements * Updated from global requirements * Python3: Add support for urlparse 2.0.0 ----- * Allow the OSC CLI Plugin to walk pages * Improvement in zone transfer request command description * Fixed grammar mistake in shell.rst * Updated from global requirements * Update typos * Show output on transfer accept * Change to ShowOne to display output upon update * Updated from global requirements * Updated from global requirements * Put py34 first in the env order of tox * Replace assertEqual(None, \*) with assertIsNone in tests * Add a missing whitespace * Deprecated tox -downloadcache option removed * Indicate py34 support * Removes MANIFEST.in as it is not needed explicitely by PBR * Pass environment variables of proxy to tox * Remove py26 support from designateclient * Updated from global requirements * Updated from global requirements * Improve logging in designateclient * Support OS\_INTERFACE and set V2 API as default * Pass OS\_ENDPOINT\_TYPE from shell to client * Updated from global requirements * Adds changes related to py3 compatibility * Clearing out .pyc file before a tox run * Fix support for --os-endpoint * Updates shell doc of v1 client * Fixes doc for v2 cli * Add functional tests for blacklists * Add functional tests for tlds * Adds different filter option for zone listing * Updated from global requirements * Resolve TLD's by name * improve readme contents * Removes --name argument from zone set command * Fixes error over quota update * Fixes order of argument in assertEqual * Fixes typos in zone transfer request show command description * Add functional tests for recordsets * Flesh out zone-related test cases * Fixes exception message for zone creation command * Fixes target-project-id argument in zone transfer command * Removes name argument from openstack recordset set command * Fixes openstack zone blacklist set command * Added test cases for v2 client * Adds test cases for servers in designateclient * Adds test cases for domain in designate v1 client * Makes error msg more helpful * Add period in help message * Layout some functional tests for the V2 CLI 1.5.0 ----- * Updated from global requirements * Add formatting of output data * Added appropriate error message for designate cli * Meaningful Exception for over-quota on domains * Added test cases for reports in designateclient * Added diagnostics,sync and touch test cases for v1 designateclient * V2 CLI Support * Added quotas and servers test cases for designateclient * Added test cases for designate v1 client * Make is so exceptions have some message * Updated from global requirements * Update github's URL * Updated from global requirements * Implement socket timeout in v1 * Don't wildcard resolve names * Updated from global requirements * Updated from global requirements * Improve help strings 1.4.0 ----- * Convert byte to str for py3 compatibilty * Updated from global requirements * Updated from global requirements * Add some missing Unit Tests to test\_utils.py * Solve some py3 compatibilty issues * Fix py3 compatibilty issues * Updated from global requirements * Replacing print with print() to provide py 2/3 compatibility * Updated from global requirements * Updated from global requirements * Fix for ttl values * Log error message from EndpointNotFound exceptions * Removed peronal email address from example 1.3.0 ----- * Updated from global requirements * Fix backwards compat for edit\_managed/all\_tenants * Add .eggs to gitignore * Add backwards compat for edit\_managed/all\_tenants * Updated from global requirements * Updated from global requirements * Updated from global requirements * Update github link * Sync tox.ini and add py34 * Use oslosphinx from PyPi * Drop incubating theme from docs * Move all\_tenants and edit\_managed attributes to designate Client * Log a more informative error upon EndpointNotFound * Updated from global requirements * V2 Bindings * Add --edit-managed flag to cli * Updated from global requirements * Allow relative names in record-create 1.2.0 ----- * Update README to work with release tools * Uncap library requirements for liberty * Allow to use domain names instead of ids * Updated from global requirements * Added extra previllege to list all domains from all tenants * Updated from global requirements * Updated from global requirements * Fix if checking on ttl for Create/Update commands * Updated from global requirements * Workflow documentation is now in infra-manual 1.1.1 ----- * Refactor KS session creation and support CA certs * Move some useful code outside v1 * Make token + endpoints work correctly * Move session creation up to shell * Set defaults for endpoint\_type and service\_type * Updated from global requirements * Use keystone sessions for v1 client * Fixes homepage in metadata and internal docs * Updated from global requirements * Updated from global requirements * Fixes pypi tarball not delivering Apache 2.0 LICENSE file * Updated from global requirements * Updated from global requirements 1.1.0 ----- * Add support for SOA records * sync and touch Commands Extend the base.DeleteCommand * Updated from global requirements * Updated from global requirements * Add support for quotas for v1 cli / bindings * Enable hacking check H104 * Added oslosphinx theme, and enabled the incubating option * Enabled hacking check H401 * Support Keystone V3 authentication * Support better logging (Fixes --debug option) * Update hacking package, fix/ignore new style errors * Updated from global requirements * Update .gitreview for repository move * Use six.add\_metaclass instead of \_\_metaclass\_\_ * Add dummy tests 1.0.3 ----- * designate record-list should display record data * Correct Python bindings create-record example * Standardize doc requirements 1.0.2 ----- * Ensure that url stripping is done correctly * Sync with global-requirements 1.0.1 ----- * Support versionless catalog URLs * Correctly support multiple regions * Update global requirements * Fix misspellings in python designateclient * Disable verbose logging by default * There are no mox tests, so remove dependency * Remove dependencies on pep8, pyflakes and flake8 * Sync with global requirements * Fix and enable gating on H306 * Support building wheels (PEP-427) * Add touch-domain to CLI * Move "sync" commands out of Diagnostics and fix them 1.0.0 ----- * Keyring is not actually used * Remove outdated version value from setup.cfg * Added domain and record description editing * Sync requirements with stable/havana requirements * Ensure TTL is treated as an int in the CLI 0.0.5 ----- * Update dependency versions * Ensure Invalid JSON and errors during deletes are displayed correctly * Correct two Record examples in the binding docs * Add Python bindings docs * first drop of python-designateclient docs * Ensure beta versions are not downloaded from pypi * Add a --insecure arg to ignore invalid SSL certs * Ensure we only list sphinx as a dep once * Use Python 3.x compatible except construct 0.0.4 ----- * Allow auth using a pre-fetched token * Update to PBR 0.5.21+ * doc/requirements.txt should be a proxy * Remove comments from \*requirements.txt (workaround pbr bug) * Sync with openstack/requirements * Rename to designate in .gitreview * Rename Moniker -> Designate * Add Hacking checks * Migrate to PBR * Always allow overriding the endpoint * Switch to flake8 and testr * Added reporting functionality to Moniker client * Correct diagnostics URI * Include type when listing records 0.0.3 ----- * Support loading resources from 3rd party packages. E.g. client extensions * Allow for controllers to be loaded dynamically * Add a command to list the nameservers for a domain * List commands should show a reduced set of columns * Add keyring to pip-requires * Clean error message display :) * Cleanup optional and obsolete CLI params * Rename OS\_SERVICE\_ENDPOINT env var to OS\_DNS\_ENDPOINT * Sync JSONSchemas * Rename OS\_SERVICE\_TYPE env var to OS\_DNS\_SERVICE\_TYPE 0.0.2 ----- * Sync JSONSchemas * Have tox.ini actually run nosetests * Pin to jsonschema less than 1.0 * Add support for administrative access (via X-Moniker-Sudo-Tenant-ID header) * Ensure resources and tools/\* are included in sdist * Add diagnostics to Python API and CLI 0.0.1 ----- * Bump JSONSchema version to 0.8, tracking the server component * Attempt to support both cliff 1.2 and 1.3 * Re-add version.py * Sync with Oslo 30a50c8a * Sync Schemas * Add support for supplying custom service type * Fixup MANIFEST.in * Ensure we supply Oslo Version with the correct \`python\_package\` name * Sync with oslo a8973c52 * Ensure \`moniker --version\` reports the correct version number * Sync with oslo-incubator ad93e4e3 0.0.1.alpha1 ------------ * Actually use the \*correct\* version of requests * Depend on and update for requests>1.0 (Matching {keystone,cinder,nova}client) * Depend on cliff>=1.2.1 * Support record priority in the CLI * Support CLI updating of domains/records/servers * Ensure Update Records and Servers works correctly in the Python API * Sync JSON-Schemas * Provide a sane error message when --os-auth-url or --os-endpoint have not been supplied * Handle HTTP 400 errors somewhat more sanely * Ensure Warlock passes the approperiate error message along with it's exceptions * Include setup-requires in tox.ini * Positionals don't need require=Bool * Sync with Oslo e6c576d9 * Remove unused code * Ensure Servers CLI args are defined correctly * Sync Server JSONSchema * Fixup pyflakes issue * Add Records and Servers CLI commands * Add ID as a positional argument to GetCommand * More helpers in the base Controller * Fix actual != expected row count for items * Initial Python client bindings and CLI * Initial Domain/Record/Server model skeletons * Sync tox.ini with moniker repo * Add PyFlakes tox env * Add JSONSchemas * Sync with OS-Common 3d6c2368 + Use OS-C's Version Module * Rename package to python-monikerclient * Add pypi trove classifiers * Cache pip downloads on Jenkins * Fixup tox.ini for Stackforge+Gerrit * Exit with the correct return code * Initial Commit - CLI Skeleton ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/LICENSE0000664000175000017500000002363700000000000017242 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=1725031659.5573387 python-designateclient-6.1.0/PKG-INFO0000664000175000017500000000212300000000000017315 0ustar00zuulzuul00000000000000Metadata-Version: 1.2 Name: python-designateclient Version: 6.1.0 Summary: OpenStack DNS-as-a-Service - Client Home-page: https://docs.openstack.org/python-designateclient/latest Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: Apache License, Version 2.0 Description: OpenStack DNS-as-a-Service - Client Platform: UNKNOWN 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 :: Implementation :: CPython Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Topic :: Internet :: Name Service (DNS) Requires-Python: >=3.8 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/README.rst0000664000175000017500000000333300000000000017713 0ustar00zuulzuul00000000000000======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/python-designateclient.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on Python bindings to the Designate API ===================================== .. image:: https://img.shields.io/pypi/v/python-designateclient.svg :target: https://pypi.org/project/python-designateclient/ :alt: Latest Version This is a client library for Designate built on the Designate API. It provides a Python API (the ``designateclient`` module) and a command-line tool (``designate``). Development takes place via the usual OpenStack processes as outlined in the `developer guide `_. The master repository is in `Git `_. See release notes and more at ``_. * License: Apache License, Version 2.0 * `PyPi`_ - package installation * `Online Documentation`_ * `Bugs`_ - issue tracking * `Blueprints`_ - feature specifications * `Source`_ * `How to Contribute`_ * `Release Notes`_ .. _PyPi: https://pypi.org/project/python-designateclient .. _Online Documentation: https://docs.openstack.org/python-designateclient/latest/ .. _Bugs: https://bugs.launchpad.net/python-designateclient .. _Blueprints: https://blueprints.launchpad.net/python-designateclient .. _Source: https://git.openstack.org/cgit/openstack/python-designateclient .. _How to Contribute: https://docs.openstack.org/infra/manual/developers.html .. _Release Notes: https://docs.openstack.org/releasenotes/python-designateclient ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5253391 python-designateclient-6.1.0/designateclient/0000775000175000017500000000000000000000000021364 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/__init__.py0000664000175000017500000000130700000000000023476 0ustar00zuulzuul00000000000000# Copyright 2017 Huawei, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from designateclient import version __version__ = version.version_info.version_string() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/client.py0000664000175000017500000000730000000000000023214 0ustar00zuulzuul00000000000000# Copyright 2012 Managed I.T. # # Author: Kiall Mac Innes # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES 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 from stevedore import extension from urllib import parse from oslo_serialization import jsonutils from designateclient import exceptions class Controller(metaclass=abc.ABCMeta): def __init__(self, client): self.client = client def build_url(self, url, criterion=None, marker=None, limit=None): params = criterion or {} if marker is not None: params['marker'] = marker if limit is not None: params['limit'] = limit q = parse.urlencode(params) if params else '' return '{url}{params}'.format( url=url, params=f'?{q}' ) def _serialize(self, kwargs): headers = kwargs.get('headers') content_type = headers.get('Content-Type') if headers else None if 'data' in kwargs and content_type in {None, 'application/json'}: kwargs['data'] = jsonutils.dumps(kwargs['data']) def _post(self, url, response_key=None, **kwargs): self._serialize(kwargs) resp, body = self.client.session.post(url, **kwargs) if response_key is not None: return body[response_key] return body def _get(self, url, response_key=None): resp, body = self.client.session.get(url) if response_key is not None: return body[response_key] return body def _patch(self, url, response_key=None, **kwargs): self._serialize(kwargs) resp, body = self.client.session.patch(url, **kwargs) if response_key is not None: return body[response_key] return body def _put(self, url, response_key=None, **kwargs): self._serialize(kwargs) resp, body = self.client.session.put(url, **kwargs) if response_key is not None: return body[response_key] return body def _delete(self, url, response_key=None, **kwargs): resp, body = self.client.session.delete(url, **kwargs) if response_key is not None: return body[response_key] return body class CrudController(Controller, metaclass=abc.ABCMeta): @abc.abstractmethod def list(self, *args, **kw): """ List a resource """ @abc.abstractmethod def get(self, *args, **kw): """ Get a resource """ @abc.abstractmethod def create(self, *args, **kw): """ Create a resource """ @abc.abstractmethod def update(self, *args, **kw): """ Update a resource """ @abc.abstractmethod def delete(self, *args, **kw): """ Delete a resource """ def get_versions(): mgr = extension.ExtensionManager('designateclient.versions') return {ep.name: ep.plugin for ep in mgr.extensions} def Client(version, *args, **kwargs): # noqa versions = get_versions() if version not in versions: msg = 'Version {} is not supported, use one of ({})'.format( version, list(versions.keys()) ) raise exceptions.UnsupportedVersion(msg) return versions[version](*args, **kwargs) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/exceptions.py0000664000175000017500000000402300000000000024116 0ustar00zuulzuul00000000000000# Copyright 2012 Managed I.T. # # Author: Kiall Mac Innes # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. class Base(Exception): def __init__(self, message=None): if not message: message = self.__class__.__name__ super().__init__(message) class UnsupportedVersion(Base): pass class ResourceNotFound(Base): pass class NoUniqueMatch(Base): pass class RemoteError(Base): def __init__(self, message=None, code=None, type=None, errors=None, request_id=None, **ignore): err_message = self._get_error_message(message, type, errors) self.message = err_message self.code = code self.type = type self.errors = errors self.request_id = request_id super().__init__(err_message) def _get_error_message(self, _message, _type, _errors): # Try to get a useful error msg if 'message' has nothing if not _message: if _errors and 'errors' in _errors: err_msg = list() for err in _errors['errors']: if 'message' in err: err_msg.append(err['message']) _message = '. '.join(err_msg) elif _type: _message = str(_type) return _message class Unknown(RemoteError): pass class BadRequest(RemoteError): pass class Forbidden(RemoteError): pass class Conflict(RemoteError): pass class NotFound(RemoteError): pass class OverQuota(RemoteError): pass ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1725031659.529339 python-designateclient-6.1.0/designateclient/functionaltests/0000775000175000017500000000000000000000000024611 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/__init__.py0000664000175000017500000000016700000000000026726 0ustar00zuulzuul00000000000000import logging logging.basicConfig( filename='functional-tests.log', filemode='w', level=logging.DEBUG, ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/base.py0000664000175000017500000000301700000000000026076 0ustar00zuulzuul00000000000000""" Copyright 2015 Rackspace Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from tempest.lib.cli import base from tempest.lib.exceptions import CommandFailed from designateclient.functionaltests import client from designateclient.functionaltests import config class BaseDesignateTest(base.ClientTestBase): def _get_clients(self): config.read_config() return client.DesignateCLI.as_user('default') def ensure_tld_exists(self, tld): try: self.clients.as_user('admin').tld_create(tld) except CommandFailed: pass def _is_entity_in_list(self, entity, entity_list): """Determines if the given entity exists in the given list. Uses the id for comparison. Certain entities (e.g. zone import, export) cannot be made comparable in a list of CLI output results, because the fields in a list command can be different from those in a show command. """ return any([entity_record.id == entity.id for entity_record in entity_list]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/client.py0000664000175000017500000004065200000000000026450 0ustar00zuulzuul00000000000000""" Copyright 2015 Rackspace Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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 from tempest.lib.cli import base from designateclient.functionaltests.config import cfg from designateclient.functionaltests.models import FieldValueModel from designateclient.functionaltests.models import ListModel LOG = logging.getLogger(__name__) def build_option_string(options): """Format a string of option flags (--key 'value'). This will quote the values, in case spaces are included. Any values that are None are excluded entirely. Usage:: build_option_string({ "--email": "me@example.com", "--name": "example.com." "--ttl": None, }) Returns:: "--email 'me@example.com' --name 'example.com.' """ return " ".join(f"{flag} '{value}'" for flag, value in options.items() if value is not None) def build_flags_string(flags): """Format a string of value-less flags. Pass in a dictionary mapping flags to booleans. Those flags set to true are included in the returned string. Usage:: build_flags_string({ '--no-ttl': True, '--no-name': False, '--verbose': True, }) Returns:: '--no-ttl --verbose' """ flags = {flag: is_set for flag, is_set in flags.items() if is_set} return " ".join(flags.keys()) class ZoneCommands: """This is a mixin that provides zone commands to DesignateCLI""" def zone_list(self, *args, **kwargs): return self.parsed_cmd('zone list', ListModel, *args, **kwargs) def zone_show(self, id, *args, **kwargs): return self.parsed_cmd(f'zone show {id}', FieldValueModel, *args, **kwargs) def zone_delete(self, id, *args, **kwargs): return self.parsed_cmd(f'zone delete {id}', FieldValueModel, *args, **kwargs) def zone_create(self, name, email=None, ttl=None, description=None, type=None, masters=None, *args, **kwargs): options_str = build_option_string({ "--email": email, "--ttl": ttl, "--description": description, "--masters": masters, "--type": type, }) cmd = f'zone create {name} {options_str}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def zone_set(self, id, email=None, ttl=None, description=None, type=None, masters=None, *args, **kwargs): options_str = build_option_string({ "--email": email, "--ttl": ttl, "--description": description, "--masters": masters, "--type": type, }) cmd = f'zone set {id} {options_str}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) class ZoneTransferCommands: """A mixin for DesignateCLI to add zone transfer commands""" def zone_transfer_request_list(self, *args, **kwargs): cmd = 'zone transfer request list' return self.parsed_cmd(cmd, ListModel, *args, **kwargs) def zone_transfer_request_create(self, zone_id, target_project_id=None, description=None, *args, **kwargs): options_str = build_option_string({ "--target-project-id": target_project_id, "--description": description, }) cmd = f'zone transfer request create {zone_id} {options_str}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def zone_transfer_request_show(self, id, *args, **kwargs): cmd = f'zone transfer request show {id}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def zone_transfer_request_set(self, id, description=None, *args, **kwargs): options_str = build_option_string({"--description": description}) cmd = f'zone transfer request set {options_str} {id}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def zone_transfer_request_delete(self, id, *args, **kwargs): cmd = f'zone transfer request delete {id}' return self.parsed_cmd(cmd, *args, **kwargs) def zone_transfer_accept_request(self, id, key, *args, **kwargs): options_str = build_option_string({ "--transfer-id": id, "--key": key, }) cmd = f'zone transfer accept request {options_str}' return self.parsed_cmd(cmd, *args, **kwargs) def zone_transfer_accept_show(self, id, *args, **kwargs): cmd = f'zone transfer accept show {id}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def zone_transfer_accept_list(self, *args, **kwargs): cmd = 'zone transfer accept list' return self.parsed_cmd(cmd, ListModel, *args, **kwargs) class ZoneExportCommands: """A mixin for DesignateCLI to add zone export commands""" def zone_export_list(self, *args, **kwargs): cmd = 'zone export list' return self.parsed_cmd(cmd, ListModel, *args, **kwargs) def zone_export_create(self, zone_id, *args, **kwargs): cmd = f'zone export create {zone_id}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def zone_export_show(self, zone_export_id, *args, **kwargs): cmd = f'zone export show {zone_export_id}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def zone_export_delete(self, zone_export_id, *args, **kwargs): cmd = f'zone export delete {zone_export_id}' return self.parsed_cmd(cmd, *args, **kwargs) def zone_export_showfile(self, zone_export_id, *args, **kwargs): cmd = f'zone export showfile {zone_export_id}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) class ZoneImportCommands: """A mixin for DesignateCLI to add zone import commands""" def zone_import_list(self, *args, **kwargs): cmd = 'zone import list' return self.parsed_cmd(cmd, ListModel, *args, **kwargs) def zone_import_create(self, zone_file_path, *args, **kwargs): cmd = f'zone import create {zone_file_path}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def zone_import_show(self, zone_import_id, *args, **kwargs): cmd = f'zone import show {zone_import_id}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def zone_import_delete(self, zone_import_id, *args, **kwargs): cmd = f'zone import delete {zone_import_id}' return self.parsed_cmd(cmd, *args, **kwargs) class RecordsetCommands: def recordset_show(self, zone_id, id, *args, **kwargs): cmd = f'recordset show {zone_id} {id}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def recordset_list(self, zone_id, *args, **kwargs): cmd = f'recordset list {zone_id}' return self.parsed_cmd(cmd, ListModel, *args, **kwargs) def recordset_create(self, zone_id, name, record=None, type=None, description=None, ttl=None, *args, **kwargs): options_str = build_option_string({ # NOTE(tkajinam): --record argument should be repeated when # multiple records are used. This code needs update when multiple # records are used in functional tests. '--record': record, '--type': type, '--description': description, '--ttl': ttl, }) cmd = f'recordset create {zone_id} {name} {options_str}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def recordset_set(self, zone_id, id, record=None, type=None, description=None, ttl=None, no_description=False, no_ttl=False, *args, **kwargs): options_str = build_option_string({ # NOTE(tkajinam): --record argument should be repeated when # multiple records are used. This code needs update when multiple # records are used in functional tests. '--record': record, '--type': type, '--description': description, '--ttl': ttl, }) flags_str = build_flags_string({ '--no-description': no_description, '--no-ttl': no_ttl, }) cmd = f'recordset set {zone_id} {id} {flags_str} {options_str}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def recordset_delete(self, zone_id, id, *args, **kwargs): cmd = f'recordset delete {zone_id} {id}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) class TLDCommands: def tld_list(self, *args, **kwargs): return self.parsed_cmd('tld list', ListModel, *args, **kwargs) def tld_show(self, id, *args, **kwargs): return self.parsed_cmd(f'tld show {id}', FieldValueModel, *args, **kwargs) def tld_delete(self, id, *args, **kwargs): return self.parsed_cmd(f'tld delete {id}', *args, **kwargs) def tld_create(self, name, description=None, *args, **kwargs): options_str = build_option_string({ '--name': name, '--description': description, }) cmd = f'tld create {options_str}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def tld_set(self, id, name=None, description=None, no_description=False, *args, **kwargs): options_str = build_option_string({ '--name': name, '--description': description, }) flags_str = build_flags_string({'--no-description': no_description}) cmd = f'tld set {id} {options_str} {flags_str}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) class TSIGKeyCommands: def tsigkey_list(self, *args, **kwargs): return self.parsed_cmd('tsigkey list', ListModel, *args, **kwargs) def tsigkey_show(self, id, *args, **kwargs): return self.parsed_cmd(f'tsigkey show {id}', FieldValueModel, *args, **kwargs) def tsigkey_delete(self, id, *args, **kwargs): return self.parsed_cmd(f'tsigkey delete {id}', *args, **kwargs) def tsigkey_create(self, name, algorithm, secret, scope, resource_id, *args, **kwargs): options_str = build_option_string({ '--name': name, '--algorithm': algorithm, '--secret': secret, '--scope': scope, '--resource-id': resource_id, }) cmd = f'tsigkey create {options_str}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def tsigkey_set(self, id, name=None, algorithm=None, secret=None, scope=None, *args, **kwargs): options_str = build_option_string({ '--name': name, '--algorithm': algorithm, '--secret': secret, '--scope': scope, }) cmd = f'tsigkey set {id} {options_str}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) class BlacklistCommands: def zone_blacklist_list(self, *args, **kwargs): cmd = 'zone blacklist list' return self.parsed_cmd(cmd, ListModel, *args, **kwargs) def zone_blacklist_create(self, pattern, description=None, *args, **kwargs): options_str = build_option_string({ '--pattern': pattern, '--description': description, }) cmd = f'zone blacklist create {options_str}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def zone_blacklist_set(self, id, pattern=None, description=None, no_description=False, *args, **kwargs): options_str = build_option_string({ '--pattern': pattern, '--description': description, }) flags_str = build_flags_string({'--no-description': no_description}) cmd = f'zone blacklist set {id} {options_str} {flags_str}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def zone_blacklist_show(self, id, *args, **kwargs): cmd = f'zone blacklist show {id}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def zone_blacklist_delete(self, id, *args, **kwargs): cmd = f'zone blacklist delete {id}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) class SharedZoneCommands: def shared_zone_show(self, zone_id, shared_zone_id, *args, **kwargs): cmd = f'zone share show {zone_id} {shared_zone_id}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def shared_zone_list(self, zone_id, *args, **kwargs): cmd = f'zone share list {zone_id}' return self.parsed_cmd(cmd, ListModel, *args, **kwargs) def share_zone(self, zone_id, target_project_id, *args, **kwargs): cmd = f'zone share create {zone_id} {target_project_id}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) def unshare_zone(self, zone_id, shared_zone_id, *args, **kwargs): cmd = f'zone share delete {zone_id} {shared_zone_id}' return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) class DesignateCLI(base.CLIClient, ZoneCommands, ZoneTransferCommands, ZoneExportCommands, ZoneImportCommands, RecordsetCommands, TLDCommands, BlacklistCommands, SharedZoneCommands): # instantiate this once to minimize requests to keystone _CLIENTS = None def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # grab the project id. this is used for zone transfer requests resp = FieldValueModel(self.openstack('token issue')) self.project_id = resp.project_id @property def using_auth_override(self): return bool(cfg.CONF.identity.override_endpoint) @classmethod def get_clients(cls): if not cls._CLIENTS: cls._init_clients() return cls._CLIENTS @classmethod def _init_clients(cls): cls._CLIENTS = { 'default': DesignateCLI( cli_dir=cfg.CONF.designateclient.directory, username=cfg.CONF.identity.username, password=cfg.CONF.identity.password, tenant_name=cfg.CONF.identity.tenant_name, uri=cfg.CONF.identity.uri, ), 'alt': DesignateCLI( cli_dir=cfg.CONF.designateclient.directory, username=cfg.CONF.identity.alt_username, password=cfg.CONF.identity.alt_password, tenant_name=cfg.CONF.identity.alt_tenant_name, uri=cfg.CONF.identity.uri, ), 'admin': DesignateCLI( cli_dir=cfg.CONF.designateclient.directory, username=cfg.CONF.identity.admin_username, password=cfg.CONF.identity.admin_password, tenant_name=cfg.CONF.identity.admin_tenant_name, uri=cfg.CONF.identity.uri, ) } @classmethod def as_user(self, user): clients = self.get_clients() if user in clients: return clients[user] raise Exception(f"User '{user}' does not exist") def parsed_cmd(self, cmd, model=None, *args, **kwargs): if self.using_auth_override: # use --os-url and --os-token func = self._openstack_noauth else: # use --os-username --os-tenant-name --os-password --os-auth-url func = self.openstack out = func(cmd, *args, **kwargs) LOG.debug(out) if model is not None: return model(out) return out def _openstack_noauth(self, cmd, *args, **kwargs): exe = os.path.join(cfg.CONF.designateclient.directory, 'openstack') options = build_option_string({ '--os-url': cfg.CONF.identity.override_endpoint, '--os-token': cfg.CONF.identity.override_token, }) cmd = options + " " + cmd return base.execute(exe, cmd, *args, **kwargs) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/config.py0000664000175000017500000000405700000000000026436 0ustar00zuulzuul00000000000000""" Copyright 2015 Rackspace Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ import os from oslo_config import cfg cfg.CONF.register_group(cfg.OptGroup( name='identity', title="Configuration for Keystone auth" )) cfg.CONF.register_group(cfg.OptGroup( name='designateclient', title="Configuration for the Designate client" )) cfg.CONF.register_opts([ cfg.StrOpt('uri', help="The Keystone v2 endpoint"), cfg.StrOpt('uri_v3', help="The Keystone v3 endpoint"), cfg.StrOpt('auth_version', default='v2'), cfg.StrOpt('region', default='RegionOne'), cfg.StrOpt('username'), cfg.StrOpt('tenant_name'), cfg.StrOpt('password', secret=True), cfg.StrOpt('domain_name'), cfg.StrOpt('alt_username'), cfg.StrOpt('alt_tenant_name'), cfg.StrOpt('alt_password', secret=True), cfg.StrOpt('alt_domain_name'), cfg.StrOpt('admin_username'), cfg.StrOpt('admin_tenant_name'), cfg.StrOpt('admin_password', secret=True), cfg.StrOpt('admin_domain_name'), cfg.StrOpt("override_endpoint", help="use this url instead of the url in the service catalog"), cfg.StrOpt("override_token", help="with the override endpoint, pass this token to the api"), ], group='identity') cfg.CONF.register_opts([ cfg.StrOpt('directory', help='the directory containing the client executable'), ], group='designateclient') def find_config_file(): return os.environ.get( 'TEMPEST_CONFIG', '/opt/stack/tempest/etc/tempest.conf') def read_config(): cfg.CONF(args=[], default_config_files=[find_config_file()]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/datagen.py0000664000175000017500000000326200000000000026571 0ustar00zuulzuul00000000000000""" Copyright 2015 Rackspace Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ import random import string def random_digits(n=8): return ''.join([random.choice(string.digits) for _ in range(n)]) def random_tld(name='testtld'): return f'{name}{random_digits()}' def random_tsigkey_name(name='testtsig'): return f'{name}{random_digits()}' def random_tsigkey_secret(name='test-secret'): return f'{name}-{random_digits(254 - len(name))}' def random_zone_name(name='testdomain', tld='com'): return f'{name}{random_digits()}.{tld}.' def random_a_recordset_name(zone_name, recordset_name='testrecord'): return f'{recordset_name}{random_digits()}.{zone_name}' def random_blacklist(name='testblacklist'): return f'{name}{random_digits()}' def random_zone_file(name='testzoneimport'): return ( '$ORIGIN {0}{1}.com.\n' '$TTL 300\n' '{0}{1}.com. 300 IN SOA ns.{0}{1}.com. ' 'nsadmin.{0}{1}.com. 42 42 42 42 42\n' '{0}{1}.com. 300 IN NS ns.{0}{1}.com.\n' '{0}{1}.com. 300 IN MX 10 mail.{0}{1}.com.\n' 'ns.{0}{1}.com. 300 IN A 10.0.0.1\n' 'mail.{0}{1}.com. 300 IN A 10.0.0.2\n'.format( name, random_digits() ) ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/models.py0000664000175000017500000000636700000000000026462 0ustar00zuulzuul00000000000000""" Copyright 2015 Rackspace Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from tempest.lib.cli import output_parser class Model: def __str__(self): return str(self.__dict__) class FieldValueModel(Model): """This converts cli output from messy lists/dicts to neat attributes.""" def __init__(self, out): """This parses output with fields and values like: +----------------+------------------------------+ | Field | Value | +----------------+------------------------------+ | action | CREATE | | created_at | 2015-08-20T17:22:17.000000 | | description | None | +----------------+------------------------------+ These are then accessible as: model.action model.created_at model.description """ table = output_parser.table(out) # Because the output_parser handles Values with multiple lines # in additional Field/Value pairs with Field name '', the following # code is necessary to aggregate Values. # # The list of Field/Value pairs is in-order, so we can append Value # continuation to the previously seen Field, with a newline separator. value_lines = [] prev_field = None for field, value in table['values']: if field == '': value_lines.append(value) setattr(self, prev_field, '\n'.join(value_lines)) else: setattr(self, field, value) prev_field = field value_lines = [value] class ListEntryModel(Model): def __init__(self, fields, values): for k, v in zip(fields, values): setattr(self, k, v) class ListModel(Model, list): def __init__(self, out): """This parses an output table with any number of headers, and any number of entries: +--------------------------------------+----------+---------+ | id | name | type | +--------------------------------------+----------+---------+ | e658a875-1024-4f88-a347-e5b244ec5a10 | aaa.com. | PRIMARY | +--------------------------------------+----------+---------+ | 98d1fb5f-2954-448e-988e-6f1df0f24c52 | bbb.com. | PRIMARY | +--------------------------------------+----------+---------+ These are then accessible as: model[0].name == 'aaa.com.' model[1].name == 'bbb.com.' """ table = output_parser.table(out) for entry in table['values']: self.append(ListEntryModel(table['headers'], entry)) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1725031659.529339 python-designateclient-6.1.0/designateclient/functionaltests/v2/0000775000175000017500000000000000000000000025140 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/v2/__init__.py0000664000175000017500000000000000000000000027237 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/v2/fixtures.py0000664000175000017500000002032000000000000027360 0ustar00zuulzuul00000000000000""" Copyright 2015 Rackspace Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ import sys import tempfile import traceback import fixtures from tempest.lib.exceptions import CommandFailed from testtools.runtest import MultipleExceptions from designateclient.functionaltests.client import DesignateCLI class BaseFixture(fixtures.Fixture): def __init__(self, user='default', *args, **kwargs): """args/kwargs are forwarded to a create method on DesignateCLI""" super().__init__() self.args = args self.kwargs = kwargs self.client = DesignateCLI.as_user(user) def setUp(self): # Sometimes, exceptions are raised in _setUp methods on fixtures. # testtools pushes the exception into a MultipleExceptions object along # with an artificial SetupError, which produces bad error messages. # This just logs those stack traces to stderr for easier debugging. try: super().setUp() except MultipleExceptions as e: for i, exc_info in enumerate(e.args): print('--- printing MultipleExceptions traceback {} of {} ---' .format(i + 1, len(e.args)), file=sys.stderr) traceback.print_exception(*exc_info) raise class ZoneFixture(BaseFixture): """See DesignateCLI.zone_create for __init__ args""" def _setUp(self): super()._setUp() self.zone = self.client.zone_create(*self.args, **self.kwargs) self.addCleanup(self.cleanup_zone, self.client, self.zone.id) @classmethod def cleanup_zone(cls, client, zone_id): try: client.zone_delete(zone_id) except CommandFailed: pass class TransferRequestFixture(BaseFixture): """See DesignateCLI.zone_transfer_request_create for __init__ args""" def __init__(self, zone, user='default', target_user='alt', *args, **kwargs): super().__init__(user, *args, **kwargs) self.zone = zone self.target_client = DesignateCLI.as_user(target_user) # the client has a bug such that it requires --target-project-id. # when this bug is fixed, please remove this self.kwargs['target_project_id'] = self.target_client.project_id def _setUp(self): super()._setUp() self.transfer_request = self.client.zone_transfer_request_create( zone_id=self.zone.id, *self.args, **self.kwargs ) self.addCleanup(self.cleanup_transfer_request, self.client, self.transfer_request.id) self.addCleanup(ZoneFixture.cleanup_zone, self.client, self.zone.id) self.addCleanup(ZoneFixture.cleanup_zone, self.target_client, self.zone.id) @classmethod def cleanup_transfer_request(cls, client, transfer_request_id): try: client.zone_transfer_request_delete(transfer_request_id) except CommandFailed: pass class ExportFixture(BaseFixture): """See DesignateCLI.zone_export_create for __init__ args""" def __init__(self, zone, user='default', *args, **kwargs): super().__init__(user, *args, **kwargs) self.zone = zone def _setUp(self): super()._setUp() self.zone_export = self.client.zone_export_create( zone_id=self.zone.id, *self.args, **self.kwargs ) self.addCleanup(self.cleanup_zone_export, self.client, self.zone_export.id) self.addCleanup(ZoneFixture.cleanup_zone, self.client, self.zone.id) @classmethod def cleanup_zone_export(cls, client, zone_export_id): try: client.zone_export_delete(zone_export_id) except CommandFailed: pass class ImportFixture(BaseFixture): """See DesignateCLI.zone_import_create for __init__ args""" def __init__(self, zone_file_contents, user='default', *args, **kwargs): super().__init__(user, *args, **kwargs) self.zone_file_contents = zone_file_contents def _setUp(self): super()._setUp() with tempfile.NamedTemporaryFile() as f: f.write(self.zone_file_contents) f.flush() self.zone_import = self.client.zone_import_create( zone_file_path=f.name, *self.args, **self.kwargs ) self.addCleanup(self.cleanup_zone_import, self.client, self.zone_import.id) self.addCleanup(ZoneFixture.cleanup_zone, self.client, self.zone_import.zone_id) @classmethod def cleanup_zone_import(cls, client, zone_import_id): try: client.zone_import_delete(zone_import_id) except CommandFailed: pass class RecordsetFixture(BaseFixture): """See DesignateCLI.recordset_create for __init__ args""" def _setUp(self): super()._setUp() self.recordset = self.client.recordset_create( *self.args, **self.kwargs) self.addCleanup(self.cleanup_recordset, self.client, self.recordset.zone_id, self.recordset.id) @classmethod def cleanup_recordset(cls, client, zone_id, recordset_id): try: client.recordset_delete(zone_id, recordset_id) except CommandFailed: pass class TLDFixture(BaseFixture): """See DesignateCLI.tld_create for __init__ args""" def __init__(self, user='admin', *args, **kwargs): super().__init__(user=user, *args, **kwargs) def _setUp(self): super()._setUp() self.tld = self.client.tld_create(*self.args, **self.kwargs) self.addCleanup(self.cleanup_tld, self.client, self.tld.id) @classmethod def cleanup_tld(cls, client, tld_id): try: client.tld_delete(tld_id) except CommandFailed: pass class TSIGKeyFixture(BaseFixture): """See DesignateCLI.tsigkey_create for __init__ args""" def __init__(self, user='admin', *args, **kwargs): super().__init__(user=user, *args, **kwargs) def _setUp(self): super()._setUp() self.tsigkey = self.client.tsigkey_create(*self.args, **self.kwargs) self.addCleanup(self.cleanup_tsigkey(self.client, self.tsigkey.id)) @classmethod def cleanup_tsigkey(cls, client, tsigkey_id): try: client.tsigkey_delete(tsigkey_id) except CommandFailed: pass class BlacklistFixture(BaseFixture): """See DesignateCLI.zone_blacklist_create for __init__ args""" def __init__(self, user='admin', *args, **kwargs): super().__init__(user=user, *args, **kwargs) def _setUp(self): super()._setUp() self.blacklist = self.client.zone_blacklist_create(*self.args, **self.kwargs) self.addCleanup(self.cleanup_blacklist, self.client, self.blacklist.id) @classmethod def cleanup_blacklist(cls, client, blacklist_id): try: client.zone_blacklist_delete(blacklist_id) except CommandFailed: pass class SharedZoneFixture(BaseFixture): """See DesignateCLI.recordset_create for __init__ args""" def __init__(self, zone, *args, **kwargs): super().__init__(*args, **kwargs) self.zone = zone def _setUp(self): super()._setUp() self.zone_share = self.client.zone_share(zone_id=self.zone.id, *self.args, **self.kwargs) self.addCleanup(self.cleanup_shared_zone, self.client, self.zone.id, self.zone_share.id) @classmethod def cleanup_shared_zone(cls, client, zone_id, shared_zone_id): try: client.unshare_zone(zone_id, shared_zone_id) except CommandFailed: pass ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/v2/test_blacklist.py0000664000175000017500000000763500000000000030534 0ustar00zuulzuul00000000000000""" Copyright 2015 Rackspace Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from tempest.lib.exceptions import CommandFailed from designateclient.functionaltests.base import BaseDesignateTest from designateclient.functionaltests.datagen import random_blacklist from designateclient.functionaltests.v2.fixtures import BlacklistFixture class TestBlacklist(BaseDesignateTest): def setUp(self): super().setUp() pattern = random_blacklist() self.blacklist = self.useFixture(BlacklistFixture( pattern=pattern, description='A random blacklist', )).blacklist self.assertEqual(self.blacklist.pattern, pattern) self.assertEqual(self.blacklist.description, 'A random blacklist') def test_zone_blacklist_list(self): blacklists = self.clients.as_user('admin').zone_blacklist_list() self.assertGreater(len(blacklists), 0) def test_zone_blacklist_create_and_show(self): client = self.clients.as_user('admin') blacklist = client.zone_blacklist_show(self.blacklist.id) self.assertEqual(self.blacklist.created_at, blacklist.created_at) self.assertEqual(self.blacklist.description, blacklist.description) self.assertEqual(self.blacklist.id, blacklist.id) self.assertEqual(self.blacklist.pattern, blacklist.pattern) self.assertEqual(self.blacklist.updated_at, blacklist.updated_at) def test_zone_blacklist_delete(self): client = self.clients.as_user('admin') client.zone_blacklist_delete(self.blacklist.id) self.assertRaises(CommandFailed, client.zone_blacklist_show, self.blacklist.id) def test_zone_blacklist_set(self): client = self.clients.as_user('admin') updated_pattern = random_blacklist('updatedblacklist') blacklist = client.zone_blacklist_set( id=self.blacklist.id, pattern=updated_pattern, description='An updated blacklist', ) self.assertEqual(blacklist.created_at, self.blacklist.created_at) self.assertEqual(blacklist.description, 'An updated blacklist') self.assertEqual(blacklist.id, self.blacklist.id) self.assertEqual(blacklist.pattern, updated_pattern) self.assertNotEqual(blacklist.updated_at, self.blacklist.updated_at) def test_zone_blacklist_set_no_description(self): client = self.clients.as_user('admin') blacklist = client.zone_blacklist_set( id=self.blacklist.id, no_description=True, ) self.assertEqual(blacklist.description, 'None') def test_cannot_set_description_with_no_description_flag(self): client = self.clients.as_user('admin') self.assertRaises(CommandFailed, client.zone_blacklist_set, self.blacklist.id, pattern=random_blacklist(), description='new description', no_description=True) class TestBlacklistNegative(BaseDesignateTest): def test_invalid_blacklist_command(self): client = self.clients.as_user('admin') cmd = 'zone blacklist notacommand' self.assertRaises(CommandFailed, client.openstack, cmd) def test_blacklist_create_invalid_flag(self): client = self.clients.as_user('admin') cmd = 'zone blacklist create --pattern helloworld --notaflag invalid' self.assertRaises(CommandFailed, client.openstack, cmd) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/v2/test_recordsets.py0000664000175000017500000001034100000000000030725 0ustar00zuulzuul00000000000000""" Copyright 2015 Rackspace Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from tempest.lib.exceptions import CommandFailed from designateclient.functionaltests.base import BaseDesignateTest from designateclient.functionaltests.datagen import random_a_recordset_name from designateclient.functionaltests.datagen import random_zone_name from designateclient.functionaltests.v2.fixtures import RecordsetFixture from designateclient.functionaltests.v2.fixtures import ZoneFixture class TestRecordset(BaseDesignateTest): def setUp(self): super().setUp() self.ensure_tld_exists('com') self.zone = self.useFixture(ZoneFixture( name=random_zone_name(), email='test@example.com', )).zone name = random_a_recordset_name(self.zone.name) self.recordset = self.useFixture(RecordsetFixture( zone_id=self.zone.id, name=name, record='1.2.3.4', description='An a recordset', type='A', ttl=1234, )).recordset self.assertEqual(self.recordset.name, name) self.assertEqual(self.recordset.records, '1.2.3.4') self.assertEqual(self.recordset.description, 'An a recordset') self.assertEqual(self.recordset.type, 'A') self.assertEqual(self.recordset.ttl, '1234') def test_recordset_list(self): rsets = self.clients.recordset_list(self.zone.id) self.assertGreater(len(rsets), 0) def test_recordset_create_and_show(self): rset = self.clients.recordset_show(self.zone.id, self.recordset.id) self.assertTrue(hasattr(self.recordset, 'action')) self.assertTrue(hasattr(rset, 'action')) self.assertEqual(self.recordset.created_at, rset.created_at) self.assertEqual(self.recordset.description, rset.description) self.assertEqual(self.recordset.id, rset.id) self.assertEqual(self.recordset.name, rset.name) self.assertEqual(self.recordset.records, rset.records) self.assertEqual(self.recordset.status, rset.status) self.assertEqual(self.recordset.ttl, rset.ttl) self.assertEqual(self.recordset.type, rset.type) self.assertEqual(self.recordset.updated_at, rset.updated_at) self.assertEqual(self.recordset.version, rset.version) self.assertEqual(self.recordset.zone_id, self.zone.id) def test_recordset_delete(self): rset = self.clients.recordset_delete(self.zone.id, self.recordset.id) self.assertEqual(rset.action, 'DELETE') self.assertEqual(rset.status, 'PENDING') def test_recordset_set(self): rset = self.clients.recordset_set( self.zone.id, self.recordset.id, record='2.3.4.5', ttl=2345, description='Updated description', ) self.assertEqual(rset.records, '2.3.4.5') self.assertEqual(rset.ttl, '2345') self.assertEqual(rset.description, 'Updated description') def test_recordset_set_clear_ttl_and_description(self): rset = self.clients.recordset_set( self.zone.id, self.recordset.id, no_description=True, no_ttl=True, ) self.assertEqual(rset.description, 'None') self.assertEqual(rset.ttl, 'None') class TestRecordsetNegative(BaseDesignateTest): def test_invalid_option_on_recordset_create(self): cmd = ('recordset create de47d30b-41c5-4e38-b2c5-e0b908e19ec7 ' 'aaa.desig.com. --type A --record 1.2.3.4 ' '--invalid "not valid"') self.assertRaises(CommandFailed, self.clients.openstack, cmd) def test_invalid_recordset_command(self): cmd = 'recordset hopefullynotvalid' self.assertRaises(CommandFailed, self.clients.openstack, cmd) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/v2/test_shared_zone.py0000664000175000017500000000560300000000000031056 0ustar00zuulzuul00000000000000""" Copyright 2020 Cloudification GmbH. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from designateclient.functionaltests.base import BaseDesignateTest from designateclient.functionaltests.client import DesignateCLI from designateclient.functionaltests.datagen import random_zone_name from designateclient.functionaltests.v2.fixtures import SharedZoneFixture from designateclient.functionaltests.v2.fixtures import ZoneFixture class TestSharedZone(BaseDesignateTest): def setUp(self): super().setUp() self.ensure_tld_exists('com') fixture = self.useFixture(ZoneFixture( name=random_zone_name(), email='test@example.com', )) self.zone = fixture.zone self.target_client = DesignateCLI.as_user('alt') def test_list_shared_zones(self): shared_zone = self.useFixture(SharedZoneFixture( zone_id=self.zone.id, target_tenant_id=self.target_client.project_id )).zone_share shared_zones = self.clients.shared_zone_list(self.zone.id) self.assertGreater(len(shared_zones), 0) self.assertTrue(self._is_entity_in_list(shared_zone, shared_zones)) def test_share_and_show_shared_zone(self): shared_zone = self.useFixture(SharedZoneFixture( zone_id=self.zone.id, target_tenant_id=self.target_client.project_id )).zone_share fetched_shared_zone = self.clients.shared_zone_show(self.zone.id, shared_zone.id) self.assertEqual( shared_zone.created_at, fetched_shared_zone.created_at) self.assertEqual(shared_zone.id, fetched_shared_zone.id) self.assertEqual( shared_zone.project_id, fetched_shared_zone.project_id) self.assertEqual(shared_zone.zone_id, fetched_shared_zone.zone_id) def test_unshare_zone(self): shared_zone = self.useFixture(SharedZoneFixture( zone_id=self.zone.id, target_tenant_id=self.target_client.project_id )).zone_share shared_zones = self.clients.shared_zone_list(self.zone.id) self.assertTrue(self._is_entity_in_list(shared_zone, shared_zones)) self.clients.unshare_zone(self.zone.id, shared_zone.id) shared_zones = self.clients.shared_zone_list(self.zone.id) self.assertFalse(self._is_entity_in_list(shared_zone, shared_zones)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/v2/test_tlds.py0000664000175000017500000000606000000000000027521 0ustar00zuulzuul00000000000000""" Copyright 2015 Rackspace Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from tempest.lib.exceptions import CommandFailed from designateclient.functionaltests.base import BaseDesignateTest from designateclient.functionaltests.datagen import random_tld from designateclient.functionaltests.v2.fixtures import TLDFixture class TestTld(BaseDesignateTest): def setUp(self): super().setUp() tld_name = random_tld() self.tld = self.useFixture(TLDFixture( name=tld_name, description='A random tld', )).tld self.assertEqual(self.tld.name, tld_name) self.assertEqual(self.tld.description, 'A random tld') def test_tld_list(self): tlds = self.clients.as_user('admin').tld_list() self.assertGreater(len(tlds), 0) def test_tld_create_and_show(self): tld = self.clients.as_user('admin').tld_show(self.tld.id) self.assertEqual(tld.name, self.tld.name) self.assertEqual(tld.created_at, self.tld.created_at) self.assertEqual(tld.id, self.tld.id) self.assertEqual(tld.name, self.tld.name) self.assertEqual(tld.updated_at, self.tld.updated_at) def test_tld_delete(self): client = self.clients.as_user('admin') client.tld_delete(self.tld.id) self.assertRaises(CommandFailed, client.tld_show, self.tld.id) def test_tld_set(self): client = self.clients.as_user('admin') updated_name = random_tld('updated') tld = client.tld_set(self.tld.id, name=updated_name, description='An updated tld') self.assertEqual(tld.description, 'An updated tld') self.assertEqual(tld.name, updated_name) def test_tld_set_no_description(self): client = self.clients.as_user('admin') tld = client.tld_set(self.tld.id, no_description=True) self.assertEqual(tld.description, 'None') def test_no_set_tld_with_description_and_no_description(self): client = self.clients.as_user('admin') self.assertRaises(CommandFailed, client.tld_set, self.tld.id, description='An updated tld', no_description=True) class TestTldNegative(BaseDesignateTest): def test_tld_invalid_commmand(self): client = self.clients.as_user('admin') self.assertRaises(CommandFailed, client.openstack, 'tld notacommand') def test_tld_create_invalid_flag(self): client = self.clients.as_user('admin') self.assertRaises(CommandFailed, client.openstack, 'tld create --notanoption "junk"') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/v2/test_tsigkeys.py0000664000175000017500000000733700000000000030425 0ustar00zuulzuul00000000000000""" Copyright 2017 SAP SE Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from tempest.lib.exceptions import CommandFailed from designateclient.functionaltests.base import BaseDesignateTest from designateclient.functionaltests.datagen import random_tsigkey_name from designateclient.functionaltests.datagen import random_tsigkey_secret from designateclient.functionaltests.datagen import random_zone_name from designateclient.functionaltests.v2.fixtures import TSIGKeyFixture from designateclient.functionaltests.v2.fixtures import ZoneFixture class TestTSIGKey(BaseDesignateTest): def setUp(self): super().setUp() self.ensure_tsigkey_exists('com') self.zone = self.useFixture(ZoneFixture( name=random_zone_name(), email='test@example.com', )).zone tsig_name = random_tsigkey_name() tsig_algorithm = "hmac-sha256" tsig_secret = random_tsigkey_secret() tsig_scope = 'ZONE' self.tsig = self.useFixture(TSIGKeyFixture( name=tsig_name, algorithm=tsig_algorithm, secret=tsig_secret, scope=tsig_scope, resource_id=self.zone.id )).tsig self.assertEqual(self.tsig.name, tsig_name) self.assertEqual(self.tsig.algorithm, tsig_algorithm) self.assertEqual(self.tsig.secret, tsig_secret) self.assertEqual(self.tsig.scope, tsig_scope) self.assertEqual(self.tsig.resource_id, self.zone.id) def test_tsigkey_list(self): tsigkeys = self.clients.as_user('admin').tsigkey_list() self.assertGreater(len(tsigkeys), 0) def test_tsigkey_create_and_show(self): tsigkey = self.clients.as_user('admin').tsigkey_show(self.tsigkey.id) self.assertEqual(tsigkey.name, self.tsigkey.name) self.assertEqual(tsigkey.created_at, self.tsigkey.created_at) self.assertEqual(tsigkey.id, self.tsigkey.id) self.assertEqual(self.tsig.algorithm, self.tsig_algorithm) self.assertEqual(self.tsig.secret, self.tsig_secret) self.assertEqual(self.tsig.scope, self.tsig_scope) self.assertEqual(self.tsig.resource_id, self.zone.id) self.assertEqual(tsigkey.updated_at, self.tsigkey.updated_at) def test_tsigkey_delete(self): client = self.clients.as_user('admin') client.tsigkey_delete(self.tsigkey.id) self.assertRaises(CommandFailed, client.tsigkey_show, self.tsigkey.id) def test_tsigkey_set(self): client = self.clients.as_user('admin') updated_name = random_tsigkey_name('updated') tsigkey = client.tsigkey_set(self.tsigkey.id, name=updated_name, secret='An updated tsigsecret') self.assertEqual(tsigkey.secret, 'An updated tsigsecret') self.assertEqual(tsigkey.name, updated_name) class TestTSIGKeyNegative(BaseDesignateTest): def test_tsigkey_invalid_commmand(self): client = self.clients.as_user('admin') self.assertRaises(CommandFailed, client.openstack, 'tsigkey notacommand') def test_tsigkey_create_invalid_flag(self): client = self.clients.as_user('admin') self.assertRaises(CommandFailed, client.openstack, 'tsigkey create --notanoption "junk"') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/v2/test_zone.py0000664000175000017500000001152200000000000027525 0ustar00zuulzuul00000000000000""" Copyright 2015 Rackspace Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from tempest.lib.exceptions import CommandFailed from designateclient.functionaltests.base import BaseDesignateTest from designateclient.functionaltests.datagen import random_zone_name from designateclient.functionaltests.v2.fixtures import ZoneFixture class TestZone(BaseDesignateTest): def setUp(self): super().setUp() self.ensure_tld_exists('com') self.fixture = self.useFixture(ZoneFixture( name=random_zone_name(), email='test@example.com', )) self.zone = self.fixture.zone def test_zone_list(self): zones = self.clients.zone_list() self.assertGreater(len(zones), 0) def test_zone_create_and_show(self): zone = self.clients.zone_show(self.zone.id) self.assertTrue(hasattr(zone, 'action')) self.assertEqual(self.zone.created_at, zone.created_at) self.assertEqual(self.zone.description, zone.description) self.assertEqual(self.zone.email, zone.email) self.assertEqual(self.zone.id, zone.id) self.assertEqual(self.zone.masters, zone.masters) self.assertEqual(self.zone.name, zone.name) self.assertEqual(self.zone.pool_id, zone.pool_id) self.assertEqual(self.zone.project_id, zone.project_id) self.assertEqual(self.zone.serial, zone.serial) self.assertTrue(hasattr(zone, 'status')) self.assertEqual(self.zone.transferred_at, zone.transferred_at) self.assertEqual(self.zone.ttl, zone.ttl) self.assertEqual(self.zone.type, zone.type) self.assertEqual(self.zone.updated_at, zone.updated_at) self.assertEqual(self.zone.version, zone.version) def test_zone_delete(self): zone = self.clients.zone_delete(self.zone.id) self.assertEqual(zone.action, 'DELETE') self.assertEqual(zone.status, 'PENDING') def test_zone_set(self): ttl = int(self.zone.ttl) + 123 email = f'updated{self.zone.email}' description = 'new description' zone = self.clients.zone_set(self.zone.id, ttl=ttl, email=email, description=description) self.assertEqual(ttl, int(zone.ttl)) self.assertEqual(email, zone.email) self.assertEqual(description, zone.description) def test_invalid_option_on_zone_create(self): cmd = 'zone create {} --invalid "not a valid option"'.format( random_zone_name() ) self.assertRaises(CommandFailed, self.clients.openstack, cmd) def test_invalid_zone_command(self): cmd = 'zone hopefullynotacommand' self.assertRaises(CommandFailed, self.clients.openstack, cmd) class TestsPassingZoneFlags(BaseDesignateTest): def setUp(self): super().setUp() self.ensure_tld_exists('com') def test_zone_create_primary_with_all_args(self): zone_name = random_zone_name() fixture = self.useFixture(ZoneFixture( name=zone_name, email='primary@example.com', description='A primary zone', ttl=2345, type='PRIMARY', )) zone = fixture.zone self.assertEqual(zone_name, zone.name) self.assertEqual('primary@example.com', zone.email) self.assertEqual('A primary zone', zone.description) self.assertEqual('2345', zone.ttl) self.assertEqual('PRIMARY', zone.type) def test_zone_create_secondary_with_all_args(self): zone_name = random_zone_name() fixture = self.useFixture(ZoneFixture( name=zone_name, description='A secondary zone', type='SECONDARY', masters='127.0.0.1', )) zone = fixture.zone self.assertEqual(zone_name, zone.name) self.assertEqual('A secondary zone', zone.description) self.assertEqual('SECONDARY', zone.type) self.assertEqual('127.0.0.1', zone.masters) def test_zone_set_secondary_masters(self): fixture = self.useFixture(ZoneFixture( name=random_zone_name(), description='A secondary zone', type='SECONDARY', masters='127.0.0.1', )) zone = fixture.zone self.assertEqual('127.0.0.1', zone.masters) zone = self.clients.zone_set(zone.id, masters='127.0.0.2') self.assertEqual('127.0.0.2', zone.masters) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/v2/test_zone_export.py0000664000175000017500000000571000000000000031130 0ustar00zuulzuul00000000000000""" Copyright 2016 Rackspace Author: Rahman Syed Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from designateclient.functionaltests.base import BaseDesignateTest from designateclient.functionaltests.datagen import random_zone_name from designateclient.functionaltests.v2.fixtures import ExportFixture from designateclient.functionaltests.v2.fixtures import ZoneFixture class TestZoneExport(BaseDesignateTest): def setUp(self): super().setUp() self.ensure_tld_exists('com') fixture = self.useFixture(ZoneFixture( name=random_zone_name(), email='test@example.com', )) self.zone = fixture.zone def test_list_zone_exports(self): zone_export = self.useFixture(ExportFixture( zone=self.zone )).zone_export zone_exports = self.clients.zone_export_list() self.assertGreater(len(zone_exports), 0) self.assertTrue(self._is_entity_in_list(zone_export, zone_exports)) def test_create_and_show_zone_export(self): zone_export = self.useFixture(ExportFixture( zone=self.zone )).zone_export fetched_export = self.clients.zone_export_show(zone_export.id) self.assertEqual(zone_export.created_at, fetched_export.created_at) self.assertEqual(zone_export.id, fetched_export.id) self.assertEqual(zone_export.message, fetched_export.message) self.assertEqual(zone_export.project_id, fetched_export.project_id) self.assertEqual(zone_export.zone_id, fetched_export.zone_id) def test_delete_zone_export(self): zone_export = self.useFixture(ExportFixture( zone=self.zone )).zone_export zone_exports = self.clients.zone_export_list() self.assertTrue(self._is_entity_in_list(zone_export, zone_exports)) self.clients.zone_export_delete(zone_export.id) zone_exports = self.clients.zone_export_list() self.assertFalse(self._is_entity_in_list(zone_export, zone_exports)) def test_show_export_file(self): zone_export = self.useFixture(ExportFixture( zone=self.zone )).zone_export fetched_export = self.clients.zone_export_showfile(zone_export.id) self.assertIn('$ORIGIN', fetched_export.data) self.assertIn('$TTL', fetched_export.data) self.assertIn('SOA', fetched_export.data) self.assertIn('NS', fetched_export.data) self.assertIn(self.zone.name, fetched_export.data) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/v2/test_zone_import.py0000664000175000017500000000470300000000000031122 0ustar00zuulzuul00000000000000""" Copyright 2016 Rackspace Author: Rahman Syed Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from designateclient.functionaltests.base import BaseDesignateTest from designateclient.functionaltests.datagen import random_zone_file from designateclient.functionaltests.v2.fixtures import ImportFixture class TestZoneImport(BaseDesignateTest): def setUp(self): super().setUp() self.ensure_tld_exists('com') self.zone_file_contents = random_zone_file() def test_list_zone_imports(self): zone_import = self.useFixture(ImportFixture( zone_file_contents=self.zone_file_contents )).zone_import zone_imports = self.clients.zone_import_list() self.assertGreater(len(zone_imports), 0) self.assertTrue(self._is_entity_in_list(zone_import, zone_imports)) def test_create_and_show_zone_import(self): zone_import = self.useFixture(ImportFixture( zone_file_contents=self.zone_file_contents )).zone_import fetched_import = self.clients.zone_import_show(zone_import.id) self.assertEqual(zone_import.created_at, fetched_import.created_at) self.assertEqual(zone_import.id, fetched_import.id) self.assertEqual(zone_import.project_id, fetched_import.project_id) # check both statuses to avoid a race condition, causing test failure. # we don't know when the import completes. self.assertIn(fetched_import.status, ['PENDING', 'COMPLETE']) def test_delete_zone_import(self): zone_import = self.useFixture(ImportFixture( zone_file_contents=self.zone_file_contents )).zone_import zone_imports = self.clients.zone_import_list() self.assertTrue(self._is_entity_in_list(zone_import, zone_imports)) self.clients.zone_import_delete(zone_import.id) zone_imports = self.clients.zone_import_list() self.assertFalse(self._is_entity_in_list(zone_import, zone_imports)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/functionaltests/v2/test_zone_transfer.py0000664000175000017500000001130300000000000031426 0ustar00zuulzuul00000000000000""" Copyright 2015 Rackspace Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ import unittest from tempest.lib.exceptions import CommandFailed from designateclient.functionaltests.base import BaseDesignateTest from designateclient.functionaltests.client import DesignateCLI from designateclient.functionaltests.datagen import random_zone_name from designateclient.functionaltests.v2.fixtures import TransferRequestFixture from designateclient.functionaltests.v2.fixtures import ZoneFixture class TestZoneTransferRequest(BaseDesignateTest): def setUp(self): super().setUp() self.ensure_tld_exists('com') fixture = self.useFixture(ZoneFixture( name=random_zone_name(), email='test@example.com', )) self.zone = fixture.zone def test_list_zone_transfer_request(self): self.useFixture(TransferRequestFixture(self.zone)) xfrs = self.clients.zone_transfer_request_list() self.assertGreater(len(xfrs), 0) def test_create_and_show_zone_transfer_request(self): transfer_request = self.useFixture(TransferRequestFixture( zone=self.zone, user='default', target_user='alt', )).transfer_request fetched_xfr = self.clients.zone_transfer_request_show( transfer_request.id) self.assertEqual(fetched_xfr.created_at, transfer_request.created_at) self.assertEqual(fetched_xfr.description, transfer_request.description) self.assertEqual(fetched_xfr.id, transfer_request.id) self.assertEqual(fetched_xfr.key, transfer_request.key) self.assertEqual(fetched_xfr.links, transfer_request.links) self.assertEqual(fetched_xfr.target_project_id, transfer_request.target_project_id) self.assertEqual(fetched_xfr.updated_at, transfer_request.updated_at) self.assertEqual(fetched_xfr.status, transfer_request.status) self.assertEqual(fetched_xfr.zone_id, self.zone.id) self.assertEqual(fetched_xfr.zone_name, self.zone.name) def test_delete_zone_transfer_request(self): transfer_request = self.useFixture(TransferRequestFixture( zone=self.zone, user='default', target_user='alt', )).transfer_request self.clients.zone_transfer_request_delete(transfer_request.id) self.assertRaises(CommandFailed, self.clients.zone_transfer_request_show, transfer_request.id) @unittest.skip("Fails because `zone transfer request set` returns nothing") def test_set_zone_transfer_request(self): transfer_request = self.useFixture(TransferRequestFixture( zone=self.zone, description="old description", )).transfer_request self.assertEqual(transfer_request.description, "old description") updated_xfr = self.clients.zone_transfer_request_set( transfer_request.id, description="updated description") self.assertEqual(updated_xfr.description, "updated description") class TestZoneTransferAccept(BaseDesignateTest): def setUp(self): super().setUp() self.ensure_tld_exists('com') fixture = self.useFixture(ZoneFixture( name=random_zone_name(), email='test@example.com', )) self.zone = fixture.zone self.target_client = DesignateCLI.as_user('alt') fixture = self.useFixture(TransferRequestFixture( zone=self.zone, user='default', target_user='alt', target_project_id=self.target_client.project_id, )) self.transfer_request = fixture.transfer_request def test_zone_transfer_accept_list(self): self.useFixture(TransferRequestFixture(self.zone)) list_transfer_accepts = self.clients.zone_transfer_accept_list() self.assertGreater(len(list_transfer_accepts), 0) def test_zone_transfer_accept_request(self): self.target_client.zone_transfer_accept_request( id=self.transfer_request.id, key=self.transfer_request.key, ) self.target_client.zone_show(self.zone.id) self.assertRaises(CommandFailed, self.clients.zone_show, self.zone.id) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1725031659.529339 python-designateclient-6.1.0/designateclient/hacking/0000775000175000017500000000000000000000000022770 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/hacking/__init__.py0000664000175000017500000000000000000000000025067 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/hacking/checks.py0000664000175000017500000001516300000000000024610 0ustar00zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # Copyright (c) 2012, Cloudscaling # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import re from hacking import core # D701: Default parameter value is a mutable type # D702: Log messages require translation # D703: Found use of _() without explicit import of _! # D704: Found import of %s. This oslo library has been graduated! # D705: timeutils.utcnow() must be used instead of datetime.%s() # D706: Don't translate debug level logs # D707: basestring is not Python3-compatible, use str instead. # D708: Do not use xrange. Use range for large loops. # D709: LOG.audit is deprecated, please use LOG.info! # D710: LOG.warn() is not allowed. Use LOG.warning() # D711: Don't use backslashes for line continuation. UNDERSCORE_IMPORT_FILES = [] mutable_default_argument_check = re.compile( r"^\s*def .+\((.+=\{\}|.+=\[\])") string_translation = re.compile(r"[^_]*_\(\s*('|\")") translated_log = re.compile( r"(.)*LOG\.(audit|error|info|warn|warning|critical|exception)" r"\(\s*_\(\s*('|\")") underscore_import_check = re.compile(r"(.)*import _(.)*") # We need this for cases where they have created their own _ function. custom_underscore_check = re.compile(r"(.)*_\s*=\s*(.)*") graduated_oslo_libraries_import_re = re.compile( r"^\s*(?:import|from) designate\.openstack\.common\.?.*?" r"(gettextutils|rpc)" r".*?") no_line_continuation_backslash_re = re.compile(r'.*(\\)\n') @core.flake8ext def mutable_default_arguments(logical_line, filename, noqa): if noqa: return if mutable_default_argument_check.match(logical_line): yield (0, "D701: Default parameter value is a mutable type") @core.flake8ext def no_translate_debug_logs(logical_line, filename): """Check for 'LOG.debug(_(' As per our translation policy, https://wiki.openstack.org/wiki/LoggingStandards#Log_Translation we shouldn't translate debug level logs. * This check assumes that 'LOG' is a logger. * Use filename so we can start enforcing this in specific folders instead of needing to do so all at once. N319 """ if logical_line.startswith("LOG.debug(_("): yield (0, "D706: Don't translate debug level logs") @core.flake8ext def check_explicit_underscore_import(logical_line, filename): """Check for explicit import of the _ function We need to ensure that any files that are using the _() function to translate logs are explicitly importing the _ function. We can't trust unit test to catch whether the import has been added so we need to check for it here. """ # Build a list of the files that have _ imported. No further # checking needed once it is found. if filename in UNDERSCORE_IMPORT_FILES: pass elif (underscore_import_check.match(logical_line) or custom_underscore_check.match(logical_line)): UNDERSCORE_IMPORT_FILES.append(filename) elif (translated_log.match(logical_line) or string_translation.match(logical_line)): yield (0, "D703: Found use of _() without explicit import of _!") @core.flake8ext def no_import_graduated_oslo_libraries(logical_line, filename): """Check that we don't continue to use o.c. oslo libraries after graduation After a library graduates from oslo-incubator, as we make the switch, we should ensure we don't continue to use the oslo-incubator versions. In many cases, it's not possible to immediately remove the code from the openstack/common folder due to dependency issues. """ # We can't modify oslo-incubator code, so ignore it here. if "designate/openstack/common" in filename: return matches = graduated_oslo_libraries_import_re.match(logical_line) if matches: yield (0, "D704: Found import of %s. This oslo library has been " "graduated!" % matches.group(1)) @core.flake8ext def use_timeutils_utcnow(logical_line, filename): # tools are OK to use the standard datetime module if "/tools/" in filename: return msg = "D705: timeutils.utcnow() must be used instead of datetime.%s()" datetime_funcs = ['now', 'utcnow'] for f in datetime_funcs: pos = logical_line.find('datetime.%s' % f) if pos != -1: yield (pos, msg % f) @core.flake8ext def check_no_basestring(logical_line): if re.search(r"\bbasestring\b", logical_line): msg = ("D707: basestring is not Python3-compatible, use " "str instead.") yield (0, msg) @core.flake8ext def check_python3_xrange(logical_line): if re.search(r"\bxrange\s*\(", logical_line): yield (0, "D708: Do not use xrange. Use range for " "large loops.") @core.flake8ext def check_no_log_audit(logical_line): """Ensure that we are not using LOG.audit messages Plans are in place going forward as discussed in the following spec (https://review.opendev.org/#/c/132552/) to take out LOG.audit messages. Given that audit was a concept invented for OpenStack we can enforce not using it. """ if "LOG.audit(" in logical_line: yield (0, "D709: LOG.audit is deprecated, please use LOG.info!") @core.flake8ext def check_no_log_warn(logical_line): """Disallow 'LOG.warn(' D710 """ if logical_line.startswith('LOG.warn('): yield (0, "D710:Use LOG.warning() rather than LOG.warn()") @core.flake8ext def check_line_continuation_no_backslash(logical_line, tokens): """D711 - Don't use backslashes for line continuation. :param logical_line: The logical line to check. Not actually used. :param tokens: List of tokens to check. :returns: None if the tokens don't contain any issues, otherwise a tuple is yielded that contains the offending index in the logical line and a message describe the check validation failure. """ backslash = None for token_type, text, start, end, orig_line in tokens: m = no_line_continuation_backslash_re.match(orig_line) if m: backslash = (start[0], m.start(1)) break if backslash is not None: msg = 'D711 Backslash line continuations not allowed' yield backslash, msg ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1725031659.529339 python-designateclient-6.1.0/designateclient/osc/0000775000175000017500000000000000000000000022150 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/osc/__init__.py0000664000175000017500000000000000000000000024247 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/osc/plugin.py0000664000175000017500000000307100000000000024021 0ustar00zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """OpenStackClient plugin for DNS service.""" import os from osc_lib import utils as oscutils DEFAULT_API_VERSION = '2' API_NAME = 'dns' API_VERSION_OPTION = 'os_dns_api_version' API_VERSIONS = { '2': 'designateclient.v2.client.Client', } def make_client(instance): cls = oscutils.get_client_class( API_NAME, instance._api_version[API_NAME], API_VERSIONS) kwargs = oscutils.build_kwargs_dict('endpoint_type', instance._interface) return cls(session=instance.session, region_name=instance._region_name, **kwargs) def build_option_parser(parser): """Hook to add global options.""" parser.add_argument( '--os-dns-api-version', metavar='', default=os.environ.get('OS_DNS_API_VERSION', '2'), help=('DNS API version, default=' + DEFAULT_API_VERSION + ' (Env: OS_DNS_API_VERSION)')) return parser ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1725031659.533339 python-designateclient-6.1.0/designateclient/tests/0000775000175000017500000000000000000000000022526 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/__init__.py0000664000175000017500000000000000000000000024625 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/base.py0000664000175000017500000001241100000000000024011 0ustar00zuulzuul00000000000000# Copyright 2010-2011 OpenStack Foundation # Copyright (c) 2015 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 os import fixtures from keystoneauth1 import session as keystone_session from oslo_serialization import jsonutils from oslotest import base as test from requests_mock.contrib import fixture as req_fixture from urllib import parse as urlparse from designateclient import client from designateclient.utils import AdapterWithTimeout _TRUE_VALUES = ('True', 'true', '1', 'yes') class TestCase(test.BaseTestCase): """Test case base class for all unit tests.""" def setUp(self): """Run before each test method to initialize test environment.""" super().setUp() test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0) try: test_timeout = int(test_timeout) except ValueError: # If timeout value is invalid do not set a timeout. test_timeout = 0 if test_timeout > 0: self.useFixture(fixtures.Timeout(test_timeout, gentle=True)) self.useFixture(fixtures.NestedTempfile()) self.useFixture(fixtures.TempHomeDir()) if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES: stdout = self.useFixture(fixtures.StringStream('stdout')).stream self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES: stderr = self.useFixture(fixtures.StringStream('stderr')).stream self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) self.log_fixture = self.useFixture(fixtures.FakeLogger()) class APITestCase(TestCase): """Test case base class for all unit tests.""" TEST_URL = "http://127.0.0.1:9001/" VERSION = None def setUp(self): """Run before each test method to initialize test environment.""" super(TestCase, self).setUp() self.log_fixture = self.useFixture(fixtures.FakeLogger()) self.requests = self.useFixture(req_fixture.Fixture()) self.client = self.get_client() def get_base(self, base_url=None): if not base_url: base_url = f'{self.TEST_URL}v{self.VERSION}' return base_url def stub_url(self, method, parts=None, base_url=None, json=None, **kwargs): base_url = self.get_base(base_url) if json: kwargs['text'] = jsonutils.dumps(json) headers = kwargs.setdefault('headers', {}) headers['Content-Type'] = 'application/json' if parts: url = '/'.join([p.strip('/') for p in [base_url] + parts]) else: url = base_url url = url.replace("/?", "?") self.requests.register_uri(method, url, **kwargs) def get_client(self, version=None, session=None): version = version or self.VERSION session = session or keystone_session.Session() adapted = AdapterWithTimeout( session=session, endpoint_override=self.get_base()) return client.Client(version, session=adapted) def assertRequestBodyIs(self, body=None, json=None): last_request_body = self.requests.last_request.body if json: val = jsonutils.loads(last_request_body) self.assertEqual(json, val) elif body: self.assertEqual(body, last_request_body) def assertQueryStringIs(self, qs=''): """Verify the QueryString matches what is expected. The qs parameter should be of the format \'foo=bar&abc=xyz\' """ expected = urlparse.parse_qs(qs, keep_blank_values=True) parts = urlparse.urlparse(self.requests.last_request.url) querystring = urlparse.parse_qs(parts.query, keep_blank_values=True) self.assertEqual(expected, querystring) def assertQueryStringContains(self, **kwargs): """Verify the query string contains the expected parameters. This method is used to verify that the query string for the most recent request made contains all the parameters provided as ``kwargs``, and that the value of each parameter contains the value for the kwarg. If the value for the kwarg is an empty string (''), then all that's verified is that the parameter is present. """ parts = urlparse.urlparse(self.requests.last_request.url) qs = urlparse.parse_qs(parts.query, keep_blank_values=True) for k, v in kwargs.items(): self.assertIn(k, qs) self.assertIn(v, qs[k]) def assertRequestHeaderEqual(self, name, val): """Verify that the last request made contains a header and its value The request must have already been made. """ headers = self.requests.last_request.headers self.assertEqual(val, headers.get(name)) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1725031659.533339 python-designateclient-6.1.0/designateclient/tests/osc/0000775000175000017500000000000000000000000023312 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/osc/__init__.py0000664000175000017500000000000000000000000025411 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1725031659.533339 python-designateclient-6.1.0/designateclient/tests/osc/resources/0000775000175000017500000000000000000000000025324 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/osc/resources/__init__.py0000664000175000017500000000133500000000000027437 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import json import os.path path = os.path.dirname(os.path.realpath(__file__)) def load(action): with open(os.path.join(path, f'{action}.json')) as fp: return json.load(fp) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/osc/resources/recordset_create.json0000664000175000017500000000115600000000000031537 0ustar00zuulzuul00000000000000{ "id": "a00ac483-d74d-4555-b30f-2e0a176287e2", "zone_id": "6f106adb-0896-4114-b34f-4ac8dfee9465", "project_id": "15a6ae43e4dd498383e127e5f2521301", "name": "example.internal.example.org.", "zone_name": "internal.example.org.", "type": "A", "records": [ "127.0.0.1", "127.0.0.2" ], "description": null, "ttl": null, "status": "PENDING", "action": "CREATE", "version": 1, "created_at": "2021-12-05T23:47:57.000000", "updated_at": null, "links": { "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets/a00ac483-d74d-4555-b30f-2e0a176287e2" } } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/osc/resources/recordset_list.json0000664000175000017500000000443600000000000031253 0ustar00zuulzuul00000000000000{ "recordsets": [ { "id": "393d65fe-c0f1-4a55-a91e-bf6c1725dddc", "zone_id": "6f106adb-0896-4114-b34f-4ac8dfee9465", "project_id": "15a6ae43e4dd498383e127e5f2521301", "name": "internal.example.org.", "zone_name": "internal.example.org.", "type": "NS", "records": [ "ns1.example.org." ], "description": null, "ttl": null, "status": "ACTIVE", "action": "NONE", "version": 1, "created_at": "2021-12-05T23:45:54.000000", "updated_at": null, "links": { "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets/393d65fe-c0f1-4a55-a91e-bf6c1725dddc" } }, { "id": "4055914e-0f2b-4c47-a8d3-287a5286fe92", "zone_id": "6f106adb-0896-4114-b34f-4ac8dfee9465", "project_id": "15a6ae43e4dd498383e127e5f2521301", "name": "internal.example.org.", "zone_name": "internal.example.org.", "type": "SOA", "records": [ "ns1.example.org. fake.example.org. 1638748076 3584 600 86400 3600" ], "description": null, "ttl": null, "status": "ACTIVE", "action": "NONE", "version": 4, "created_at": "2021-12-05T23:45:54.000000", "updated_at": "2021-12-05T23:47:57.000000", "links": { "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets/4055914e-0f2b-4c47-a8d3-287a5286fe92" } }, { "id": "a00ac483-d74d-4555-b30f-2e0a176287e2", "zone_id": "6f106adb-0896-4114-b34f-4ac8dfee9465", "project_id": "15a6ae43e4dd498383e127e5f2521301", "name": "example.internal.example.org.", "zone_name": "internal.example.org.", "type": "A", "records": [ "127.0.0.2", "127.0.0.1" ], "description": null, "ttl": null, "status": "ACTIVE", "action": "NONE", "version": 1, "created_at": "2021-12-05T23:47:57.000000", "updated_at": null, "links": { "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets/a00ac483-d74d-4555-b30f-2e0a176287e2" } } ], "links": { "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets" }, "metadata": { "total_count": 3 } } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/osc/resources/recordset_list_all.json0000664000175000017500000000722100000000000032076 0ustar00zuulzuul00000000000000{ "recordsets": [ { "id": "393d65fe-c0f1-4a55-a91e-bf6c1725dddc", "zone_id": "6f106adb-0896-4114-b34f-4ac8dfee9465", "project_id": "15a6ae43e4dd498383e127e5f2521301", "name": "internal.example.org.", "zone_name": "internal.example.org.", "type": "NS", "records": [ "ns1.example.org." ], "description": null, "ttl": null, "status": "ACTIVE", "action": "NONE", "version": 1, "created_at": "2021-12-05T23:45:54.000000", "updated_at": null, "links": { "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets/393d65fe-c0f1-4a55-a91e-bf6c1725dddc" } }, { "id": "4055914e-0f2b-4c47-a8d3-287a5286fe92", "zone_id": "6f106adb-0896-4114-b34f-4ac8dfee9465", "project_id": "15a6ae43e4dd498383e127e5f2521301", "name": "internal.example.org.", "zone_name": "internal.example.org.", "type": "SOA", "records": [ "ns1.example.org. fake.example.org. 1638748076 3584 600 86400 3600" ], "description": null, "ttl": null, "status": "ACTIVE", "action": "NONE", "version": 4, "created_at": "2021-12-05T23:45:54.000000", "updated_at": "2021-12-05T23:47:57.000000", "links": { "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets/4055914e-0f2b-4c47-a8d3-287a5286fe92" } }, { "id": "a00ac483-d74d-4555-b30f-2e0a176287e2", "zone_id": "6f106adb-0896-4114-b34f-4ac8dfee9465", "project_id": "15a6ae43e4dd498383e127e5f2521301", "name": "example.internal.example.org.", "zone_name": "internal.example.org.", "type": "A", "records": [ "127.0.0.2", "127.0.0.1" ], "description": null, "ttl": null, "status": "ACTIVE", "action": "NONE", "version": 1, "created_at": "2021-12-05T23:47:57.000000", "updated_at": null, "links": { "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets/a00ac483-d74d-4555-b30f-2e0a176287e2" } }, { "id": "2f5b0a00-2f03-426c-a5fc-5c1c85a5156b", "zone_id": "a5f8e94e-af03-4e3e-b3eb-688a3b9f3b31", "project_id": "15a6ae43e4dd498383e127e5f2521301", "name": "external.example.org.", "zone_name": "external.example.org.", "type": "SOA", "records": [ "ns1.example.org. another_fake.example.org. 1638748236 3554 600 86400 3600" ], "description": null, "ttl": null, "status": "PENDING", "action": "CREATE", "version": 1, "created_at": "2021-12-05T23:50:36.000000", "updated_at": null, "links": { "self": "http://10.0.1.203/dns/v2/zones/a5f8e94e-af03-4e3e-b3eb-688a3b9f3b31/recordsets/2f5b0a00-2f03-426c-a5fc-5c1c85a5156b" } }, { "id": "4aacd04d-2546-43d9-98cd-58960a0a2494", "zone_id": "a5f8e94e-af03-4e3e-b3eb-688a3b9f3b31", "project_id": "15a6ae43e4dd498383e127e5f2521301", "name": "external.example.org.", "zone_name": "external.example.org.", "type": "NS", "records": [ "ns1.example.org." ], "description": null, "ttl": null, "status": "PENDING", "action": "CREATE", "version": 1, "created_at": "2021-12-05T23:50:36.000000", "updated_at": null, "links": { "self": "http://10.0.1.203/dns/v2/zones/a5f8e94e-af03-4e3e-b3eb-688a3b9f3b31/recordsets/4aacd04d-2546-43d9-98cd-58960a0a2494" } } ], "links": { "self": "http://10.0.1.203/dns/v2/recordsets" }, "metadata": { "total_count": 5 } } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/osc/resources/zone_create.json0000664000175000017500000000112500000000000030514 0ustar00zuulzuul00000000000000{ "id": "6f106adb-0896-4114-b34f-4ac8dfee9465", "pool_id": "794ccc2c-d751-44fe-b57f-8894c9f5c842", "project_id": "15a6ae43e4dd498383e127e5f2521301", "name": "internal.example.org.", "email": "fake@example.org", "description": null, "ttl": 3600, "serial": 1638747953, "status": "PENDING", "action": "CREATE", "version": 1, "attributes": {}, "type": "PRIMARY", "masters": [], "created_at": "2021-12-05T23:45:54.000000", "updated_at": null, "transferred_at": null, "links": { "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465" } } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/osc/resources/zone_list.json0000664000175000017500000000303000000000000030221 0ustar00zuulzuul00000000000000{ "zones": [ { "id": "6f106adb-0896-4114-b34f-4ac8dfee9465", "pool_id": "794ccc2c-d751-44fe-b57f-8894c9f5c842", "project_id": "15a6ae43e4dd498383e127e5f2521301", "name": "internal.example.org.", "email": "fake@example.org", "description": null, "ttl": 3600, "serial": 1638748076, "status": "ACTIVE", "action": "NONE", "version": 8, "attributes": {}, "type": "PRIMARY", "masters": [], "created_at": "2021-12-05T23:45:54.000000", "updated_at": "2021-12-05T23:48:02.000000", "transferred_at": null, "links": { "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465" } }, { "id": "a5f8e94e-af03-4e3e-b3eb-688a3b9f3b31", "pool_id": "794ccc2c-d751-44fe-b57f-8894c9f5c842", "project_id": "15a6ae43e4dd498383e127e5f2521301", "name": "external.example.org.", "email": "another_fake@example.org", "description": null, "ttl": 3600, "serial": 1638748236, "status": "ACTIVE", "action": "NONE", "version": 2, "attributes": {}, "type": "PRIMARY", "masters": [], "created_at": "2021-12-05T23:50:36.000000", "updated_at": "2021-12-05T23:50:42.000000", "transferred_at": null, "links": { "self": "http://10.0.1.203/dns/v2/zones/a5f8e94e-af03-4e3e-b3eb-688a3b9f3b31" } } ], "links": { "self": "http://10.0.1.203/dns/v2/zones?type=PRIMARY" }, "metadata": { "total_count": 2 } } ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1725031659.533339 python-designateclient-6.1.0/designateclient/tests/osc/v2/0000775000175000017500000000000000000000000023641 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/osc/v2/__init__.py0000664000175000017500000000000000000000000025740 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/osc/v2/test_recordsets.py0000664000175000017500000000621000000000000027426 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from unittest import mock from osc_lib.tests import utils from designateclient.tests.osc import resources from designateclient.v2 import base from designateclient.v2.cli import recordsets class TestDesignateCreateRecordSets(utils.TestCommand): def setUp(self): super().setUp() self.app.client_manager.dns = mock.MagicMock() self.cmd = recordsets.CreateRecordSetCommand(self.app, None) self.dns_client = self.app.client_manager.dns def test_create_recordset(self): arg_list = [ '6f106adb-0896-4114-b34f-4ac8dfee9465', 'example', '--type', 'A', '--record', '127.0.0.1', '--record', '127.0.0.2', ] verify_args = [ ('zone_id', '6f106adb-0896-4114-b34f-4ac8dfee9465'), ('name', 'example'), ('type', 'A'), ('record', ['127.0.0.1', '127.0.0.2']), ] body = resources.load('recordset_create') self.dns_client.recordsets.create.return_value = body parsed_args = self.check_parser(self.cmd, arg_list, verify_args) columns, data = self.cmd.take_action(parsed_args) results = list(data) self.assertEqual(14, len(results)) class TestDesignateListRecordSets(utils.TestCommand): def setUp(self): super().setUp() self.app.client_manager.dns = mock.MagicMock() self.cmd = recordsets.ListRecordSetsCommand(self.app, None) self.dns_client = self.app.client_manager.dns def test_list_recordsets(self): arg_list = ['6f106adb-0896-4114-b34f-4ac8dfee9465'] verify_args = [ ('zone_id', '6f106adb-0896-4114-b34f-4ac8dfee9465'), ] body = resources.load('recordset_list') result = base.DesignateList() result.extend(body['recordsets']) self.dns_client.recordsets.list.return_value = result parsed_args = self.check_parser(self.cmd, arg_list, verify_args) columns, data = self.cmd.take_action(parsed_args) results = list(data) self.assertEqual(3, len(results)) def test_list_all_recordsets(self): arg_list = ['all'] verify_args = [ ('zone_id', 'all'), ] body = resources.load('recordset_list_all') result = base.DesignateList() result.extend(body['recordsets']) self.dns_client.recordsets.list_all_zones.return_value = result parsed_args = self.check_parser(self.cmd, arg_list, verify_args) columns, data = self.cmd.take_action(parsed_args) results = list(data) self.assertEqual(5, len(results)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/osc/v2/test_zone.py0000664000175000017500000000437300000000000026234 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from unittest import mock from osc_lib.tests import utils from designateclient.tests.osc import resources from designateclient.v2 import base from designateclient.v2.cli import zones class TestDesignateCreateZone(utils.TestCommand): def setUp(self): super().setUp() self.app.client_manager.dns = mock.MagicMock() self.cmd = zones.CreateZoneCommand(self.app, None) self.dns_client = self.app.client_manager.dns def test_create_zone(self): arg_list = [ 'example.devstack.org.', '--email', 'fake@devstack.org', ] verify_args = [ ('name', 'example.devstack.org.'), ('email', 'fake@devstack.org'), ] body = resources.load('zone_create') self.dns_client.zones.create.return_value = body parsed_args = self.check_parser(self.cmd, arg_list, verify_args) columns, data = self.cmd.take_action(parsed_args) results = list(data) self.assertEqual(17, len(results)) class TestDesignateListZones(utils.TestCommand): def setUp(self): super().setUp() self.app.client_manager.dns = mock.MagicMock() self.cmd = zones.ListZonesCommand(self.app, None) self.dns_client = self.app.client_manager.dns def test_list_zones(self): arg_list = [] verify_args = [] body = resources.load('zone_list') result = base.DesignateList() result.extend(body['zones']) self.dns_client.zones.list.return_value = result parsed_args = self.check_parser(self.cmd, arg_list, verify_args) columns, data = self.cmd.take_action(parsed_args) results = list(data) self.assertEqual(2, len(results)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/test_designateclient.py0000664000175000017500000000152200000000000027301 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. """ test_designateclient ---------------------------------- Tests for `designateclient` module. """ import designateclient from designateclient.tests import base class ClientTestCase(base.TestCase): def test_module_version(self): self.assertTrue(hasattr(designateclient, '__version__')) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/test_exceptions.py0000664000175000017500000000447000000000000026325 0ustar00zuulzuul00000000000000# Copyright 2015 Rackspace Inc. # # Author: James Li # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from designateclient import exceptions from designateclient.tests import base class RemoteErrorTestCase(base.TestCase): response_dict = { 'message': None, 'code': 500, 'type': None, 'errors': None, 'request_id': 1234 } def test_get_error_message(self): expected_msg = 'something wrong' self.response_dict['message'] = expected_msg remote_err = exceptions.RemoteError(**self.response_dict) self.assertEqual(expected_msg, remote_err.message) def test_get_error_message_with_errors(self): expected_msg = "u'nodot.com' is not a 'domainname'" errors = {"errors": [ {"path": ["name"], "message": expected_msg, "validator": "format", "validator_value": "domainname"} ] } self.response_dict['message'] = None self.response_dict['errors'] = errors remote_err = exceptions.RemoteError(**self.response_dict) self.assertEqual(expected_msg, remote_err.message) def test_get_error_message_with_type(self): expected_msg = 'invalid_object' self.response_dict['message'] = None self.response_dict['errors'] = None self.response_dict['type'] = expected_msg remote_err = exceptions.RemoteError(**self.response_dict) self.assertEqual(expected_msg, remote_err.message) def test_get_error_message_with_unknown_response(self): expected_msg = 'invalid_object' self.response_dict['message'] = expected_msg self.response_dict['unknown'] = 'fake' remote_err = exceptions.RemoteError(**self.response_dict) self.assertEqual(expected_msg, remote_err.message) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/test_utils.py0000664000175000017500000000465600000000000025312 0ustar00zuulzuul00000000000000# Copyright (c) 2015 Thales Services SAS # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from unittest import mock import uuid from designateclient import exceptions from designateclient.tests import base from designateclient import utils LIST_MOCK_RESPONSE = [ {'id': '13579bdf-0000-0000-abcd-000000000001', 'name': 'abcd'}, {'id': '13579bdf-0000-0000-baba-000000000001', 'name': 'baba'}, {'id': '13579bdf-0000-0000-baba-000000000002', 'name': 'baba'}, ] class UtilsTestCase(base.TestCase): def _find_resourceid_by_name_or_id(self, name_or_id, by_name=False): resource_client = mock.Mock() resource_client.list.return_value = LIST_MOCK_RESPONSE resourceid = utils.find_resourceid_by_name_or_id( resource_client, name_or_id) self.assertEqual(by_name, resource_client.list.called) return resourceid def test_find_resourceid_with_hyphen_uuid(self): expected = str(uuid.uuid4()) observed = self._find_resourceid_by_name_or_id(expected) self.assertEqual(expected, observed) def test_find_resourceid_with_nonhyphen_uuid(self): expected = str(uuid.uuid4()) fakeid = expected.replace('-', '') observed = self._find_resourceid_by_name_or_id(fakeid) self.assertEqual(expected, observed) def test_find_resourceid_with_unique_resource(self): observed = self._find_resourceid_by_name_or_id('abcd', by_name=True) self.assertEqual('13579bdf-0000-0000-abcd-000000000001', observed) def test_find_resourceid_with_nonexistent_resource(self): self.assertRaises(exceptions.ResourceNotFound, self._find_resourceid_by_name_or_id, 'taz', by_name=True) def test_find_resourceid_with_multiple_resources(self): self.assertRaises(exceptions.NoUniqueMatch, self._find_resourceid_by_name_or_id, 'baba', by_name=True) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1725031659.537339 python-designateclient-6.1.0/designateclient/tests/v2/0000775000175000017500000000000000000000000023055 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/v2/__init__.py0000664000175000017500000000274300000000000025174 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import uuid from designateclient.tests import base class CrudMixin: path_prefix = None def new_ref(self, **kwargs): kwargs.setdefault('id', uuid.uuid4().hex) return kwargs def stub_entity(self, method, parts=None, entity=None, id=None, **kwargs): if entity: kwargs['json'] = entity if not parts: parts = [self.RESOURCE] if self.path_prefix: parts.insert(0, self.path_prefix) if id: if not parts: parts = [] parts.append(id) self.stub_url(method, parts=parts, **kwargs) def assertList(self, expected, actual): self.assertEqual(len(expected), len(actual)) for i in expected: self.assertIn(i, actual) class APIV2TestCase(base.APITestCase): VERSION = "2" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/v2/test_blacklists.py0000664000175000017500000000472200000000000026626 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import uuid from designateclient.tests import v2 class TestBlacklists(v2.APIV2TestCase, v2.CrudMixin): RESOURCE = 'blacklists' def new_ref(self, **kwargs): ref = super().new_ref(**kwargs) ref.setdefault("pattern", uuid.uuid4().hex) return ref def test_create(self): ref = self.new_ref() self.stub_url("POST", parts=[self.RESOURCE], json=ref) values = ref.copy() del values["id"] self.client.blacklists.create(**values) self.assertRequestBodyIs(json=values) def test_create_with_description(self): ref = self.new_ref(description="My Blacklist") self.stub_url("POST", parts=[self.RESOURCE], json=ref) values = ref.copy() del values["id"] self.client.blacklists.create(**values) self.assertRequestBodyIs(json=values) def test_get(self): ref = self.new_ref() self.stub_entity("GET", entity=ref, id=ref["id"]) response = self.client.blacklists.get(ref["id"]) self.assertEqual(ref, response) def test_list(self): items = [ self.new_ref(), self.new_ref() ] self.stub_url("GET", parts=[self.RESOURCE], json={"blacklists": items}) listed = self.client.blacklists.list() self.assertList(items, listed) self.assertQueryStringIs("") def test_update(self): ref = self.new_ref() self.stub_entity("PATCH", entity=ref, id=ref["id"]) values = ref.copy() del values["id"] self.client.blacklists.update(ref["id"], values) self.assertRequestBodyIs(json=values) def test_delete(self): ref = self.new_ref() self.stub_entity("DELETE", id=ref["id"]) self.client.blacklists.delete(ref["id"]) self.assertRequestBodyIs(None) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/v2/test_client.py0000664000175000017500000000304600000000000025747 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Federico Ceratto # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneauth1 import adapter from keystoneauth1 import session as keystone_session from designateclient.tests.base import TestCase from designateclient.v2.client import Client class TestClient(TestCase): def test_init(self): self.assertRaises(ValueError, Client) def test_init_with_session(self): session = keystone_session.Session() adapted = adapter.Adapter(session=session) client = Client(session=adapted) assert client.session def test_init_with_session_timeout(self): session = keystone_session.Session() client = Client( session=session, timeout=1) assert client.session.timeout == 1 def test_init_with_auth(self): session = keystone_session.Session() client = Client( auth='http://127.0.0.1:22/', session=session) assert client.session.auth ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/v2/test_limits.py0000664000175000017500000000165500000000000025776 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from designateclient.tests import v2 class TestLimits(v2.APIV2TestCase, v2.CrudMixin): def test_get(self): ref = {"max_zones": "foo"} self.stub_url("GET", parts=["limits"], json=ref) limits = self.client.limits.get() self.assertEqual(ref, limits) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/v2/test_nameservers.py0000664000175000017500000000235200000000000027022 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES 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.mock import patch from designateclient.tests import v2 from designateclient.v2 import zones class TestLimits(v2.APIV2TestCase, v2.CrudMixin): @patch.object(zones.ZoneController, "list") def test_get(self, zones_get): zones_get.return_value = [{"id": "foo"}] ref = [{ "hostname": "ns1.example.com.", "priority": 1 }] parts = ["zones", "foo", "nameservers"] self.stub_url("GET", parts=parts, json={"nameservers": ref}) response = self.client.nameservers.list("foo") self.assertEqual(ref, response) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/v2/test_recordsets.py0000664000175000017500000001570600000000000026654 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES 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.mock import patch import uuid import testtools from designateclient import exceptions from designateclient.tests import v2 from designateclient.v2 import zones ZONE = { "id": str(uuid.uuid4()), "name": "example.com." } class TestRecordSets(v2.APIV2TestCase, v2.CrudMixin): RESOURCE = 'recordsets' def new_ref(self, **kwargs): ref = super().new_ref(**kwargs) ref.setdefault("name", uuid.uuid4().hex) ref.setdefault("type", "A") ref.setdefault("records", ["10.0.0.1"]) return ref def test_create_absolute_with_zone_dict(self): ref = self.new_ref() parts = ["zones", ZONE["id"], self.RESOURCE] self.stub_url("POST", parts=parts, json=ref) values = ref.copy() del values["id"] self.client.recordsets.create( ZONE, "{}.{}".format(values["name"], ZONE["name"]), values["type"], values["records"]) values["name"] = "{}.{}".format(ref["name"], ZONE["name"]) self.assertRequestBodyIs(json=values) @patch.object(zones.ZoneController, "get") def test_create_absolute_with_zone_name(self, zone_get): ref = self.new_ref() zone_get.return_value = ZONE parts = ["zones", ZONE["id"], self.RESOURCE] self.stub_url("POST", parts=parts, json=ref) values = ref.copy() del values["id"] self.client.recordsets.create( ZONE["name"], "{}.{}".format(values["name"], ZONE["name"]), values["type"], values["records"]) values["name"] = "{}.{}".format(ref["name"], ZONE["name"]) self.assertRequestBodyIs(json=values) @patch.object(zones.ZoneController, "get") def test_create_non_absolute_with_zone_name(self, zone_get): ref = self.new_ref() zone_get.return_value = ZONE parts = ["zones", ZONE["id"], self.RESOURCE] self.stub_url("POST", parts=parts, json=ref) values = ref.copy() del values["id"] self.client.recordsets.create( ZONE["name"], values["name"], values["type"], values["records"]) values["name"] = "{}.{}".format(ref["name"], ZONE["name"]) self.assertRequestBodyIs(json=values) @patch.object(zones.ZoneController, "list") def test_create_non_absolute_with_zone_name_non_unique(self, zone_list): zone_list.return_value = [ 1, 2 ] ref = self.new_ref() values = ref.copy() del values["id"] with testtools.ExpectedException(exceptions.NoUniqueMatch): self.client.recordsets.create( ZONE["name"], "{}.{}".format(values["name"], ZONE["name"]), values["type"], values["records"]) def test_create_absolute_with_zone_id(self): ref = self.new_ref() parts = ["zones", ZONE["id"], self.RESOURCE] self.stub_url("POST", parts=parts, json=ref) values = ref.copy() del values["id"] self.client.recordsets.create( ZONE["id"], "{}.{}".format(values["name"], ZONE["name"]), values["type"], values["records"]) values["name"] = "{}.{}".format(ref["name"], ZONE["name"]) self.assertRequestBodyIs(json=values) @patch.object(zones.ZoneController, "get") def test_create_non_absolute_with_zone_id(self, zone_get): ref = self.new_ref() zone_get.return_value = ZONE parts = ["zones", ZONE["id"], self.RESOURCE] self.stub_url("POST", parts=parts, json=ref) values = ref.copy() del values["id"] self.client.recordsets.create( ZONE["id"], values["name"], values["type"], values["records"]) values["name"] = "{}.{}".format(ref["name"], ZONE["name"]) self.assertRequestBodyIs(json=values) def test_create_with_description(self): ref = self.new_ref(description="Foo") parts = ["zones", ZONE["id"], self.RESOURCE] self.stub_url("POST", parts=parts, json=ref) values = ref.copy() del values["id"] self.client.recordsets.create( ZONE["id"], "{}.{}".format(values["name"], ZONE["name"]), values["type"], values["records"], description=values["description"]) values["name"] = "{}.{}".format(ref["name"], ZONE["name"]) self.assertRequestBodyIs(json=values) def test_create_with_ttl(self): ref = self.new_ref(ttl=60) parts = ["zones", ZONE["id"], self.RESOURCE] self.stub_url("POST", parts=parts, json=ref) values = ref.copy() del values["id"] self.client.recordsets.create( ZONE["id"], "{}.{}".format(values["name"], ZONE["name"]), values["type"], values["records"], ttl=values["ttl"]) values["name"] = "{}.{}".format(ref["name"], ZONE["name"]) self.assertRequestBodyIs(json=values) def test_get(self): ref = self.new_ref() parts = ["zones", ZONE["id"], self.RESOURCE] self.stub_entity("GET", entity=ref, id=ref["id"], parts=parts) response = self.client.recordsets.get(ZONE["id"], ref["id"]) self.assertEqual(ref, response) def test_list(self): items = [ self.new_ref(), self.new_ref() ] parts = ["zones", ZONE["id"], self.RESOURCE] self.stub_url("GET", parts=parts, json={"recordsets": items}) listed = self.client.recordsets.list(ZONE["id"]) self.assertList(items, listed) self.assertQueryStringIs("") def test_update(self): ref = self.new_ref() parts = ["zones", ZONE["id"], self.RESOURCE] self.stub_entity("PUT", entity=ref, id=ref["id"], parts=parts) values = ref.copy() del values["id"] self.client.recordsets.update(ZONE["id"], ref["id"], values) self.assertRequestBodyIs(json=values) def test_delete(self): ref = self.new_ref() parts = ["zones", ZONE["id"], self.RESOURCE] self.stub_entity("DELETE", id=ref["id"], parts=parts) self.client.recordsets.delete(ZONE["id"], ref["id"]) self.assertRequestBodyIs(None) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/v2/test_reverse.py0000664000175000017500000000345000000000000026143 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import uuid from designateclient.tests import v2 FIP_ID = '{}:{}'.format(str(uuid.uuid4()), "RegionOne") class TestFloatingIP(v2.APIV2TestCase, v2.CrudMixin): def test_set(self): name = "foo.com." ref = { "ptrdname": name, "description": "foo" } parts = ["reverse", "floatingips", FIP_ID] self.stub_url("PATCH", parts=parts, json=ref) self.client.floatingips.set(FIP_ID, name, "foo") def test_list(self): ref = [ {"ptrdname": "foo.com."} ] self.stub_url("GET", parts=["reverse", "floatingips"], json={"floatingips": ref}) self.client.floatingips.list() def test_get(self): ref = { "ptrdname": "foo.com." } parts = ["reverse", "floatingips", FIP_ID] self.stub_url("GET", parts=parts, json=ref) self.client.floatingips.get(FIP_ID) def test_unset(self): parts = ["reverse", "floatingips", FIP_ID] self.stub_url("PATCH", parts=parts, json={"ptdrname": None}) self.client.floatingips.unset(FIP_ID) self.assertRequestBodyIs(None) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/v2/test_service_statuses.py0000664000175000017500000000271100000000000030062 0ustar00zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company LP # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from designateclient.tests import v2 class TestServiceStatuses(v2.APIV2TestCase, v2.CrudMixin): RESOURCE = 'service_statuses' def new_ref(self, **kwargs): ref = super().new_ref(**kwargs) ref["name"] = "foo" return ref def test_get(self): ref = self.new_ref() self.stub_entity("GET", entity=ref, id=ref["id"]) response = self.client.service_statuses.get(ref["id"]) self.assertEqual(ref, response) def test_list(self): items = [ self.new_ref(), self.new_ref() ] self.stub_url("GET", parts=[self.RESOURCE], json={"service_statuses": items}) listed = self.client.service_statuses.list() self.assertList(items, listed) self.assertQueryStringIs("") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/v2/test_timeout.py0000664000175000017500000000743000000000000026160 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Federico Ceratto # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES 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.identity import generic from keystoneauth1 import session as keystone_session from unittest.mock import Mock from designateclient.tests import v2 from designateclient.v2.client import Client def create_session(timeout=None): auth = generic.Password(auth_url='', username='', password='', tenant_name='') return keystone_session.Session(auth=auth, timeout=timeout) class TestTimeout(v2.APIV2TestCase, v2.CrudMixin): def setUp(self): super().setUp() # Mock methods in KeyStone's Session self._saved_methods = ( keystone_session.Session.get_auth_headers, keystone_session.Session.get_endpoint, keystone_session.Session._send_request, ) resp = Mock() resp.text = '' resp.status_code = 200 keystone_session.Session.get_auth_headers = Mock( return_value=[] ) keystone_session.Session.get_endpoint = Mock( return_value='foo' ) keystone_session.Session._send_request = Mock( return_value=resp, ) self.mock_send_request = keystone_session.Session._send_request def tearDown(self): super().tearDown() ( keystone_session.Session.get_auth_headers, keystone_session.Session.get_endpoint, keystone_session.Session._send_request, ) = self._saved_methods def _call_request_and_check_timeout(self, client, timeout): """call the mocked _send_request() and check if the timeout was set """ client.limits.get() self.assertTrue(self.mock_send_request.called) kw = self.mock_send_request.call_args[1] if timeout is None: self.assertNotIn('timeout', kw) else: self.assertEqual(timeout, kw['timeout']) def test_no_timeout(self): session = create_session(timeout=None) client = Client(session=session) self.assertIsNone(session.timeout) self.assertIsNone(client.session.timeout) self._call_request_and_check_timeout(client, None) def test_timeout_in_session(self): session = create_session(timeout=1) client = Client(session=session) self.assertEqual(1, session.timeout) self.assertIsNone(client.session.timeout) self._call_request_and_check_timeout(client, 1) def test_timeout_override_session_timeout(self): # The adapter timeout should override the session timeout session = create_session(timeout=10) self.assertEqual(10, session.timeout) client = Client(session=session, timeout=2) self.assertEqual(2, client.session.timeout) self._call_request_and_check_timeout(client, 2) def test_timeout_update(self): session = create_session(timeout=1) client = Client(session=session) self.assertEqual(1, session.timeout) self.assertIsNone(client.session.timeout) self._call_request_and_check_timeout(client, 1) session.timeout = 2 self.assertEqual(2, session.timeout) self._call_request_and_check_timeout(client, 2) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/v2/test_tlds.py0000664000175000017500000000561300000000000025441 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import uuid from designateclient.tests import v2 class TestTlds(v2.APIV2TestCase, v2.CrudMixin): RESOURCE = 'tlds' def new_ref(self, **kwargs): ref = super().new_ref(**kwargs) ref.setdefault("name", uuid.uuid4().hex) return ref def test_create(self): ref = self.new_ref() self.stub_url("POST", parts=[self.RESOURCE], json=ref) values = ref.copy() del values["id"] self.client.tlds.create(**values) self.assertRequestBodyIs(json=values) def test_create_with_description(self): ref = self.new_ref(description="My TLD") self.stub_url("POST", parts=[self.RESOURCE], json=ref) values = ref.copy() del values["id"] self.client.tlds.create(**values) self.assertRequestBodyIs(json=values) def test_get(self): ref = self.new_ref() self.stub_entity("GET", entity=ref, id=ref["id"]) response = self.client.tlds.get(ref["id"]) self.assertEqual(ref, response) def test_get_by_name(self): ref = self.new_ref(name="www") self.stub_entity("GET", entity=ref, id=ref["id"]) self.stub_url("GET", parts=[self.RESOURCE], json={"tlds": [ref]}) response = self.client.tlds.get(ref['name']) self.assertEqual("GET", self.requests.request_history[0].method) self.assertEqual( "http://127.0.0.1:9001/v2/tlds?name=www", self.requests.request_history[0].url) self.assertEqual(ref, response) def test_list(self): items = [ self.new_ref(), self.new_ref() ] self.stub_url("GET", parts=[self.RESOURCE], json={"tlds": items}) listed = self.client.tlds.list() self.assertList(items, listed) self.assertQueryStringIs("") def test_update(self): ref = self.new_ref() self.stub_entity("PATCH", entity=ref, id=ref["id"]) values = ref.copy() del values["id"] self.client.tlds.update(ref["id"], values) self.assertRequestBodyIs(json=values) def test_delete(self): ref = self.new_ref() self.stub_entity("DELETE", id=ref["id"]) self.client.tlds.delete(ref["id"]) self.assertRequestBodyIs(None) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/v2/test_tsigkeys.py0000664000175000017500000000545600000000000026342 0ustar00zuulzuul00000000000000# Copyright 2017 SAP SE # # Author: Rudolf Vriend # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import uuid from designateclient.tests import v2 class TestTSIGKeys(v2.APIV2TestCase, v2.CrudMixin): RESOURCE = 'tsigkeys' def new_ref(self, **kwargs): ref = super().new_ref(**kwargs) ref.setdefault("name", uuid.uuid4().hex) ref.setdefault("algorithm", "hmac-sha256") ref.setdefault("secret", uuid.uuid4().hex) ref.setdefault("scope", "POOL") ref.setdefault("resource_id", uuid.uuid4().hex) return ref def test_create(self): ref = self.new_ref() self.stub_url("POST", parts=[self.RESOURCE], json=ref) values = ref.copy() del values["id"] self.client.tsigkeys.create(**values) self.assertRequestBodyIs(json=values) def test_get(self): ref = self.new_ref() self.stub_entity("GET", entity=ref, id=ref["id"]) response = self.client.tsigkeys.get(ref["id"]) self.assertEqual(ref, response) def test_get_by_name(self): ref = self.new_ref(name="www") self.stub_entity("GET", entity=ref, id=ref["id"]) self.stub_url("GET", parts=[self.RESOURCE], json={"tsigkeys": [ref]}) response = self.client.tsigkeys.get(ref['name']) self.assertEqual("GET", self.requests.request_history[0].method) self.assertEqual( "http://127.0.0.1:9001/v2/tsigkeys?name=www", self.requests.request_history[0].url) self.assertEqual(ref, response) def test_list(self): items = [ self.new_ref(), self.new_ref() ] self.stub_url("GET", parts=[self.RESOURCE], json={"tsigkeys": items}) listed = self.client.tsigkeys.list() self.assertList(items, listed) self.assertQueryStringIs("") def test_update(self): ref = self.new_ref() self.stub_entity("PATCH", entity=ref, id=ref["id"]) values = ref.copy() del values["id"] self.client.tsigkeys.update(ref["id"], values) self.assertRequestBodyIs(json=values) def test_delete(self): ref = self.new_ref() self.stub_entity("DELETE", id=ref["id"]) self.client.tsigkeys.delete(ref["id"]) self.assertRequestBodyIs(None) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/tests/v2/test_zones.py0000664000175000017500000003456400000000000025640 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import time import uuid from designateclient.tests import v2 class TestZones(v2.APIV2TestCase, v2.CrudMixin): RESOURCE = 'zones' def new_ref(self, **kwargs): ref = super().new_ref(**kwargs) ref.setdefault("name", uuid.uuid4().hex) ref.setdefault("type", "PRIMARY") return ref def test_create_with_description(self): ref = self.new_ref(email="root@example.com", description="Foo") self.stub_url("POST", parts=[self.RESOURCE], json=ref) values = ref.copy() del values["id"] self.client.zones.create( values["name"], email=values["email"], description=values["description"]) self.assertRequestBodyIs(json=values) def test_create_primary(self): ref = self.new_ref(email="root@example.com") self.stub_url("POST", parts=[self.RESOURCE], json=ref) values = ref.copy() del values["id"] self.client.zones.create( values["name"], email=values["email"]) self.assertRequestBodyIs(json=values) def test_create_primary_with_ttl(self): ref = self.new_ref(email="root@example.com", ttl=60) self.stub_url("POST", parts=[self.RESOURCE], json=ref) values = ref.copy() del values["id"] self.client.zones.create( values["name"], email=values["email"], ttl=values["ttl"]) self.assertRequestBodyIs(json=values) def test_create_secondary(self): ref = self.new_ref(type="SECONDARY", masters=["10.0.0.1"]) self.stub_url("POST", parts=[self.RESOURCE], json=ref) values = ref.copy() del values["id"] self.client.zones.create( values["name"], type_=values["type"], masters=values["masters"]) self.assertRequestBodyIs(json=values) def test_get(self): ref = self.new_ref() self.stub_entity("GET", entity=ref, id=ref["id"]) response = self.client.zones.get(ref["id"]) self.assertEqual(ref, response) def test_list(self): items = [ self.new_ref(), self.new_ref() ] self.stub_url("GET", parts=[self.RESOURCE], json={"zones": items}) listed = self.client.zones.list() self.assertList(items, listed) self.assertQueryStringIs("") def test_update(self): ref = self.new_ref() self.stub_entity("PATCH", entity=ref, id=ref["id"]) values = ref.copy() del values["id"] self.client.zones.update(ref["id"], values) self.assertRequestBodyIs(json=values) def test_delete(self): ref = self.new_ref() self.stub_entity("DELETE", id=ref["id"]) self.client.zones.delete(ref["id"]) self.assertRequestBodyIs(None) self.assertRequestHeaderEqual('X-Designate-Delete-Shares', None) def test_delete_with_delete_shares(self): ref = self.new_ref() self.stub_entity("DELETE", id=ref["id"]) self.client.zones.delete(ref["id"], delete_shares=True) self.assertRequestBodyIs(None) self.assertRequestHeaderEqual('X-Designate-Delete-Shares', 'true') def test_task_abandon(self): ref = self.new_ref() parts = [self.RESOURCE, ref["id"], "tasks", "abandon"] self.stub_url("POST", parts=parts) self.client.zones.abandon(ref["id"]) self.assertRequestBodyIs(None) def test_task_axfr(self): ref = self.new_ref() parts = [self.RESOURCE, ref["id"], "tasks", "xfr"] self.stub_url("POST", parts=parts) self.client.zones.axfr(ref["id"]) self.assertRequestBodyIs(None) def test_task_pool_move(self): ref = self.new_ref(pool_id=1) parts = [self.RESOURCE, ref["id"], "tasks", "pool_move"] self.stub_url("POST", parts=parts) values = ref.copy() self.client.zones.pool_move(ref["id"], values) self.assertRequestBodyIs(json=values) class TestZoneTransfers(v2.APIV2TestCase, v2.CrudMixin): def test_create_request(self): zone = "098bee04-fe30-4a83-8ccd-e0c496755816" project = "123" ref = { "target_project_id": project } parts = ["zones", zone, "tasks", "transfer_requests"] self.stub_url('POST', parts=parts, json=ref) self.client.zone_transfers.create_request(zone, project) self.assertRequestBodyIs(json=ref) def test_create_request_with_description(self): zone = "098bee04-fe30-4a83-8ccd-e0c496755816" project = "123" ref = { "target_project_id": project, "description": "My Foo" } parts = ["zones", zone, "tasks", "transfer_requests"] self.stub_url('POST', parts=parts, json=ref) self.client.zone_transfers.create_request( zone, project, ref["description"]) self.assertRequestBodyIs(json=ref) def test_get_request(self): transfer = "098bee04-fe30-4a83-8ccd-e0c496755816" project = "098bee04-fe30-4a83-8ccd-e0c496755817" ref = { "target_project_id": project } parts = ["zones", "tasks", "transfer_requests", transfer] self.stub_url('GET', parts=parts, json=ref) self.client.zone_transfers.get_request(transfer) self.assertRequestBodyIs("") def test_list_request(self): project = "098bee04-fe30-4a83-8ccd-e0c496755817" ref = [{ "target_project_id": project }] parts = ["zones", "tasks", "transfer_requests"] self.stub_url('GET', parts=parts, json={"transfer_requests": ref}) self.client.zone_transfers.list_requests() self.assertRequestBodyIs("") def test_update_request(self): transfer = "098bee04-fe30-4a83-8ccd-e0c496755816" project = "098bee04-fe30-4a83-8ccd-e0c496755817" ref = { "target_project_id": project } parts = ["zones", "tasks", "transfer_requests", transfer] self.stub_url('PATCH', parts=parts, json=ref) self.client.zone_transfers.update_request(transfer, ref) self.assertRequestBodyIs(json=ref) def test_delete_request(self): transfer = "098bee04-fe30-4a83-8ccd-e0c496755816" parts = ["zones", "tasks", "transfer_requests", transfer] self.stub_url('DELETE', parts=parts) self.client.zone_transfers.delete_request(transfer) self.assertRequestBodyIs("") def test_accept_request(self): transfer = "098bee04-fe30-4a83-8ccd-e0c496755816" key = "foo123" ref = { "status": "COMPLETE" } parts = ["zones", "tasks", "transfer_accepts"] self.stub_url('POST', parts=parts, json=ref) request = { "key": key, "zone_transfer_request_id": transfer } self.client.zone_transfers.accept_request(transfer, key) self.assertRequestBodyIs(json=request) def test_get_accept(self): accept_id = "098bee04-fe30-4a83-8ccd-e0c496755816" ref = { "status": "COMPLETE" } parts = ["zones", "tasks", "transfer_accepts", accept_id] self.stub_url('GET', parts=parts, json=ref) response = self.client.zone_transfers.get_accept(accept_id) self.assertEqual(ref, response) def test_list_accepts(self): accept_id = "098bee04-fe30-4a83-8ccd-e0c496755816" ref = { "id": accept_id, "status": "COMPLETE" } parts = ["zones", "tasks", "transfer_accepts"] self.stub_url('GET', parts=parts, json={"transfer_accepts": ref}) self.client.zone_transfers.list_accepts() self.assertRequestBodyIs("") class TestZoneExports(v2.APIV2TestCase, v2.CrudMixin): def new_ref(self, **kwargs): ref = super().new_ref(**kwargs) ref.setdefault("zone_id", uuid.uuid4().hex) ref.setdefault("created_at", time.strftime("%c")) ref.setdefault("updated_at", time.strftime("%c")) ref.setdefault("status", 'PENDING') ref.setdefault("version", '1') return ref def test_create_export(self): zone = uuid.uuid4().hex ref = {} parts = ["zones", zone, "tasks", "export"] self.stub_url('POST', parts=parts, json=ref) self.client.zone_exports.create(zone) self.assertRequestBodyIs(json=ref) def test_get_export(self): ref = self.new_ref() parts = ["zones", "tasks", "exports", ref["id"]] self.stub_url('GET', parts=parts, json=ref) self.stub_entity("GET", parts=parts, entity=ref, id=ref["id"]) response = self.client.zone_exports.get_export_record(ref["id"]) self.assertEqual(ref, response) def test_list_exports(self): items = [ self.new_ref(), self.new_ref() ] parts = ["zones", "tasks", "exports"] self.stub_url('GET', parts=parts, json={"exports": items}) listed = self.client.zone_exports.list() self.assertList(items, listed["exports"]) self.assertQueryStringIs("") def test_delete_export(self): ref = self.new_ref() parts = ["zones", "tasks", "exports", ref["id"]] self.stub_url('DELETE', parts=parts, json=ref) self.stub_entity("DELETE", parts=parts, id=ref["id"]) self.client.zone_exports.delete(ref["id"]) self.assertRequestBodyIs(None) def test_get_export_file(self): ref = self.new_ref() parts = ["zones", "tasks", "exports", ref["id"], "export"] self.stub_url('GET', parts=parts, json=ref) self.stub_entity("GET", parts=parts, entity=ref, id=ref["id"]) response = self.client.zone_exports.get_export(ref["id"]) self.assertEqual(ref, response) class TestZoneImports(v2.APIV2TestCase, v2.CrudMixin): def new_ref(self, **kwargs): ref = super().new_ref(**kwargs) ref.setdefault("zone_id", uuid.uuid4().hex) ref.setdefault("created_at", time.strftime("%c")) ref.setdefault("updated_at", time.strftime("%c")) ref.setdefault("status", 'PENDING') ref.setdefault("message", 'Importing...') ref.setdefault("version", '1') return ref def test_create_import(self): zonefile = '$ORIGIN example.com' parts = ["zones", "tasks", "imports"] self.stub_url('POST', parts=parts, json=zonefile) self.client.zone_imports.create(zonefile) self.assertRequestBodyIs(body=zonefile) def test_get_import(self): ref = self.new_ref() parts = ["zones", "tasks", "imports", ref["id"]] self.stub_url('GET', parts=parts, json=ref) self.stub_entity("GET", parts=parts, entity=ref, id=ref["id"]) response = self.client.zone_imports.get_import_record(ref["id"]) self.assertEqual(ref, response) def test_list_imports(self): items = [ self.new_ref(), self.new_ref() ] parts = ["zones", "tasks", "imports"] self.stub_url('GET', parts=parts, json={"imports": items}) listed = self.client.zone_imports.list() self.assertList(items, listed["imports"]) self.assertQueryStringIs("") def test_delete_import(self): ref = self.new_ref() parts = ["zones", "tasks", "imports", ref["id"]] self.stub_url('DELETE', parts=parts, json=ref) self.stub_entity("DELETE", parts=parts, id=ref["id"]) self.client.zone_imports.delete(ref["id"]) self.assertRequestBodyIs(None) class TestZoneShared(v2.APIV2TestCase, v2.CrudMixin): def setUp(self): super().setUp() self.zone_id = str(uuid.uuid4()) self.target_project_id = str(uuid.uuid4()) self.project_id = str(uuid.uuid4()) self.created_at = time.strftime("%c") self.updated_at = time.strftime("%c") def new_ref(self, **kwargs): ref = super().new_ref(**kwargs) ref.setdefault("zone_id", self.zone_id) ref.setdefault("target_project_id", self.target_project_id) ref.setdefault("project_id", self.project_id) ref.setdefault("created_at", self.created_at) ref.setdefault("updated_at", self.updated_at) return ref def test_share_a_zone(self): json_body = {"target_project_id": self.target_project_id} expected = self.new_ref() self.stub_entity('POST', parts=['zones', self.zone_id, 'shares'], entity=expected, json=json_body) response = self.client.zone_share.create(self.zone_id, self.target_project_id) self.assertRequestBodyIs(json=json_body) self.assertEqual(expected, response) def test_get_zone_share(self): expected = self.new_ref() parts = ["zones", self.zone_id, "shares"] self.stub_entity("GET", parts=parts, entity=expected, id=expected["id"]) response = self.client.zone_share.get(self.zone_id, expected["id"]) self.assertRequestBodyIs(None) self.assertEqual(expected, response) def test_list_zone_shares(self): items = [ self.new_ref(), self.new_ref() ] parts = ["zones", self.zone_id, "shares"] self.stub_entity('GET', parts=parts, entity={"shared_zones": items}) listed = self.client.zone_share.list(self.zone_id) self.assertList(items, listed) self.assertQueryStringIs("") def test_delete_zone_share(self): ref = self.new_ref() parts = ["zones", self.zone_id, "shares", ref["id"]] self.stub_url('DELETE', parts=parts) response = self.client.zone_share.delete(self.zone_id, ref["id"]) self.assertRequestBodyIs(None) self.assertEqual('', response) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/utils.py0000664000175000017500000000701500000000000023101 0ustar00zuulzuul00000000000000# Copyright 2012 Managed I.T. # # Author: Kiall Mac Innes # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import uuid from keystoneauth1 import adapter from designateclient import exceptions def get_item_properties(item, fields, mixed_case_fields=(), formatters=None): """Return a tuple containing the item properties. :param item: a single item resource (e.g. Server, Tenant, etc) :param fields: tuple of strings with the desired field names :param mixed_case_fields: tuple of field names to preserve case :param formatters: dictionary mapping field names to callables to format the values """ if formatters is None: formatters = {} row = [] for field in fields: if field in formatters: row.append(formatters[field](item)) else: if field in mixed_case_fields: field_name = field.replace(' ', '_') else: field_name = field.lower().replace(' ', '_') if (not hasattr(item, field_name) and (isinstance(item, dict) and field_name in item)): data = item[field_name] else: data = getattr(item, field_name, '') if data is None: data = '' row.append(data) return tuple(row) def get_columns(data): """ Some row's might have variable count of columns, ensure that we have the same. :param data: Results in [{}, {]}] """ columns = set() def _seen(col): columns.add(str(col)) map(lambda item: map(_seen, list(item.keys())), data) return list(columns) def find_resourceid_by_name_or_id(resource_client, name_or_id): """Find resource id from its id or name.""" try: # Try to return an uuid return str(uuid.UUID(name_or_id)) except ValueError: # Not an uuid => assume it is resource name pass resources = resource_client.list() candidate_ids = [r['id'] for r in resources if r.get('name') == name_or_id] if not candidate_ids: raise exceptions.ResourceNotFound( f'Could not find resource with name "{name_or_id}"') elif len(candidate_ids) > 1: str_ids = ','.join(candidate_ids) raise exceptions.NoUniqueMatch( f'Multiple resources with name "{name_or_id}": {str_ids}') return candidate_ids[0] class AdapterWithTimeout(adapter.Adapter): """adapter.Adapter wraps around a Session. The user can pass a timeout keyword that will apply only to the Designate Client, in order: - timeout keyword passed to ``request()`` - timeout keyword passed to ``AdapterWithTimeout()`` - timeout attribute on keystone session """ def __init__(self, *args, **kw): self.timeout = kw.pop('timeout', None) super(self.__class__, self).__init__(*args, **kw) def request(self, *args, **kwargs): if self.timeout is not None: kwargs.setdefault('timeout', self.timeout) return super().request(*args, **kwargs) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1725031659.541339 python-designateclient-6.1.0/designateclient/v2/0000775000175000017500000000000000000000000021713 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/__init__.py0000664000175000017500000000000000000000000024012 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/base.py0000664000175000017500000000250700000000000023203 0ustar00zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company LP # # Author: Graham Hayes # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from designateclient import client from designateclient.v2.utils import parse_query_from_url class DesignateList(list): next_link_criterion = {} next_page = False class V2Controller(client.Controller): def _get(self, url, response_key=None, **kwargs): resp, body = self.client.session.get(url, **kwargs) if response_key is not None: data = DesignateList() data.extend(body[response_key]) if 'next' in body.get('links', {}): data.next_page = True data.next_link_criterion = parse_query_from_url( body['links']['next']) return data return body ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/blacklists.py0000664000175000017500000000300400000000000024415 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from designateclient.v2.base import V2Controller class BlacklistController(V2Controller): def create(self, pattern, description=None): data = { 'pattern': pattern, } if description is not None: data['description'] = description return self._post('/blacklists', data=data) def list(self, criterion=None, marker=None, limit=None): url = self.build_url('/blacklists', criterion, marker, limit) return self._get(url, response_key='blacklists') def get(self, blacklist_id): url = f'/blacklists/{blacklist_id}' return self._get(url) def update(self, blacklist_id, values): url = f'/blacklists/{blacklist_id}' return self._patch(url, data=values) def delete(self, blacklist_id): url = f'/blacklists/{blacklist_id}' return self._delete(url) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1725031659.541339 python-designateclient-6.1.0/designateclient/v2/cli/0000775000175000017500000000000000000000000022462 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/cli/__init__.py0000664000175000017500000000000000000000000024561 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/cli/blacklists.py0000664000175000017500000001070700000000000025174 0ustar00zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from osc_lib.command import command from designateclient import utils from designateclient.v2.cli import common from designateclient.v2.utils import get_all LOG = logging.getLogger(__name__) def _format_blacklist(blacklist): # Remove unneeded fields for display output formatting blacklist.pop('links', None) class ListBlacklistsCommand(command.Lister): """List blacklists""" columns = ['id', 'pattern', 'description'] def get_parser(self, prog_name): parser = super().get_parser(prog_name) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) cols = self.columns data = get_all(client.blacklists.list) return cols, (utils.get_item_properties(s, cols) for s in data) class ShowBlacklistCommand(command.ShowOne): """Show blacklist details""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='Blacklist ID') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.blacklists.get(parsed_args.id) _format_blacklist(data) return zip(*sorted(data.items())) class CreateBlacklistCommand(command.ShowOne): """Create new blacklist""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('--pattern', help='Blacklist pattern', required=True) parser.add_argument('--description', help='Description') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.blacklists.create( parsed_args.pattern, parsed_args.description) _format_blacklist(data) return zip(*sorted(data.items())) class SetBlacklistCommand(command.ShowOne): """Set blacklist properties""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='Blacklist ID') parser.add_argument('--pattern', help='Blacklist pattern') description_group = parser.add_mutually_exclusive_group() description_group.add_argument('--description', help='Description') description_group.add_argument('--no-description', action='store_true') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): data = {} if parsed_args.pattern: data['pattern'] = parsed_args.pattern if parsed_args.no_description: data['description'] = None elif parsed_args.description: data['description'] = parsed_args.description client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) updated = client.blacklists.update(parsed_args.id, data) _format_blacklist(updated) return zip(*sorted(updated.items())) class DeleteBlacklistCommand(command.Command): """Delete blacklist""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='Blacklist ID') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) client.blacklists.delete(parsed_args.id) LOG.info('Blacklist %s was deleted', parsed_args.id) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/cli/common.py0000664000175000017500000000535100000000000024330 0ustar00zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company LP # # Author: Graham Hayes # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. def add_all_projects_option(parser): parser.add_argument( '--all-projects', default=False, action='store_true', help='Show results from all projects. Default: False' ) def add_edit_managed_option(parser): parser.add_argument( '--edit-managed', default=False, action='store_true', help='Edit resources marked as managed. Default: False' ) def add_hard_delete_option(parser): parser.add_argument( '--hard-delete', default=False, action='store_true', help='Delete zone along-with backend zone resources (i.e. files). ' 'Default: False' ) def add_sudo_project_id_option(parser): parser.add_argument( '--sudo-project-id', default=None, help='Project ID to impersonate for this command. Default: None' ) def add_all_common_options(parser): add_all_projects_option(parser) add_sudo_project_id_option(parser) def set_all_projects(client, value): client.session.all_projects = value def set_sudo_project_id(client, value): client.session.sudo_project_id = value def set_edit_managed(client, value): client.session.edit_managed = value def set_hard_delete(client, value): client.session.hard_delete = value def set_all_common_headers(client, parsed_args): if (parsed_args.all_projects is not None and isinstance(parsed_args.all_projects, bool)): set_all_projects(client, parsed_args.all_projects) if (hasattr(parsed_args, 'edit_managed') and parsed_args.edit_managed is not None and isinstance(parsed_args.edit_managed, bool)): set_edit_managed(client, parsed_args.edit_managed) if (parsed_args.sudo_project_id is not None and isinstance(parsed_args.sudo_project_id, str)): set_sudo_project_id(client, parsed_args.sudo_project_id) if (hasattr(parsed_args, 'hard_delete') and parsed_args.hard_delete is not None and isinstance(parsed_args.hard_delete, bool)): set_hard_delete(client, parsed_args.hard_delete) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/cli/quotas.py0000664000175000017500000000740300000000000024354 0ustar00zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import itertools import logging from cliff import command from cliff import show from designateclient.v2.cli import common LOG = logging.getLogger(__name__) DNS_QUOTAS = { "api_export_size": "api-export-size", "recordset_records": "recordset-records", "zone_records": "zone-records", "zone_recordsets": "zone-recordsets", "zones": "zones" } class ListQuotasCommand(show.ShowOne): """List quotas""" # columns = ['resource', 'hard_limit'] def get_parser(self, prog_name): parser = super().get_parser(prog_name) common.add_all_common_options(parser) parser.add_argument( '--project-id', help='Project ID Default: current project') return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) proj_id = client.session.get_project_id() if parsed_args.project_id and parsed_args.project_id != proj_id: proj_id = parsed_args.project_id common.set_all_projects(client, True) data = client.quotas.list(proj_id) return zip(*sorted(data.items())) class SetQuotasCommand(show.ShowOne): """Set quotas""" def _build_options_list(self): return itertools.chain(DNS_QUOTAS.items()) def get_parser(self, prog_name): parser = super().get_parser(prog_name) common.add_all_common_options(parser) parser.add_argument('--project-id', help='Project ID') for k, v in self._build_options_list(): parser.add_argument( f'--{v}', metavar=f'<{v}>', dest=k, type=int, help=f'New value for the {v} quota', ) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) quotas = {} for k, v in DNS_QUOTAS.items(): value = getattr(parsed_args, k, None) if value is not None: quotas[k] = value proj_id = parsed_args.project_id or client.session.get_project_id() if parsed_args.project_id != client.session.get_project_id(): common.set_all_projects(client, True) updated = client.quotas.update(proj_id, quotas) return zip(*sorted(updated.items())) class ResetQuotasCommand(command.Command): """Reset quotas""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) common.add_all_common_options(parser) parser.add_argument('--project-id', help='Project ID') return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) proj_id = parsed_args.project_id or client.session.get_project_id() if parsed_args.project_id != client.session.get_project_id(): common.set_all_projects(client, True) client.quotas.reset(proj_id) LOG.info('Quota for project %s was reset', parsed_args.project_id) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/cli/recordsets.py0000664000175000017500000002002500000000000025210 0ustar00zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from osc_lib.command import command from designateclient import utils from designateclient.v2.cli import common from designateclient.v2.utils import get_all LOG = logging.getLogger(__name__) def _format_recordset(recordset): # Remove unneeded fields for display output formatting recordset['records'] = "\n".join(recordset['records']) recordset.pop('links', None) return recordset def _has_project_id(data): if len(data) < 1: return False if 'project_id' in data[0]: return True return False class ListRecordSetsCommand(command.Lister): """List recordsets""" columns = ['id', 'name', 'type', 'records', 'status', 'action'] def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('--name', help='RecordSet Name', required=False) parser.add_argument('--type', help='RecordSet Type', required=False) parser.add_argument('--data', help='RecordSet Record Data', required=False) parser.add_argument('--ttl', help='Time To Live (Seconds)', required=False) parser.add_argument('--description', help='Description', required=False) parser.add_argument('--status', help='RecordSet Status', required=False) parser.add_argument('--action', help='RecordSet Action', required=False) parser.add_argument('zone_id', help="Zone ID. To list all" " recordsets specify 'all'") common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) criterion = {} if parsed_args.type is not None: criterion['type'] = parsed_args.type if parsed_args.name is not None: criterion['name'] = parsed_args.name if parsed_args.data is not None: criterion['data'] = parsed_args.data if parsed_args.ttl is not None: criterion['ttl'] = parsed_args.ttl if parsed_args.description is not None: criterion['description'] = parsed_args.description if parsed_args.status is not None: criterion['status'] = parsed_args.status if parsed_args.action is not None: criterion['action'] = parsed_args.action cols = list(self.columns) if parsed_args.zone_id == 'all': data = get_all(client.recordsets.list_all_zones, criterion=criterion) cols.insert(2, 'zone_name') else: data = get_all(client.recordsets.list, args=[parsed_args.zone_id], criterion=criterion) if client.session.all_projects and _has_project_id(data): cols.insert(1, 'project_id') for i, rs in enumerate(data): data[i] = _format_recordset(rs) return cols, (utils.get_item_properties(s, cols) for s in data) class ShowRecordSetCommand(command.ShowOne): """Show recordset details""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('zone_id', help='Zone ID') parser.add_argument('id', help='RecordSet ID') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.recordsets.get(parsed_args.zone_id, parsed_args.id) _format_recordset(data) return zip(*sorted(data.items())) class CreateRecordSetCommand(command.ShowOne): """Create new recordset""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('zone_id', help='Zone ID') parser.add_argument('name', help='RecordSet Name') req_group = parser.add_mutually_exclusive_group(required=True) req_group.add_argument( '--record', help='RecordSet Record, repeat if necessary', action='append') parser.add_argument('--type', help='RecordSet Type', required=True) parser.add_argument('--ttl', type=int, help='Time To Live (Seconds)') parser.add_argument('--description', help='Description') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.recordsets.create( parsed_args.zone_id, parsed_args.name, parsed_args.type, parsed_args.record, description=parsed_args.description, ttl=parsed_args.ttl) _format_recordset(data) return zip(*sorted(data.items())) class SetRecordSetCommand(command.ShowOne): """Set recordset properties""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('zone_id', help='Zone ID') parser.add_argument('id', help='RecordSet ID') req_group = parser.add_mutually_exclusive_group() req_group.add_argument( '--record', help='RecordSet Record, repeat if necessary', action='append') description_group = parser.add_mutually_exclusive_group() description_group.add_argument('--description', help='Description') description_group.add_argument('--no-description', action='store_true') ttl_group = parser.add_mutually_exclusive_group() ttl_group.add_argument('--ttl', type=int, help='TTL') ttl_group.add_argument('--no-ttl', action='store_true') common.add_all_common_options(parser) common.add_edit_managed_option(parser) return parser def take_action(self, parsed_args): data = {} if parsed_args.no_description: data['description'] = None elif parsed_args.description: data['description'] = parsed_args.description if parsed_args.no_ttl: data['ttl'] = None elif parsed_args.ttl: data['ttl'] = parsed_args.ttl if parsed_args.record: data['records'] = parsed_args.record client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) updated = client.recordsets.update( parsed_args.zone_id, parsed_args.id, data) _format_recordset(updated) return zip(*sorted(updated.items())) class DeleteRecordSetCommand(command.ShowOne): """Delete recordset""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('zone_id', help='Zone ID') parser.add_argument('id', help='RecordSet ID') common.add_all_common_options(parser) common.add_edit_managed_option(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.recordsets.delete(parsed_args.zone_id, parsed_args.id) LOG.info('RecordSet %s was deleted', parsed_args.id) _format_recordset(data) return zip(*sorted(data.items())) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/cli/reverse.py0000664000175000017500000001054300000000000024512 0ustar00zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from osc_lib.command import command from designateclient import utils from designateclient.v2.cli import common from designateclient.v2.utils import get_all LOG = logging.getLogger(__name__) def _format_floatingip(fip): # Remove unneeded fields for display output formatting fip.pop('links', None) class ListFloatingIPCommand(command.Lister): """List floatingip ptr records""" columns = ['id', 'ptrdname', 'description', 'ttl'] def get_parser(self, prog_name): parser = super().get_parser(prog_name) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) cols = self.columns data = get_all(client.floatingips.list) return cols, (utils.get_item_properties(s, cols) for s in data) class ShowFloatingIPCommand(command.ShowOne): """Show floatingip ptr record details""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('floatingip_id', help='Floating IP ID in format ' 'region:floatingip_id') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.floatingips.get(parsed_args.floatingip_id) _format_floatingip(data) return zip(*sorted(data.items())) class SetFloatingIPCommand(command.ShowOne): """Set floatingip ptr record""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('floatingip_id', help='Floating IP ID in format ' 'region:floatingip_id') parser.add_argument('ptrdname', help='PTRD Name') description_group = parser.add_mutually_exclusive_group() description_group.add_argument('--description', help='Description') description_group.add_argument('--no-description', action='store_true') ttl_group = parser.add_mutually_exclusive_group() ttl_group.add_argument('--ttl', type=int, help='TTL') ttl_group.add_argument('--no-ttl', action='store_true') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): data = {} if parsed_args.no_description: data['description'] = None elif parsed_args.description: data['description'] = parsed_args.description if parsed_args.no_ttl: data['ttl'] = None elif parsed_args.ttl: data['ttl'] = parsed_args.ttl client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) fip = client.floatingips.set( parsed_args.floatingip_id, parsed_args.ptrdname, parsed_args.description, parsed_args.ttl) _format_floatingip(fip) return zip(*sorted(fip.items())) class UnsetFloatingIPCommand(command.Command): """Unset floatingip ptr record""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('floatingip_id', help='Floating IP ID in format ' 'region:floatingip_id') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) client.floatingips.unset(parsed_args.floatingip_id) LOG.info('FloatingIP PTR %s was unset', parsed_args.floatingip_id) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/cli/service_statuses.py0000664000175000017500000000555000000000000026434 0ustar00zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company LP # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from osc_lib.command import command from designateclient import utils from designateclient.v2.cli import common from designateclient.v2 import utils as v2_utils LOG = logging.getLogger(__name__) def _format_status(status): status.pop('links', None) # Remove unneeded fields for display output formatting for k in ('capabilities', 'stats'): status[k] = '\n'.join(status[k]) if status[k] else '-' return status class ListServiceStatusesCommand(command.Lister): """List service statuses""" columns = ['id', 'hostname', 'service_name', 'status', 'stats', 'capabilities'] def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('--hostname', help='Hostname', required=False) parser.add_argument('--service_name', help='Service Name', required=False) parser.add_argument('--status', help='Status', required=False) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) cols = self.columns criterion = {} for i in ['hostname', 'service_name', 'status']: v = getattr(parsed_args, i) if v is not None: criterion[i] = v data = v2_utils.get_all(client.service_statuses.list, criterion=criterion) for i, s in enumerate(data): data[i] = _format_status(s) return cols, (utils.get_item_properties(s, cols) for s in data) class ShowServiceStatusCommand(command.ShowOne): """Show service status details""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='Service Status ID') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.service_statuses.get(parsed_args.id) _format_status(data) return zip(*sorted(data.items())) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/cli/tlds.py0000664000175000017500000001055300000000000024006 0ustar00zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from osc_lib.command import command from designateclient import utils from designateclient.v2.cli import common from designateclient.v2.utils import get_all LOG = logging.getLogger(__name__) def _format_tld(tld): # Remove unneeded fields for display output formatting tld.pop('links', None) class ListTLDsCommand(command.Lister): """List tlds""" columns = ['id', 'name', 'description'] def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('--name', help='TLD NAME') parser.add_argument('--description', help='TLD Description') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = get_all(client.tlds.list) cols = self.columns return cols, (utils.get_item_properties(s, cols) for s in data) class ShowTLDCommand(command.ShowOne): """Show tld details""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='TLD name or ID') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.tlds.get(parsed_args.id) _format_tld(data) return zip(*sorted(data.items())) class CreateTLDCommand(command.ShowOne): """Create new tld""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('--name', help='TLD Name', required=True) parser.add_argument('--description', help='Description') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.tlds.create(parsed_args.name, parsed_args.description) _format_tld(data) return zip(*sorted(data.items())) class SetTLDCommand(command.ShowOne): """Set tld properties""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='TLD name or ID') parser.add_argument('--name', help='TLD Name') description_group = parser.add_mutually_exclusive_group() description_group.add_argument('--description', help='Description') description_group.add_argument('--no-description', action='store_true') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): data = {} if parsed_args.name: data['name'] = parsed_args.name if parsed_args.no_description: data['description'] = None elif parsed_args.description: data['description'] = parsed_args.description client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.tlds.update(parsed_args.id, data) _format_tld(data) return zip(*sorted(data.items())) class DeleteTLDCommand(command.Command): """Delete tld""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='TLD name or ID') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) client.tlds.delete(parsed_args.id) LOG.info('TLD %s was deleted', parsed_args.id) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/cli/tsigkeys.py0000664000175000017500000001257100000000000024704 0ustar00zuulzuul00000000000000# Copyright 2017 SAP SE # # Author: Rudolf Vriend # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from osc_lib.command import command from designateclient import utils from designateclient.v2.cli import common from designateclient.v2.utils import get_all LOG = logging.getLogger(__name__) def _format_tsigkey(tsigkey): # Remove unneeded fields for display output formatting tsigkey.pop('links', None) class ListTSIGKeysCommand(command.Lister): """List tsigkeys""" columns = ['id', 'name', 'algorithm', 'secret', 'scope', 'resource_id'] def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('--name', help='TSIGKey NAME', required=False) parser.add_argument('--algorithm', help='TSIGKey algorithm', required=False) parser.add_argument('--scope', help='TSIGKey scope', required=False) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) criterion = {} if parsed_args.name is not None: criterion['name'] = parsed_args.name if parsed_args.algorithm is not None: criterion['algorithm'] = parsed_args.algorithm if parsed_args.scope is not None: criterion['scope'] = parsed_args.scope data = get_all(client.tsigkeys.list, criterion) cols = self.columns return cols, (utils.get_item_properties(s, cols) for s in data) class ShowTSIGKeyCommand(command.ShowOne): """Show tsigkey details""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='TSIGKey ID') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.tsigkeys.get(parsed_args.id) _format_tsigkey(data) return zip(*sorted(data.items())) class CreateTSIGKeyCommand(command.ShowOne): """Create new tsigkey""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('--name', help='TSIGKey Name', required=True) parser.add_argument('--algorithm', help='TSIGKey algorithm', required=True) parser.add_argument('--secret', help='TSIGKey secret', required=True) parser.add_argument('--scope', help='TSIGKey scope', required=True) parser.add_argument('--resource-id', help='TSIGKey resource_id', required=True) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.tsigkeys.create(parsed_args.name, parsed_args.algorithm, parsed_args.secret, parsed_args.scope, parsed_args.resource_id) _format_tsigkey(data) return zip(*sorted(data.items())) class SetTSIGKeyCommand(command.ShowOne): """Set tsigkey properties""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='TSIGKey ID') parser.add_argument('--name', help='TSIGKey Name') parser.add_argument('--algorithm', help='TSIGKey algorithm') parser.add_argument('--secret', help='TSIGKey secret') parser.add_argument('--scope', help='TSIGKey scope') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): data = {} if parsed_args.name: data['name'] = parsed_args.name if parsed_args.algorithm: data['algorithm'] = parsed_args.algorithm if parsed_args.secret: data['secret'] = parsed_args.secret if parsed_args.scope: data['scope'] = parsed_args.scope client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.tsigkeys.update(parsed_args.id, data) _format_tsigkey(data) return zip(*sorted(data.items())) class DeleteTSIGKeyCommand(command.Command): """Delete tsigkey""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='TSIGKey ID') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) client.tsigkeys.delete(parsed_args.id) LOG.info('TSIGKey %s was deleted', parsed_args.id) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/cli/zones.py0000664000175000017500000006304600000000000024203 0ustar00zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from osc_lib.command import command from osc_lib import exceptions as osc_exc from designateclient import utils from designateclient.v2.cli import common from designateclient.v2.utils import get_all LOG = logging.getLogger(__name__) def _format_zone(zone): zone.pop('links', None) zone['masters'] = ', '.join(zone['masters']) attrib = '' for attr in zone['attributes']: attrib += '{}:{}\n'.format(attr, zone['attributes'][attr]) zone['attributes'] = attrib def _format_zone_export_record(zone_export_record): zone_export_record.pop('links', None) def _format_zone_import_record(zone_import_record): zone_import_record.pop('links', None) class ListZonesCommand(command.Lister): """List zones""" columns = ['id', 'name', 'type', 'serial', 'status', 'action'] def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('--name', help='Zone Name', required=False) parser.add_argument('--email', help='Zone Email', required=False) parser.add_argument('--type', help='Zone Type', choices=['PRIMARY', 'SECONDARY'], default=None, required=False) parser.add_argument('--ttl', help='Time To Live (Seconds)', required=False) parser.add_argument('--description', help='Description', required=False) parser.add_argument('--status', help='Zone Status', required=False) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) criterion = {} if parsed_args.type is not None: criterion['type'] = parsed_args.type if parsed_args.name is not None: criterion['name'] = parsed_args.name if parsed_args.ttl is not None: criterion['ttl'] = parsed_args.ttl if parsed_args.description is not None: criterion['description'] = parsed_args.description if parsed_args.email is not None: criterion['email'] = parsed_args.email if parsed_args.status is not None: criterion['status'] = parsed_args.status data = get_all(client.zones.list, criterion) cols = list(self.columns) if client.session.all_projects: cols.insert(1, 'project_id') return cols, (utils.get_item_properties(s, cols) for s in data) class ShowZoneCommand(command.ShowOne): """Show zone details""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='Zone ID') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zones.get(parsed_args.id) _format_zone(data) return zip(*sorted(data.items())) class CreateZoneCommand(command.ShowOne): """Create new zone""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('name', help='Zone Name') parser.add_argument('--email', help='Zone Email') parser.add_argument('--type', help='Zone Type', choices=['PRIMARY', 'SECONDARY'], default='PRIMARY') parser.add_argument('--ttl', type=int, help='Time To Live (Seconds)') parser.add_argument('--description', help='Description') parser.add_argument('--masters', help='Zone Masters', nargs='+') parser.add_argument('--attributes', help='Zone Attributes', nargs='+') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) payload = {} if parsed_args.description: payload['description'] = parsed_args.description if parsed_args.attributes: payload['attributes'] = {} for attr in parsed_args.attributes: try: k, v = attr.split(':') payload['attributes'][k] = v except ValueError: raise osc_exc.CommandError( f"Attribute '{attr}' is in an incorrect format. " "Attributes are : formated" ) if parsed_args.type == 'PRIMARY': # email is just for PRIMARY. if not parsed_args.email: raise osc_exc.CommandError( 'Zone type PRIMARY requires --email.' ) payload['email'] = parsed_args.email # TTL is just valid for PRIMARY if parsed_args.ttl is not None: payload['ttl'] = parsed_args.ttl elif parsed_args.type == 'SECONDARY': payload['masters'] = parsed_args.masters else: raise osc_exc.CommandError( f'Type {parsed_args.type} is not supported. Please choose ' 'between PRIMARY or SECONDARY' ) data = client.zones.create( parsed_args.name, parsed_args.type, **payload) _format_zone(data) return zip(*sorted(data.items())) class SetZoneCommand(command.ShowOne): """Set zone properties""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='Zone ID') parser.add_argument('--email', help='Zone Email') parser.add_argument('--ttl', type=int, help='Time To Live (Seconds)') description_group = parser.add_mutually_exclusive_group() description_group.add_argument('--description', help='Description') description_group.add_argument('--no-description', action='store_true') parser.add_argument('--masters', help='Zone Masters', nargs='+') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = {} # TODO(kiall): API needs updating.. this get is silly if parsed_args.email: data['email'] = parsed_args.email if parsed_args.ttl: data['ttl'] = parsed_args.ttl if parsed_args.no_description: data['description'] = None elif parsed_args.description: data['description'] = parsed_args.description if parsed_args.masters: data['masters'] = parsed_args.masters updated = client.zones.update(parsed_args.id, data) _format_zone(updated) return zip(*sorted(updated.items())) class DeleteZoneCommand(command.ShowOne): """Delete zone""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='Zone ID') parser.add_argument('--delete-shares', default=False, action='store_true', help='Delete existing zone shares. Default: False') common.add_all_common_options(parser) common.add_hard_delete_option(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) delete_shares = False if (hasattr(parsed_args, 'delete_shares') and parsed_args.delete_shares is not None and isinstance(parsed_args.delete_shares, bool)): delete_shares = parsed_args.delete_shares data = client.zones.delete(parsed_args.id, delete_shares=delete_shares) LOG.info('Zone %s was deleted', parsed_args.id) _format_zone(data) return zip(*sorted(data.items())) class AbandonZoneCommand(command.Command): """Abandon a zone""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='Zone ID') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) client.zones.abandon(parsed_args.id) LOG.info('Z %(zone_id)s abandoned', {'zone_id': parsed_args.id}) class AXFRZoneCommand(command.Command): """AXFR a zone""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='Zone ID') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) client.zones.axfr(parsed_args.id) LOG.info('Scheduled AXFR for zone %(zone_id)s', {'zone_id': parsed_args.id}) class PoolMoveZoneCommand(command.Command): """Move a zone to another pool""" def get_parser(self, prog_name): parser = super(PoolMoveZoneCommand, self).get_parser(prog_name) parser.add_argument('zone_id', help="Zone ID") parser.add_argument('--pool-id', help="Pool ID") common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = {} if parsed_args.pool_id: data['pool_id'] = parsed_args.pool_id client.zones.pool_move(parsed_args.zone_id, data) LOG.info("Scheduled move for zone %(zone_id)s", {"zone_id": parsed_args.zone_id}) class CreateTransferRequestCommand(command.ShowOne): """Create new zone transfer request""" def get_parser(self, prog_name): parser = super().get_parser( prog_name) parser.add_argument('zone_id', help='Zone ID to transfer.',) parser.add_argument( '--target-project-id', help='Target Project ID to transfer to.') parser.add_argument('--description', help='Description') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zone_transfers.create_request( parsed_args.zone_id, parsed_args.target_project_id, parsed_args.description) return zip(*sorted(data.items())) class ListTransferRequestsCommand(command.Lister): """List Zone Transfer Requests""" columns = ['id', 'zone_id', 'zone_name', 'project_id', 'target_project_id', 'status', 'key'] def get_parser(self, prog_name): parser = super().get_parser( prog_name) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zone_transfers.list_requests() cols = self.columns return cols, (utils.get_item_properties(s, cols) for s in data) class ShowTransferRequestCommand(command.ShowOne): """Show Zone Transfer Request Details""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='Zone Tranfer Request ID') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zone_transfers.get_request(parsed_args.id) return zip(*sorted(data.items())) class SetTransferRequestCommand(command.ShowOne): """Set a Zone Transfer Request""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='Zone Transfer Request ID') description_group = parser.add_mutually_exclusive_group() description_group.add_argument('--description', help='Description') description_group.add_argument('--no-description', action='store_true') parser.add_argument( '--target-project-id', help='Target Project ID to transfer to.') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = {} if parsed_args.no_description: data['description'] = None elif parsed_args.description: data['description'] = parsed_args.description if parsed_args.target_project_id: data['target_project_id'] = parsed_args.target_project_id updated = client.zone_transfers.update_request(parsed_args.id, data) return zip(*sorted(updated.items())) class DeleteTransferRequestCommand(command.Command): """Delete a Zone Transfer Request""" def get_parser(self, prog_name): parser = super().get_parser( prog_name) parser.add_argument('id', help='Zone Transfer Request ID') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) client.zone_transfers.delete_request(parsed_args.id) LOG.info('Zone Transfer %s was deleted', parsed_args.id) class AcceptTransferRequestCommand(command.ShowOne): """Accept a Zone Transfer Request""" def get_parser(self, prog_name): parser = super().get_parser( prog_name) parser.add_argument('--transfer-id', help='Transfer ID', type=str, required=True) parser.add_argument('--key', help='Transfer Key', type=str, required=True) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zone_transfers.accept_request( parsed_args.transfer_id, parsed_args.key) return zip(*sorted(data.items())) class ListTransferAcceptsCommand(command.Lister): """List Zone Transfer Accepts""" columns = ['id', 'zone_id', 'project_id', 'zone_transfer_request_id', 'status', 'key'] def get_parser(self, prog_name): parser = super().get_parser( prog_name) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zone_transfers.list_accepts() cols = self.columns return cols, (utils.get_item_properties(s, cols) for s in data) class ShowTransferAcceptCommand(command.ShowOne): """Show Zone Transfer Accept""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('id', help='Zone Tranfer Accept ID') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zone_transfers.get_accept(parsed_args.id) return zip(*sorted(data.items())) class ExportZoneCommand(command.ShowOne): """Export a Zone""" def get_parser(self, prog_name): parser = super().get_parser( prog_name) common.add_all_common_options(parser) parser.add_argument('zone_id', help='Zone ID', type=str) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zone_exports.create(parsed_args.zone_id) _format_zone_export_record(data) LOG.info('Zone Export %s was created', data['id']) return zip(*sorted(data.items())) class ListZoneExportsCommand(command.Lister): """List Zone Exports""" columns = [ 'id', 'zone_id', 'created_at', 'status', ] def get_parser(self, prog_name): parser = super().get_parser( prog_name) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zone_exports.list() cols = self.columns return cols, (utils.get_item_properties(s, cols) for s in data['exports']) class ShowZoneExportCommand(command.ShowOne): """Show a Zone Export""" def get_parser(self, prog_name): parser = super().get_parser( prog_name) parser.add_argument('zone_export_id', help='Zone Export ID', type=str) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zone_exports.get_export_record( parsed_args.zone_export_id) _format_zone_export_record(data) return zip(*sorted(data.items())) class DeleteZoneExportCommand(command.Command): """Delete a Zone Export""" def get_parser(self, prog_name): parser = super().get_parser( prog_name) parser.add_argument('zone_export_id', help='Zone Export ID', type=str) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) client.zone_exports.delete(parsed_args.zone_export_id) LOG.info('Zone Export %s was deleted', parsed_args.zone_export_id) class ShowZoneExportFileCommand(command.ShowOne): """Show the zone file for the Zone Export""" def get_parser(self, prog_name): parser = super().get_parser( prog_name) parser.add_argument('zone_export_id', help='Zone Export ID', type=str) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zone_exports.get_export(parsed_args.zone_export_id) return ['data'], [data] class ImportZoneCommand(command.ShowOne): """Import a Zone from a file on the filesystem""" def get_parser(self, prog_name): parser = super().get_parser( prog_name) parser.add_argument('zone_file_path', help='Path to a zone file', type=str) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) with open(parsed_args.zone_file_path) as f: zone_file_contents = f.read() data = client.zone_imports.create(zone_file_contents) _format_zone_import_record(data) LOG.info('Zone Import %s was created', data['id']) return zip(*sorted(data.items())) class ListZoneImportsCommand(command.Lister): """List Zone Imports""" columns = [ 'id', 'zone_id', 'created_at', 'status', 'message', ] def get_parser(self, prog_name): parser = super().get_parser( prog_name) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zone_imports.list() cols = self.columns return cols, (utils.get_item_properties(s, cols) for s in data['imports']) class ShowZoneImportCommand(command.ShowOne): """Show a Zone Import""" def get_parser(self, prog_name): parser = super().get_parser( prog_name) parser.add_argument('zone_import_id', help='Zone Import ID', type=str) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zone_imports.get_import_record( parsed_args.zone_import_id) _format_zone_import_record(data) return zip(*sorted(data.items())) class DeleteZoneImportCommand(command.Command): """Delete a Zone Import""" def get_parser(self, prog_name): parser = super().get_parser( prog_name) parser.add_argument('zone_import_id', help='Zone Import ID', type=str) common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) client.zone_imports.delete(parsed_args.zone_import_id) LOG.info('Zone Import %s was deleted', parsed_args.zone_import_id) class ShareZoneCommand(command.ShowOne): """Share a Zone""" def get_parser(self, prog_name): parser = super().get_parser( prog_name) common.add_all_common_options(parser) parser.add_argument('zone', help='The zone name or ID to share.') parser.add_argument('target_project_id', help='Target project ID to share the zone with.') return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zone_share.create( parsed_args.zone, parsed_args.target_project_id ) LOG.info('Zone %s was shared', data['id']) data.pop('links', None) return self.dict2columns(data) class ListSharedZonesCommand(command.Lister): """List Zone Shares""" columns = [ 'id', 'zone_id', 'target_project_id', ] def get_parser(self, prog_name): parser = super().get_parser( prog_name) common.add_all_common_options(parser) parser.add_argument('zone', help='The zone name or ID to share.') parser.add_argument('--target-project-id', help='The target project ID to filter on.', required=False) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) criterion = {} if parsed_args.target_project_id is not None: criterion['target_project_id'] = parsed_args.target_project_id data = get_all(client.zone_share.list, criterion=criterion, args=[parsed_args.zone]) cols = list(self.columns) if client.session.all_projects: cols.insert(1, 'project_id') return cols, (utils.get_item_properties(s, cols) for s in data) class ShowSharedZoneCommand(command.ShowOne): """Show Zone Share Details""" def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument('zone', help='The zone name or ID to share.') parser.add_argument('shared_zone_id', help='The zone share ID to show.') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) data = client.zone_share.get(parsed_args.zone, parsed_args.shared_zone_id) data.pop('links', None) return self.dict2columns(data) class DeleteSharedZoneCommand(command.Command): """Delete a Zone Share""" def get_parser(self, prog_name): parser = super().get_parser( prog_name) parser.add_argument('zone', help='The zone name or ID to share.') parser.add_argument('shared_zone_id', help='The zone share ID to delete.') common.add_all_common_options(parser) return parser def take_action(self, parsed_args): client = self.app.client_manager.dns common.set_all_common_headers(client, parsed_args) client.zone_share.delete(parsed_args.zone, parsed_args.shared_zone_id) LOG.info('Shared Zone %s was deleted', parsed_args.shared_zone_id) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/client.py0000664000175000017500000001414600000000000023551 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneauth1 import adapter from designateclient import exceptions from designateclient.v2.blacklists import BlacklistController from designateclient.v2.limits import LimitController from designateclient.v2.nameservers import NameServerController from designateclient.v2.pools import PoolController from designateclient.v2.quotas import QuotasController from designateclient.v2.recordsets import RecordSetController from designateclient.v2.reverse import FloatingIPController from designateclient.v2.service_statuses import ServiceStatusesController from designateclient.v2.tlds import TLDController from designateclient.v2.tsigkeys import TSIGKeysController from designateclient.v2.zones import ZoneController from designateclient.v2.zones import ZoneExportsController from designateclient.v2.zones import ZoneImportsController from designateclient.v2.zones import ZoneShareController from designateclient.v2.zones import ZoneTransfersController from designateclient import version from oslo_utils import importutils osprofiler_web = importutils.try_import("osprofiler.web") class DesignateAdapter(adapter.LegacyJsonAdapter): """Adapter around LegacyJsonAdapter. The user can pass a timeout keyword that will apply only to the Designate Client, in order: - timeout keyword passed to ``request()`` - timeout attribute on keystone session """ def __init__(self, *args, **kwargs): self.timeout = kwargs.pop('timeout', None) self.all_projects = kwargs.pop('all_projects', False) self.edit_managed = kwargs.pop('edit_managed', False) self.hard_delete = kwargs.pop('hard_delete', False) self.sudo_project_id = kwargs.pop('sudo_project_id', None) super(self.__class__, self).__init__(*args, **kwargs) def request(self, *args, **kwargs): kwargs.setdefault('raise_exc', False) if self.timeout is not None: kwargs.setdefault('timeout', self.timeout) kwargs.setdefault('headers', {}) if self.all_projects: kwargs['headers'].setdefault( 'X-Auth-All-Projects', str(self.all_projects) ) if self.edit_managed: kwargs['headers'].setdefault( 'X-Designate-Edit-Managed-Records', str(self.edit_managed) ) if self.hard_delete: kwargs['headers'].setdefault( 'X-Designate-Hard-Delete', str(self.hard_delete) ) if self.sudo_project_id is not None: kwargs['headers'].setdefault( 'X-Auth-Sudo-Project-ID', self.sudo_project_id ) kwargs['headers'].setdefault( 'Content-Type', 'application/json') if osprofiler_web: kwargs['headers'].update(osprofiler_web.get_trace_id_headers()) response, body = super(self.__class__, self).request(*args, **kwargs) # Decode is response, if possible try: response_payload = response.json() except ValueError: response_payload = {} body = response.text if response.status_code == 400: raise exceptions.BadRequest(**response_payload) elif response.status_code in (401, 403): raise exceptions.Forbidden(**response_payload) elif response.status_code == 404: raise exceptions.NotFound(**response_payload) elif response.status_code == 409: raise exceptions.Conflict(**response_payload) elif response.status_code == 413: raise exceptions.OverQuota(**response_payload) elif response.status_code >= 500: raise exceptions.Unknown(**response_payload) return response, body class Client: def __init__(self, region_name=None, endpoint_type='publicURL', extensions=None, service_type='dns', service_name=None, http_log_debug=False, session=None, auth=None, timeout=None, endpoint_override=None, all_projects=False, edit_managed=False, hard_delete=False, sudo_project_id=None): if session is None: raise ValueError("A session instance is required") self.session = DesignateAdapter( session, auth=auth, region_name=region_name, service_type=service_type, interface=endpoint_type.rstrip('URL'), user_agent=f'python-designateclient-{version.version_info}', version=('2'), endpoint_override=endpoint_override, timeout=timeout, all_projects=all_projects, edit_managed=edit_managed, hard_delete=hard_delete, sudo_project_id=sudo_project_id ) self.blacklists = BlacklistController(self) self.floatingips = FloatingIPController(self) self.limits = LimitController(self) self.nameservers = NameServerController(self) self.recordsets = RecordSetController(self) self.service_statuses = ServiceStatusesController(self) self.tlds = TLDController(self) self.zones = ZoneController(self) self.zone_transfers = ZoneTransfersController(self) self.zone_exports = ZoneExportsController(self) self.zone_imports = ZoneImportsController(self) self.zone_share = ZoneShareController(self) self.pools = PoolController(self) self.quotas = QuotasController(self) self.tsigkeys = TSIGKeysController(self) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/limits.py0000664000175000017500000000143600000000000023572 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from designateclient.v2.base import V2Controller class LimitController(V2Controller): def get(self): return self._get('/limits') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/nameservers.py0000664000175000017500000000174400000000000024625 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from designateclient.v2.base import V2Controller from designateclient.v2 import utils as v2_utils class NameServerController(V2Controller): def list(self, zone): zone = v2_utils.resolve_by_name(self.client.zones.list, zone) url = f'/zones/{zone}/nameservers' return self._get(url, response_key='nameservers') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/pools.py0000664000175000017500000000144400000000000023424 0ustar00zuulzuul00000000000000# Copyright (c) 2016 Hewlett-Packard Enterprise 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. from designateclient.v2.base import V2Controller class PoolController(V2Controller): def list(self): url = '/pools' return self._get(url, response_key='pools') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/quotas.py0000664000175000017500000000177500000000000023613 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from designateclient.v2.base import V2Controller class QuotasController(V2Controller): def list(self, project_id): return self._get(f'/quotas/{project_id}') def update(self, project_id, values): return self._patch(f'/quotas/{project_id}', data=values) def reset(self, project_id): return self._delete(f'/quotas/{project_id}') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/recordsets.py0000664000175000017500000000663500000000000024454 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_utils import uuidutils from designateclient.v2.base import V2Controller from designateclient.v2 import utils as v2_utils class RecordSetController(V2Controller): def _canonicalize_record_name(self, zone, name): zone_info = None # If we get a zone name we'll need to get the ID of it before POST. if (isinstance(zone, str) and not uuidutils.is_uuid_like(zone)): zone_info = self.client.zones.get(zone) elif isinstance(zone, dict): zone_info = zone # We where given a name like "www" vs www.i.io., attempt to fix it on # the behalf of the actor. if not name.endswith('.'): if not isinstance(zone_info, dict): zone_info = self.client.zones.get(zone) name = '{}.{}'.format(name, zone_info['name']) return name, zone_info def create(self, zone, name, type_, records, description=None, ttl=None): name, zone_info = self._canonicalize_record_name(zone, name) data = { 'name': name, 'type': type_, 'records': records } if ttl is not None: data['ttl'] = ttl if description is not None: data['description'] = description if zone_info is not None: zone_id = zone_info['id'] else: zone_id = zone return self._post(f'/zones/{zone_id}/recordsets', data=data) def list(self, zone, criterion=None, marker=None, limit=None): zone = v2_utils.resolve_by_name(self.client.zones.list, zone) url = self.build_url( f'/zones/{zone}/recordsets', criterion, marker, limit ) return self._get(url, response_key='recordsets') def list_all_zones(self, criterion=None, marker=None, limit=None): url = self.build_url('/recordsets', criterion, marker, limit) return self._get(url, response_key='recordsets') def get(self, zone, recordset): zone = v2_utils.resolve_by_name(self.client.zones.list, zone) recordset = v2_utils.resolve_by_name(self.list, recordset, zone) url = self.build_url(f'/zones/{zone}/recordsets/{recordset}') return self._get(url) def update(self, zone, recordset, values): zone = v2_utils.resolve_by_name(self.client.zones.list, zone) recordset = v2_utils.resolve_by_name(self.list, recordset, zone) url = f'/zones/{zone}/recordsets/{recordset}' return self._put(url, data=values) def delete(self, zone, recordset): zone = v2_utils.resolve_by_name(self.client.zones.list, zone) recordset = v2_utils.resolve_by_name(self.list, recordset, zone) url = f'/zones/{zone}/recordsets/{recordset}' return self._delete(url) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/reverse.py0000664000175000017500000000304200000000000023737 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from designateclient.v2.base import V2Controller class FloatingIPController(V2Controller): def set(self, floatingip_id, ptrdname, description=None, ttl=None): data = { 'ptrdname': ptrdname } if description is not None: data['description'] = description if ttl is not None: data['ttl'] = ttl url = f'/reverse/floatingips/{floatingip_id}' return self._patch(url, data=data) def list(self, criterion=None): url = self.build_url('/reverse/floatingips', criterion) return self._get(url, response_key='floatingips') def get(self, floatingip_id): url = f'/reverse/floatingips/{floatingip_id}' return self._get(url) def unset(self, floatingip_id): data = {'ptrdname': None} url = f'/reverse/floatingips/{floatingip_id}' return self._patch(url, data=data) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/service_statuses.py0000664000175000017500000000206700000000000025665 0ustar00zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company LP # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from designateclient.v2 import base class ServiceStatusesController(base.V2Controller): def list(self, criterion=None, marker=None, limit=None): url = self.build_url('/service_statuses', criterion, marker, limit) return self._get(url, response_key='service_statuses') def get(self, service_status_id): url = f'/service_statuses/{service_status_id}' return self._get(url) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/tlds.py0000664000175000017500000000307300000000000023236 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from designateclient.v2.base import V2Controller from designateclient.v2 import utils as v2_utils class TLDController(V2Controller): def create(self, name, description=None): data = { 'name': name, } if description is not None: data['description'] = description return self._post('/tlds', data=data) def list(self, criterion=None, marker=None, limit=None): url = self.build_url('/tlds', criterion, marker, limit) return self._get(url, response_key='tlds') def get(self, tld): tld = v2_utils.resolve_by_name(self.list, tld) return self._get(f'/tlds/{tld}') def update(self, tld, values): tld = v2_utils.resolve_by_name(self.list, tld) return self._patch(f'/tlds/{tld}', data=values) def delete(self, tld): tld = v2_utils.resolve_by_name(self.list, tld) return self._delete(f'/tlds/{tld}') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/tsigkeys.py0000664000175000017500000000325500000000000024134 0ustar00zuulzuul00000000000000# Copyright 2017 SAP SE # # Author: Rudolf Vriend # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from designateclient.v2.base import V2Controller from designateclient.v2 import utils as v2_utils class TSIGKeysController(V2Controller): def create(self, name, algorithm, secret, scope, resource_id): data = { 'name': name, 'algorithm': algorithm, 'secret': secret, 'scope': scope, 'resource_id': resource_id } return self._post('/tsigkeys', data=data) def list(self, criterion=None, marker=None, limit=None): url = self.build_url('/tsigkeys', criterion, marker, limit) return self._get(url, response_key='tsigkeys') def get(self, tsigkey): tsigkey = v2_utils.resolve_by_name(self.list, tsigkey) return self._get(f'/tsigkeys/{tsigkey}') def update(self, tsigkey, values): tsigkey = v2_utils.resolve_by_name(self.list, tsigkey) return self._patch(f'/tsigkeys/{tsigkey}', data=values) def delete(self, tsigkey): tsigkey = v2_utils.resolve_by_name(self.list, tsigkey) return self._delete(f'/tsigkeys/{tsigkey}') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/utils.py0000664000175000017500000000443300000000000023431 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_utils import uuidutils from urllib.parse import parse_qs from urllib.parse import urlparse from designateclient import exceptions def resolve_by_name(func, name, *args): """ Helper to resolve a "name" a'la foo.com to it's ID by using REST api's query support and filtering on name. """ if uuidutils.is_uuid_like(name): return name results = func(criterion={"name": f"{name}"}, *args) length = len(results) if length == 1: return results[0]["id"] elif length == 0: raise exceptions.NotFound(f"Name {name} didn't resolve") else: raise exceptions.NoUniqueMatch( "Multiple matches found for {name}, please use ID instead." ) def parse_query_from_url(url): """ Helper to get key bits of data from the "next" url returned from the API on collections :param url: :return: dict """ values = parse_qs(urlparse(url)[4]) return {k: values[k][0] for k in values.keys()} def get_all(function, criterion=None, args=None): """ :param function: Function to be called to get data :param criterion: dict of filters to be applied :param args: arguments to be given to the function :return: DesignateList() """ criterion = criterion or {} args = args or [] data = function(*args, criterion=criterion) returned_data = data while True: if data.next_page: for k, v in data.next_link_criterion.items(): criterion[k] = v data = function(*args, criterion=criterion) returned_data.extend(data) else: break return returned_data ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/v2/zones.py0000664000175000017500000001475400000000000023436 0ustar00zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Author: Endre Karlson # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from designateclient.v2.base import V2Controller from designateclient.v2 import utils as v2_utils class ZoneController(V2Controller): def create(self, name, type_=None, email=None, description=None, ttl=None, masters=None, attributes=None): type_ = type_ or 'PRIMARY' data = { 'name': name, 'type': type_ } if type_ == 'PRIMARY': if email: data['email'] = email if ttl is not None: data['ttl'] = ttl elif type_ == 'SECONDARY' and masters: data['masters'] = masters if description is not None: data['description'] = description if attributes is not None: data['attributes'] = attributes return self._post('/zones', data=data) def list(self, criterion=None, marker=None, limit=None): url = self.build_url('/zones', criterion, marker, limit) return self._get(url, response_key='zones') def get(self, zone): zone = v2_utils.resolve_by_name(self.list, zone) return self._get(f'/zones/{zone}') def update(self, zone, values): zone = v2_utils.resolve_by_name(self.list, zone) url = self.build_url(f'/zones/{zone}') return self._patch(url, data=values) def delete(self, zone, delete_shares=False): zone = v2_utils.resolve_by_name(self.list, zone) url = self.build_url(f'/zones/{zone}') if delete_shares: headers = {'X-Designate-Delete-Shares': 'true'} _resp, body = self.client.session.delete(url, headers=headers) else: _resp, body = self.client.session.delete(url) return body def abandon(self, zone): zone = v2_utils.resolve_by_name(self.list, zone) url = f'/zones/{zone}/tasks/abandon' self.client.session.post(url) def axfr(self, zone): zone = v2_utils.resolve_by_name(self.list, zone) url = f'/zones/{zone}/tasks/xfr' self.client.session.post(url) def pool_move(self, zone, values): zone = v2_utils.resolve_by_name(self.list, zone) url = self.build_url('/zones/%s/tasks/pool_move' % zone) return self._post(url, data=values) class ZoneTransfersController(V2Controller): def create_request(self, zone, target_project_id, description=None): zone = v2_utils.resolve_by_name(self.client.zones.list, zone) data = { 'target_project_id': target_project_id } if description is not None: data['description'] = description url = f'/zones/{zone}/tasks/transfer_requests' return self._post(url, data=data) def get_request(self, transfer_id): url = f'/zones/tasks/transfer_requests/{transfer_id}' return self._get(url) def list_requests(self): url = '/zones/tasks/transfer_requests' return self._get(url, response_key='transfer_requests') def update_request(self, transfer_id, values): url = f'/zones/tasks/transfer_requests/{transfer_id}' return self._patch(url, data=values) def delete_request(self, transfer_id): url = f'/zones/tasks/transfer_requests/{transfer_id}' self._delete(url) def accept_request(self, transfer_id, key): url = '/zones/tasks/transfer_accepts' data = { 'key': key, 'zone_transfer_request_id': transfer_id } return self._post(url, data=data) def get_accept(self, accept_id): url = f'/zones/tasks/transfer_accepts/{accept_id}' return self._get(url) def list_accepts(self): url = '/zones/tasks/transfer_accepts' return self._get(url, response_key='transfer_accepts') class ZoneExportsController(V2Controller): def create(self, zone): zone_id = v2_utils.resolve_by_name(self.client.zones.list, zone) return self._post(f'/zones/{zone_id}/tasks/export') def get_export_record(self, zone_export_id): return self._get(f'/zones/tasks/exports/{zone_export_id}') def list(self): return self._get('/zones/tasks/exports') def delete(self, zone_export_id): return self._delete(f'/zones/tasks/exports/{zone_export_id}') def get_export(self, zone_export_id): return self._get(f'/zones/tasks/exports/{zone_export_id}/export', headers={'Accept': 'text/dns'}) class ZoneImportsController(V2Controller): def create(self, zone_file_contents): return self._post('/zones/tasks/imports', data=zone_file_contents, headers={'Content-Type': 'text/dns'}) def get_import_record(self, zone_import_id): return self._get(f'/zones/tasks/imports/{zone_import_id}') def list(self): return self._get('/zones/tasks/imports') def delete(self, zone_import_id): return self._delete(f'/zones/tasks/imports/{zone_import_id}') class ZoneShareController(V2Controller): def create(self, zone, target_project_id): zone_id = v2_utils.resolve_by_name(self.client.zones.list, zone) data = {'target_project_id': target_project_id} return self._post(f'/zones/{zone_id}/shares', data=data) def list(self, zone, criterion=None, marker=None, limit=None): zone_id = v2_utils.resolve_by_name(self.client.zones.list, zone) url = self.build_url(f'/zones/{zone_id}/shares', criterion, marker, limit) return self._get(url, response_key='shared_zones') def delete(self, zone, shared_zone_id): zone_id = v2_utils.resolve_by_name(self.client.zones.list, zone) return self._delete(f'/zones/{zone_id}/shares/{shared_zone_id}') def get(self, zone, shared_zone_id): zone_id = v2_utils.resolve_by_name(self.client.zones.list, zone) return self._get(f'/zones/{zone_id}/shares/{shared_zone_id}') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/designateclient/version.py0000664000175000017500000000130700000000000023424 0ustar00zuulzuul00000000000000# Copyright 2012 Managed I.T. # # Author: Kiall Mac Innes # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES 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 version_info = pbr.version.VersionInfo('python-designateclient') ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5453389 python-designateclient-6.1.0/doc/0000775000175000017500000000000000000000000016767 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5453389 python-designateclient-6.1.0/doc/examples/0000775000175000017500000000000000000000000020605 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/examples/recordset_create.py0000664000175000017500000000233100000000000024473 0ustar00zuulzuul00000000000000import logging import os from designateclient.v2 import client from designateclient import exceptions from keystoneauth1.identity import generic from keystoneauth1 import session as keystone_session logging.basicConfig(level='DEBUG') """ Example script to create or get a domain and add some records to it. """ auth = generic.Password( auth_url=os.environ.get('OS_AUTH_URL'), username=os.environ.get('OS_USERNAME'), password=os.environ.get('OS_PASSWORD'), project_name=os.environ.get('OS_PROJECT_NAME'), project_domain_id='default', user_domain_id='default') session = keystone_session.Session(auth=auth) client = client.Client(session=session) try: zone = client.zones.create('i.io.', email='i@i.io') except exceptions.RemoteError: zone = {z['name']: z for z in client.zones.list()}['i.io.'] print("Recordset list...") for rs in client.recordsets.list(zone['id']): print(rs) # Here's an example of just passing "www" as the record name vs "www.i.io." records = ["10.0.0.1"] rs = client.recordsets.create(zone['id'], 'www', 'A', records) # Here we're replacing the records with new ones records = ["10.0.0.1", "10.0.0.5"] client.recordsets.update(zone['id'], rs['id'], {'records': records}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/examples/recordset_crud.py0000664000175000017500000000356300000000000024175 0ustar00zuulzuul00000000000000import logging import os from designateclient.v2 import client from designateclient import exceptions from keystoneauth1.identity import generic from keystoneauth1 import session as keystone_session logging.basicConfig(level='DEBUG') auth = generic.Password( auth_url=os.environ.get('OS_AUTH_URL'), username=os.environ.get('OS_USERNAME'), password=os.environ.get('OS_PASSWORD'), project_name=os.environ.get('OS_PROJECT_NAME'), project_domain_id='default', user_domain_id='default') session = keystone_session.Session(auth=auth) client = client.Client(session=session) try: zone = {z['name']: z for z in client.zones.list()}['i.io.'] client.zones.delete(zone['id']) except exceptions.NotFound: pass zone = client.zones.create(name='i.io.', email='i@i.io') # Clean all recordsets first in this zone (for sanity sake) for rrset in client.recordsets.list(zone['id']): if rrset['type'] in ('NS', 'SOA'): continue client.recordsets.delete(zone['id'], rrset['id']) # Make some A records www = client.recordsets.create( zone['id'], 'www.{}'.format(zone['name']), 'A', ['10.0.0.1']) values = { 'records': ['10.0.1.1', '10.0.0.2'] } client.recordsets.update(zone['id'], www['id'], values) cname = client.recordsets.create( zone['id'], 'my-site.{}'.format(zone['name']), 'CNAME', [www['name']]) # Now let's do some Mailserver examples # First create the A record mail1 = client.recordsets.create( zone['id'], 'mail1.' + zone['name'], 'A', ["10.0.0.11"]) mail2 = client.recordsets.create( zone['id'], 'mail2.' + zone['name'], 'A', ["10.0.0.12"]) # Create the MX records - it will be 1 recordset with multiple records pointing # to the A records we created above mx_rrset = client.recordsets.create( zone['id'], zone['name'], 'MX', ['0 ' + mail1['name'], '5 ' + mail2['name']]) print(zone['id']) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/examples/zone_create_primary.py0000664000175000017500000000144000000000000025217 0ustar00zuulzuul00000000000000import logging import os from designateclient import exceptions from designateclient.v2 import client from keystoneauth1.identity import generic from keystoneauth1 import session as keystone_session logging.basicConfig(level='DEBUG') auth = generic.Password( auth_url=os.environ.get('OS_AUTH_URL'), username=os.environ.get('OS_USERNAME'), password=os.environ.get('OS_PASSWORD'), project_name=os.environ.get('OS_PROJECT_NAME'), project_domain_id='default', user_domain_id='default') session = keystone_session.Session(auth=auth) client = client.Client(session=session) try: zone = client.zones.create('i.io.', email='i@i.io') except exceptions.RemoteError: zone = {z['name']: z for z in client.zones.list()}['i.io.'] print(client.recordsets.list(zone['id'])) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/examples/zone_create_secondary.py0000664000175000017500000000176000000000000025530 0ustar00zuulzuul00000000000000import logging import os import uuid from keystoneauth1.identity import generic from keystoneauth1 import session as keystone_session from designateclient.v2 import client logging.basicConfig(level='DEBUG') auth = generic.Password( auth_url=os.environ.get('OS_AUTH_URL'), username=os.environ.get('OS_USERNAME'), password=os.environ.get('OS_PASSWORD'), project_name=os.environ.get('OS_PROJECT_NAME'), project_domain_id='default', user_domain_id='default') session = keystone_session.Session(auth=auth) client = client.Client(session=session) # Primary Zone primary = client.zones.create( f'primary-{str(uuid.uuid4())}.io.', 'PRIMARY', 'root@x.com') # Secondary Zone slave = client.zones.create( f'secondary-{str(uuid.uuid4())}.io.', 'SECONDARY', masters=["127.0.1.1"]) # Try updating Masters for the Secondary new_slave = client.zones.update( slave['id'], {"masters": ["10.0.0.1", "10.0.0.10"]} ) # List all Zones zones = client.zones.list() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/examples/zone_list_nameservers.py0000664000175000017500000000130700000000000025600 0ustar00zuulzuul00000000000000import logging import os import uuid from designateclient.v2 import client from keystoneauth1.identity import generic from keystoneauth1 import session as keystone_session logging.basicConfig(level='DEBUG') auth = generic.Password( auth_url=os.environ.get('OS_AUTH_URL'), username=os.environ.get('OS_USERNAME'), password=os.environ.get('OS_PASSWORD'), project_name=os.environ.get('OS_PROJECT_NAME'), project_domain_id='default', user_domain_id='default') session = keystone_session.Session(auth=auth) client = client.Client(session=session) zone = client.zones.create( f'primary-{str(uuid.uuid4())}.io.', 'PRIMARY', 'root@x.com') client.nameservers.list(zone['id']) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/examples/zone_list_paging.py0000664000175000017500000000154400000000000024516 0ustar00zuulzuul00000000000000import logging import os from keystoneauth1.identity import generic from keystoneauth1 import session as keystone_session from designateclient.v2 import client logging.basicConfig(level='DEBUG') auth = generic.Password( auth_url=os.environ.get('OS_AUTH_URL'), username=os.environ.get('OS_USERNAME'), password=os.environ.get('OS_PASSWORD'), project_name=os.environ.get('OS_PROJECT_NAME'), project_domain_id='default', user_domain_id='default') session = keystone_session.Session(auth=auth, timeout=10) client = client.Client(session=session) pages = [] fetch = 1 while fetch: kw = {'limit': 3} if pages: # marker is the latest page with the last item. kw['marker'] = pages[-1][-1]['id'] page = client.zones.list(**kw) if not page: break pages.append(page) for page in pages: print(page) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/requirements.txt0000664000175000017500000000052000000000000022250 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. sphinx>=2.0.0,!=2.1.0 # BSD reno>=3.1.0 # Apache-2.0 openstackdocstheme>=2.2.1 # Apache-2.0 sphinxcontrib-apidoc>=0.2.0 # BSD ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5453389 python-designateclient-6.1.0/doc/source/0000775000175000017500000000000000000000000020267 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5453389 python-designateclient-6.1.0/doc/source/cli/0000775000175000017500000000000000000000000021036 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/source/cli/index.rst0000664000175000017500000000166200000000000022704 0ustar00zuulzuul00000000000000Designate OpenStack Client Command Reference ============================================ List of released CLI commands available in OpenStack client. These commands can be referenced by doing ``openstack help ``. ======================== Managing the DNS Service ======================== .. autoprogram-cliff:: openstack.dns.v2 :command: dns * =============== Pointer Records =============== .. autoprogram-cliff:: openstack.dns.v2 :command: ptr * =========== Record Sets =========== .. autoprogram-cliff:: openstack.dns.v2 :command: recordset * ================= Top Level Domains ================= .. autoprogram-cliff:: openstack.dns.v2 :command: tld * ========================== Transaction Signature Keys ========================== .. autoprogram-cliff:: openstack.dns.v2 :command: tsigkey * ============== Managing Zones ============== .. autoprogram-cliff:: openstack.dns.v2 :command: zone * ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/source/conf.py0000664000175000017500000000341200000000000021566 0ustar00zuulzuul00000000000000# # designateclient documentation build configuration file # -- 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', 'cliff.sphinxext'] # openstackdocstheme options openstackdocs_repo_name = 'openstack/python-designateclient' openstackdocs_bug_project = 'python-designateclient' openstackdocs_bug_tag = '' html_theme = 'openstackdocs' apidoc_module_dir = '../../designateclient' apidoc_output_dir = 'reference/api' apidoc_excluded_paths = [ 'tests/*', 'functionaltests/*', 'hacking/*' ] apidoc_separate_modules = True autodoc_exclude_modules = [ 'designateclient.tests.*', 'designateclient.functionaltests.*', 'designateclient.hacking.*'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. copyright = '2012, Managed I.T. 2013-2014, Hewlett-Packard Development Company, L.P.' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'native' # A list of ignored prefixes for module index sorting. modindex_common_prefix = ['designateclient'] # -- Options for HTML output --------------------------------------------------- # Output file base name for HTML help builder. htmlhelp_basename = 'designateclientdoc' ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5453389 python-designateclient-6.1.0/doc/source/contributor/0000775000175000017500000000000000000000000022641 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/source/contributor/contributing.rst0000664000175000017500000000147600000000000026112 0ustar00zuulzuul00000000000000Contributing ============ Code is hosted `on GitHub`_. Submit bugs to the Designate Client project on `Launchpad`_. Submit code to the openstack/python-designateclient project using `Gerrit`_. .. _on GitHub: https://github.com/openstack/python-designateclient .. _Launchpad: https://launchpad.net/python-designateclient .. _Gerrit: https://docs.openstack.org/infra/manual/developers.html#development-workflow Here's a quick summary: Install the git-review package to make life easier .. code-block:: shell-session pip install git-review Branch, work, & submit: .. code-block:: shell-session # cut a new branch, tracking master git checkout --track -b bug/id origin/master # work work work git add stuff git commit # rebase/squash to a single commit before submitting git rebase -i # submit git-review ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/source/contributor/functional-tests.rst0000664000175000017500000000314600000000000026701 0ustar00zuulzuul00000000000000================ Functional Tests ================ The functional tests invoke the client executable to see that it actually works with a running Designate. WARNING: these tests will create and delete zones, recordsets, and other resources in Designate. Installation ------------ .. code-block:: shell-session cd python-designateclient pip install python-openstackclient pip install -r requirements.txt -r test-requirements.txt pip install -e . Configuration ------------- The functional tests look for a variable ``TEMPEST_CONFIG`` which specifies a config file for the test. .. code-block:: shell-session export TEMPEST_CONFIG=tempest.conf The tests will use Keystone to grab the Designate endpoint to test against. They need at least three users (two regular users, and one admin) for all the tests to run. .. code-block:: shell-session [identity] uri = http://localhost:5000/v2.0 uri_v3 = http://localhost:5000/v3 auth_version = v2 region = RegionOne username = demo tenant_name = demo password = password domain_name = Default alt_username = alt_demo alt_tenant_name = alt_demo alt_password = password alt_domain_name = Default admin_username = admin admin_tenant_name = admin admin_password = password admin_domain_name = Default [designateclient] # the directory containing the openstack executable directory=/root/python-designateclient/.venv/bin Running the tests ----------------- The functional tests are run with tox (installed with ``pip install tox``): .. code-block:: shell-session tox -e functional ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/source/contributor/index.rst0000664000175000017500000000016100000000000024500 0ustar00zuulzuul00000000000000==================== Contributors Guide ==================== .. toctree:: contributing functional-tests ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/source/index.rst0000664000175000017500000000135300000000000022132 0ustar00zuulzuul00000000000000====================== python-designateclient ====================== python-designateclient provides python bindings and command line tools for the Designate v2 APIs. The :ref:`Python API bindings ` are provided by the :program:`designateclient` module. The designate plugin can be used via the :program:`openstack` command line tool. More information can be found on the :ref:`designate command line tool page `. You'll need credentials for an OpenStack cloud that implements the Designate API in order to use the client. .. toctree:: :maxdepth: 1 install/index user/index cli/index contributor/index reference/index .. rubric:: Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5453389 python-designateclient-6.1.0/doc/source/install/0000775000175000017500000000000000000000000021735 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/source/install/index.rst0000664000175000017500000000272500000000000023604 0ustar00zuulzuul00000000000000============ Installation ============ Install the client from PyPI ---------------------------- The :program:`python-designateclient` package is published on `PyPI`_ and so can be installed using the pip tool, which will manage installing all python dependencies: .. code-block:: shell-session pip install python-designateclient *Warning: the packages on PyPI may lag behind the git repo in functionality.* Setup the client from source ---------------------------- If you want the latest version, straight from github: .. code-block:: shell-session git clone git@github.com:openstack/python-designateclient.git cd python-designateclient virtualenv .venv . .venv/bin/activate pip install -r requirements.txt -r test-requirements.txt python setup.py install Setup the client in development mode ------------------------------------ Installing in development mode allows your to make changes to the source code & test directly without having to re-run the "python setup.py install" step. You can find out more about `Development Mode`_ .. code-block:: shell-session git clone git@github.com:openstack/python-designateclient.git cd python-designateclient virtualenv .venv . .venv/bin/activate pip install -r requirements.txt -r test-requirements.txt python setup.py develop .. _Development Mode: https://setuptools.readthedocs.io/en/latest/setuptools.html#development-mode .. _PyPI: https://pypi.org/project/python-designateclient/ ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5453389 python-designateclient-6.1.0/doc/source/reference/0000775000175000017500000000000000000000000022225 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/source/reference/index.rst0000664000175000017500000000025100000000000024064 0ustar00zuulzuul00000000000000======================================== python-designateclient Package Reference ======================================== .. toctree:: :maxdepth: 4 api/modules ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5493388 python-designateclient-6.1.0/doc/source/user/0000775000175000017500000000000000000000000021245 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/source/user/bindings.rst0000664000175000017500000000212300000000000023572 0ustar00zuulzuul00000000000000.. _bindings: =========================== Python Bindings - v2 =========================== The python-designateclient package comes with python bindings the Designate API: v2. This can be used to interact with the Designate API from any python program. Introduction - Bindings v2 ========================== To view examples of usage please checkout the *doc/examples* folder, basic usage is: .. code-block:: python #!/usr/bin/env python from designateclient.v2 import client from designateclient import shell from keystoneauth1.identity import generic from keystoneauth1 import session as keystone_session auth = generic.Password( auth_url=shell.env('OS_AUTH_URL'), username=shell.env('OS_USERNAME'), password=shell.env('OS_PASSWORD'), project_name=shell.env('OS_PROJECT_NAME'), project_domain_id='default', user_domain_id='default') session = keystone_session.Session(auth=auth) client = client.Client(session=session) zone = client.zones.create('i.io.', email='i@i.io') rs = client.recordsets.create(zone['id'], 'www', 'A', ['10.0.0.1']) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/source/user/index.rst0000664000175000017500000000020300000000000023101 0ustar00zuulzuul00000000000000============================== Using python-designateclient ============================== .. toctree:: bindings shell-v2 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/doc/source/user/shell-v2.rst0000664000175000017500000005571600000000000023451 0ustar00zuulzuul00000000000000.. _shell-v2: ============= OpenStack CLI ============= The python-designateclient package comes with a plugin for the openstack command line tool (installed as :program:`openstack`). This can be used to access a Designate API without having to manipulate JSON by hand, it can also produce the output in a variety of formats (JSON, CSV) and allow you to select columns to be displayed. Installation ------------ Both *python-openstackclient* and *python-designateclient* must be installed: :: $ pip install python-openstackclient python-designateclient Configuration ------------- :program:`openstack` requires certain information to talk to the REST API. An in-depth explanation is covered in the `OpenStack Client configuration documentation`_. To get started, all you usually need are the following variables: :: OS_AUTH_VERSION=3 OS_IDENTITY_API_VERSION=3 OS_AUTH_URL=http://127.0.0.1:5000/v3 OS_PROJECT_NAME=demo OS_USERNAME=demo OS_TENANT_NAME=demo OS_PASSWORD=password Using the Command Line Tool --------------------------- With enough details now in the environment, you can use the :program:`openstack` to create a zone and populate it with some records: .. code-block:: shell-session $ openstack zone create --email admin@example.com example.com. +----------------+--------------------------------------+ | Field | Value | +----------------+--------------------------------------+ | action | CREATE | | created_at | 2016-04-19T17:44:04.000000 | | description | None | | email | admin@example.com | | id | 388814ef-3c5d-415e-a866-5b1d13d78dae | | masters | | | name | example.com. | | pool_id | 794ccc2c-d751-44fe-b57f-8894c9f5c842 | | project_id | 123456 | | serial | 1461087844 | | status | PENDING | | transferred_at | None | | ttl | 3600 | | type | PRIMARY | | updated_at | None | | version | 1 | +----------------+--------------------------------------+ Now that the zone has been created, we can start adding records. You'll note that the zone name (example.com) has a trailing ``.``, as per the DNS standard, and we didn't set a TTL. .. code-block:: shell-session $ openstack recordset create --type A --record 192.0.2.20 example.com. www +-------------+--------------------------------------+ | Field | Value | +-------------+--------------------------------------+ | action | CREATE | | created_at | 2016-04-19T17:51:12.000000 | | description | None | | id | 180d3574-3c29-4ea2-b6ff-df904bd3f126 | | name | www.example.com. | | records | 192.0.2.20 | | status | PENDING | | ttl | None | | type | A | | updated_at | None | | version | 1 | | zone_id | 388814ef-3c5d-415e-a866-5b1d13d78dae | +-------------+--------------------------------------+ Designate-specific Subcommands ------------------------------ Aside from the ``zone create`` and ``recordset create`` subcommands, this is the full list of subcommands that enable Designate V2 support: ============================ ====================================================== =============== subcommand Notes Admin Required ============================ ====================================================== =============== zone create Create new zone zone list List zones zone show Show zone details zone set Set zone properties zone delete Delete zone recordset create Create new recordset recordset list List recordsets recordset list all List all recordsets in all zones recordset show Show recordset details recordset set Set recordset properties recordset delete Delete recordset ptr record list List floatingip ptr records ptr record show Show floatingip ptr record details ptr record set Set floatingip ptr record ptr record unset Unset floatingip ptr record zone export create Export a Zone zone export list List Zone Exports zone export show Show a Zone Export zone export delete Delete a Zone Export zone export showfile Show the zone file for the Zone Export zone import create Import a Zone from a file on the filesystem zone import list List Zone Imports zone import show Show a Zone Import zone import delete Delete a Zone Import zone transfer request create Create new zone transfer request zone transfer request list List Zone Transfer Requests zone transfer request show Show Zone Transfer Request Details zone transfer request set Set a Zone Transfer Request zone transfer request delete Delete a Zone Transfer Request zone transfer accept request Accept a Zone Transfer Request zone transfer accept list List Zone Transfer Accepts zone transfer accept show Show Zone Transfer Accept zone abandon Abandon a zone Yes zone axfr AXFR a zone zone blacklist create Create new blacklist Yes zone blacklist list List blacklists Yes zone blacklist show Show blacklist details Yes zone blacklist set Set blacklist properties Yes zone blacklist delete Delete blacklist Yes tld create Create new tld Yes tld list List tlds Yes tld show Show tld details Yes tld set Set tld properties Yes tld delete Delete tld Yes ============================ ====================================================== =============== Built-in Designate Documentation -------------------------------- You'll find complete documentation on the shell by running: ``openstack --help`` For a specific command, you can execute: ``openstack subcommand help`` Examples -------- Because command output would make this document long, much of it will be omitted from some examples. Working with Zones '''''''''''''''''' Create a zone with the following command: .. code-block:: shell-session $ openstack zone create --email admin@example.com example.com. +----------------+--------------------------------------+ | Field | Value | +----------------+--------------------------------------+ | action | CREATE | | created_at | 2016-04-19T17:44:04.000000 | | description | None | | email | admin@example.com | | id | 388814ef-3c5d-415e-a866-5b1d13d78dae | | masters | | | name | example.com. | | pool_id | 794ccc2c-d751-44fe-b57f-8894c9f5c842 | | project_id | 123456 | | serial | 1461087844 | | status | PENDING | | transferred_at | None | | ttl | 3600 | | type | PRIMARY | | updated_at | None | | version | 1 | +----------------+--------------------------------------+ See the new zone in your list of zones with the following command: .. code-block:: shell-session $ openstack zone list Display a specific zone with either of these commands; most zone commands accept either the zone_id or name attribute: .. code-block:: shell-session $ openstack zone show example.com. $ openstack zone show 388814ef-3c5d-415e-a866-5b1d13d78dae Update the zone with this command: .. code-block:: shell-session $ openstack zone set --description "Description" example.com. Delete the zone with this command: .. code-block:: shell-session $ openstack zone delete example.com. Working with Recordsets ''''''''''''''''''''''' Using the zone above, create a recordset with the following command: .. code-block:: shell-session $ openstack recordset create example.com. --type A www --record 192.0.2.20 +-------------+--------------------------------------+ | Field | Value | +-------------+--------------------------------------+ | action | CREATE | | created_at | 2016-04-19T17:51:12.000000 | | description | None | | id | 180d3574-3c29-4ea2-b6ff-df904bd3f126 | | name | www.example.com. | | records | 192.0.2.20 | | status | PENDING | | ttl | None | | type | A | | updated_at | None | | version | 1 | | zone_id | 388814ef-3c5d-415e-a866-5b1d13d78dae | +-------------+--------------------------------------+ Multiple records can be provided for a specific recordset type: .. code-block:: shell-session $ openstack recordset create example.com. --type A www --record 192.0.2.20 --record 192.0.2.21 See the new recordset in the list of recordsets with the following command: .. code-block:: shell-session $ openstack recordset list example.com. Display a specific recordset: .. code-block:: shell-session $ openstack recordset show example.com. www.example.com. Update a specific recordset: .. code-block:: shell-session $ openstack recordset set example.com. www.example.com. --ttl 10000 --record 192.0.2.20 --record 192.0.2.21 Delete a recordset: .. code-block:: shell-session $ openstack recordset delete example.com. www.example.com. Working with PTR Records '''''''''''''''''''''''' Reverse DNS for Neutron Floating IPs can be managed with the "ptr" subcommand. Create a PTR record: .. code-block:: shell-session $ openstack ptr record set RegionOne:5c02c519-4928-4a38-bd10-c748c200912f ftp.example.com. +-------------+------------------------------------------------+ | Field | Value | +-------------+------------------------------------------------+ | action | CREATE | | address | 172.24.4.11 | | description | None | | id | RegionOne:5c02c519-4928-4a38-bd10-c748c200912f | | ptrdname | ftp.example.com. | | status | PENDING | | ttl | 3600 | +-------------+------------------------------------------------+ List all PTR records: .. code-block:: shell-session $ openstack ptr record list Show a PTR record: .. code-block:: shell-session $ openstack ptr record show RegionOne:5c02c519-4928-4a38-bd10-c748c200912f Delete a PTR record: .. code-block:: shell-session $ openstack ptr record delete RegionOne:5c02c519-4928-4a38-bd10-c748c200912f Working with Zone Exports ''''''''''''''''''''''''' Zone exports enable you to save Designate zone information offline. Create a zone export: .. code-block:: shell-session $ openstack zone export create example.com. +------------+--------------------------------------+ | Field | Value | +------------+--------------------------------------+ | created_at | 2016-04-19T20:42:16.000000 | | id | 6d5acb9d-f3d6-4ed4-96e1-03bc0e405bb5 | | location | None | | message | None | | project_id | 123456 | | status | PENDING | | updated_at | None | | version | 1 | | zone_id | 388814ef-3c5d-415e-a866-5b1d13d78dae | +------------+--------------------------------------+ List zone exports: .. code-block:: shell-session $ openstack zone export list Show zone export: .. code-block:: shell-session $ openstack zone export show 6d5acb9d-f3d6-4ed4-96e1-03bc0e405bb5 Show the zone file for the Zone Export: .. code-block:: shell-session $ openstack zone export showfile 6d5acb9d-f3d6-4ed4-96e1-03bc0e405bb5 -f value $ORIGIN example.com. $TTL 3600 example.com. IN NS ns2.exampleprovider.com. example.com. IN NS ns1.exampleprovider.com. example.com. IN SOA ns.exampleprovider.com. admin@example.com 1458678636 7200 300 604800 300 Delete zone export: .. code-block:: shell-session $ openstack zone export delete 6d5acb9d-f3d6-4ed4-96e1-03bc0e405bb5 Working with Zone Imports ''''''''''''''''''''''''' Zone imports enable you to import a zone into Designate from a file on the filesystem. Create a zone import from a file: .. code-block:: shell-session $ openstack zone import create zonefile.txt +------------+--------------------------------------+ | Field | Value | +------------+--------------------------------------+ | created_at | 2016-04-19T20:59:38.000000 | | id | bab6e152-da9f-4dfc-8a59-3f9710fe4894 | | message | None | | project_id | 123456 | | status | PENDING | | updated_at | None | | version | 1 | | zone_id | None | +------------+--------------------------------------+ List zone imports: .. code-block:: shell-session $ openstack zone import list Show zone import: .. code-block:: shell-session $ openstack zone import show 839d8041-1960-4d74-8533-118d52218074 Delete zone import: .. code-block:: shell-session $ openstack zone import delete 839d8041-1960-4d74-8533-118d52218074 Working with Zone Blacklists '''''''''''''''''''''''''''' Blacklisting zone names enables you to block any zone pattern from creation. Create a zone blacklist .. code-block:: shell-session $ openstack zone blacklist create --pattern "^example\.com\.$" --description "This is a blacklisted domain." +-------------+--------------------------------------+ | Field | Value | +-------------+--------------------------------------+ | created_at | 2016-05-10 00:26:07 | | description | This is a blacklisted domain. | | id | 308ecb82-4952-4476-88b4-9db18fc78e10 | | pattern | ^example.com.$ | | updated_at | None | +-------------+--------------------------------------+ List zone blacklist .. code-block:: shell-session $ openstack zone blacklist list Show zone blacklist .. code-block:: shell-session $ openstack zone blacklist show 308ecb82-4952-4476-88b4-9db18fc78e10 Update zone blacklist .. code-block:: shell-session $ openstack zone blacklist set --pattern "^([A-Za-z0-9_\-]+\.)*example\.com\.$" --description "Updated the description" 308ecb82-4952-4476-88b4-9db18fc78e10 Delete a zone blacklist .. code-block:: shell-session $ openstack zone blacklist delete 308ecb82-4952-4476-88b4-9db18fc78e10 Working with Zone Transfers Between Projects '''''''''''''''''''''''''''''''''''''''''''' Zone Transfers enable you to perform the transfer of zone ownership to another project. Create a Zone Transfer Request .. code-block:: shell-session $ openstack zone transfer request create --target-project-id 9cc52dd7649c4aa99fa9db2fb94dabb8 53cdcf82-9e32-4a00-a90d-32d6ec5db7e9 +-------------------+----------------------------------------------------------------------------------------+ | Field | Value | +-------------------+----------------------------------------------------------------------------------------+ | created_at | 2016-05-10 01:39:00 | | description | None | | id | 98ba1d22-c092-4603-891f-8a0ab04f7e57 | | key | J6JCET2C | | links | {u'self': | | | u'http://192.168.11.182:9001/v2/zones/tasks/transfer_requests/98ba1d22-c092-4603-891f- | | | 8a0ab04f7e57'} | | project_id | 10457ad1fe074f4a89bb1e4c0cd83d40 | | status | ACTIVE | | target_project_id | 9cc52dd7649c4aa99fa9db2fb94dabb8 | | updated_at | None | | zone_id | 53cdcf82-9e32-4a00-a90d-32d6ec5db7e9 | | zone_name | example.com. | +-------------------+----------------------------------------------------------------------------------------+ List Zone Transfer Requests .. code-block:: shell-session $ openstack zone transfer request list Show Zone Transfer Request Details .. code-block:: shell-session $ openstack zone transfer request show 98ba1d22-c092-4603-891f-8a0ab04f7e57 Update a Zone Transfer Request .. code-block:: shell-session $ openstack zone transfer request set 98ba1d22-c092-4603-891f-8a0ab04f7e57 --description "demo transfer" Delete a Zone Transfer Request .. code-block:: shell-session $ openstack zone transfer request delete 98ba1d22-c092-4603-891f-8a0ab04f7e57 Accept a Zone Transfer Request .. code-block:: shell-session $ openstack zone transfer accept request --transfer-id 98ba1d22-c092-4603-891f-8a0ab04f7e57 --key J6JCET2C +--------------------------+---------------------------------------------------------------------------------+ | Field | Value | +--------------------------+---------------------------------------------------------------------------------+ | created_at | 2016-05-10 05:02:52 | | id | a8750f50-d7e6-403a-89d2-e209d62ef60e | | key | J6JCET2C | | links | {u'self': | | | u'http://192.168.11.182:9001/v2/zones/tasks/transfer_accepts/a8750f50-d7e6 | | | -403a-89d2-e209d62ef60e', u'zone': | | | u'http://192.168.11.182:9001/v2/zones/53cdcf82-9e32-4a00-a90d-32d6ec5db7e9'} | | project_id | 10457ad1fe074f4a89bb1e4c0cd83d40 | | status | COMPLETE | | updated_at | 2016-05-10 05:02:52 | | zone_id | 53cdcf82-9e32-4a00-a90d-32d6ec5db7e9 | | zone_transfer_request_id | 98ba1d22-c092-4603-891f-8a0ab04f7e57 | +--------------------------+---------------------------------------------------------------------------------+ Show Zone Transfer Accept .. code-block:: shell-session $ openstack zone transfer accept show a8750f50-d7e6-403a-89d2-e209d62ef60e List Zone Transfer Accept .. code-block:: shell-session $ openstack zone transfer accept list Working with Top Level Domains '''''''''''''''''''''''''''''' The tld commands enable you to manage top level domains. Create a TLD .. code-block:: shell-session $ openstack tld create --name com --description "demo TLD" +-------------+--------------------------------------+ | Field | Value | +-------------+--------------------------------------+ | created_at | 2016-05-10 05:21:40 | | description | demo TLD | | id | a7bba387-712b-4b42-9368-4508642c6113 | | name | com | | updated_at | None | +-------------+--------------------------------------+ List TLDs .. code-block:: shell-session $ openstack tld list Show TLD Details .. code-block:: shell-session $ openstack tld show a7bba387-712b-4b42-9368-4508642c6113 Update a TLD .. code-block:: shell-session $ openstack tld set a7bba387-712b-4b42-9368-4508642c6113 --name org --description "TLD description" Delete a TLD .. code-block:: shell-session $ openstack tld delete a7bba387-712b-4b42-9368-4508642c6113 .. _OpenStack Client configuration documentation: https://docs.openstack.org/python-openstackclient/latest/configuration/index.html ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/python-designateclient.sublime-project0000664000175000017500000000136200000000000025733 0ustar00zuulzuul00000000000000{ "folders": [ { "file_exclude_patterns": [ "*.pyc", "*.pyo", "*.exe", "*.dll", "*.obj", "*.o", "*.a", "*.lib", "*.so", "*.dylib", "*.ncb", "*.sdf", "*.suo", "*.pdb", "*.idb", ".DS_Store", "*.class", "*.psd", "*.db", ".vagrant", ".noseids" ], "folder_exclude_patterns": [ ".svn", ".git", ".hg", "CVS", "*.egg", "*.egg-info", ".tox", "venv" ], "path": "." } ], "settings": { "default_line_ending": "unix", "detect_indentation": false, "ensure_newline_at_eof_on_save": true, "rulers": [ 79 ], "tab_size": 4, "translate_tabs_to_spaces": true, "trim_trailing_white_space_on_save": true } } ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5493388 python-designateclient-6.1.0/python_designateclient.egg-info/0000775000175000017500000000000000000000000024457 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031659.0 python-designateclient-6.1.0/python_designateclient.egg-info/PKG-INFO0000664000175000017500000000212300000000000025552 0ustar00zuulzuul00000000000000Metadata-Version: 1.2 Name: python-designateclient Version: 6.1.0 Summary: OpenStack DNS-as-a-Service - Client Home-page: https://docs.openstack.org/python-designateclient/latest Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: Apache License, Version 2.0 Description: OpenStack DNS-as-a-Service - Client Platform: UNKNOWN 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 :: Implementation :: CPython Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Topic :: Internet :: Name Service (DNS) Requires-Python: >=3.8 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031659.0 python-designateclient-6.1.0/python_designateclient.egg-info/SOURCES.txt0000664000175000017500000001240500000000000026345 0ustar00zuulzuul00000000000000.coveragerc .stestr.conf .zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog LICENSE README.rst python-designateclient.sublime-project requirements.txt setup.cfg setup.py test-requirements.txt tox.ini designateclient/__init__.py designateclient/client.py designateclient/exceptions.py designateclient/utils.py designateclient/version.py designateclient/functionaltests/__init__.py designateclient/functionaltests/base.py designateclient/functionaltests/client.py designateclient/functionaltests/config.py designateclient/functionaltests/datagen.py designateclient/functionaltests/models.py designateclient/functionaltests/v2/__init__.py designateclient/functionaltests/v2/fixtures.py designateclient/functionaltests/v2/test_blacklist.py designateclient/functionaltests/v2/test_recordsets.py designateclient/functionaltests/v2/test_shared_zone.py designateclient/functionaltests/v2/test_tlds.py designateclient/functionaltests/v2/test_tsigkeys.py designateclient/functionaltests/v2/test_zone.py designateclient/functionaltests/v2/test_zone_export.py designateclient/functionaltests/v2/test_zone_import.py designateclient/functionaltests/v2/test_zone_transfer.py designateclient/hacking/__init__.py designateclient/hacking/checks.py designateclient/osc/__init__.py designateclient/osc/plugin.py designateclient/tests/__init__.py designateclient/tests/base.py designateclient/tests/test_designateclient.py designateclient/tests/test_exceptions.py designateclient/tests/test_utils.py designateclient/tests/osc/__init__.py designateclient/tests/osc/resources/__init__.py designateclient/tests/osc/resources/recordset_create.json designateclient/tests/osc/resources/recordset_list.json designateclient/tests/osc/resources/recordset_list_all.json designateclient/tests/osc/resources/zone_create.json designateclient/tests/osc/resources/zone_list.json designateclient/tests/osc/v2/__init__.py designateclient/tests/osc/v2/test_recordsets.py designateclient/tests/osc/v2/test_zone.py designateclient/tests/v2/__init__.py designateclient/tests/v2/test_blacklists.py designateclient/tests/v2/test_client.py designateclient/tests/v2/test_limits.py designateclient/tests/v2/test_nameservers.py designateclient/tests/v2/test_recordsets.py designateclient/tests/v2/test_reverse.py designateclient/tests/v2/test_service_statuses.py designateclient/tests/v2/test_timeout.py designateclient/tests/v2/test_tlds.py designateclient/tests/v2/test_tsigkeys.py designateclient/tests/v2/test_zones.py designateclient/v2/__init__.py designateclient/v2/base.py designateclient/v2/blacklists.py designateclient/v2/client.py designateclient/v2/limits.py designateclient/v2/nameservers.py designateclient/v2/pools.py designateclient/v2/quotas.py designateclient/v2/recordsets.py designateclient/v2/reverse.py designateclient/v2/service_statuses.py designateclient/v2/tlds.py designateclient/v2/tsigkeys.py designateclient/v2/utils.py designateclient/v2/zones.py designateclient/v2/cli/__init__.py designateclient/v2/cli/blacklists.py designateclient/v2/cli/common.py designateclient/v2/cli/quotas.py designateclient/v2/cli/recordsets.py designateclient/v2/cli/reverse.py designateclient/v2/cli/service_statuses.py designateclient/v2/cli/tlds.py designateclient/v2/cli/tsigkeys.py designateclient/v2/cli/zones.py doc/requirements.txt doc/examples/recordset_create.py doc/examples/recordset_crud.py doc/examples/zone_create_primary.py doc/examples/zone_create_secondary.py doc/examples/zone_list_nameservers.py doc/examples/zone_list_paging.py doc/source/conf.py doc/source/index.rst doc/source/cli/index.rst doc/source/contributor/contributing.rst doc/source/contributor/functional-tests.rst doc/source/contributor/index.rst doc/source/install/index.rst doc/source/reference/index.rst doc/source/user/bindings.rst doc/source/user/index.rst doc/source/user/shell-v2.rst python_designateclient.egg-info/PKG-INFO python_designateclient.egg-info/SOURCES.txt python_designateclient.egg-info/dependency_links.txt python_designateclient.egg-info/entry_points.txt python_designateclient.egg-info/not-zip-safe python_designateclient.egg-info/pbr.json python_designateclient.egg-info/requires.txt python_designateclient.egg-info/top_level.txt releasenotes/notes/.placeholder releasenotes/notes/Add-shared-zones-support-4be565f3d1c6356c.yaml releasenotes/notes/add-hard-delete-option-for-zone-delete-e16652c8e72fc023.yaml releasenotes/notes/bug-1940544-9ed7805341dec1ba.yaml releasenotes/notes/drop-py2-c4e50d006fa4446c.yaml releasenotes/notes/drop-python-3-6-and-3-7-7d815c897c330d9c.yaml releasenotes/notes/quota-commands-7ff037bddae95771.yaml releasenotes/notes/remove-api-v1-4e507128b344082b.yaml releasenotes/notes/remove-records-argument-8eda058e3bf028ca.yaml releasenotes/source/2023.1.rst releasenotes/source/2023.2.rst releasenotes/source/2024.1.rst releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/newton.rst releasenotes/source/ocata.rst releasenotes/source/pike.rst releasenotes/source/queens.rst releasenotes/source/rocky.rst releasenotes/source/stein.rst releasenotes/source/train.rst releasenotes/source/unreleased.rst releasenotes/source/ussuri.rst releasenotes/source/victoria.rst releasenotes/source/wallaby.rst releasenotes/source/xena.rst releasenotes/source/yoga.rst releasenotes/source/zed.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031659.0 python-designateclient-6.1.0/python_designateclient.egg-info/dependency_links.txt0000664000175000017500000000000100000000000030525 0ustar00zuulzuul00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031659.0 python-designateclient-6.1.0/python_designateclient.egg-info/entry_points.txt0000664000175000017500000001023200000000000027753 0ustar00zuulzuul00000000000000[designateclient.versions] 2 = designateclient.v2.client:Client [openstack.cli.extension] dns = designateclient.osc.plugin [openstack.dns.v2] dns_quota_list = designateclient.v2.cli.quotas:ListQuotasCommand dns_quota_reset = designateclient.v2.cli.quotas:ResetQuotasCommand dns_quota_set = designateclient.v2.cli.quotas:SetQuotasCommand dns_service_list = designateclient.v2.cli.service_statuses:ListServiceStatusesCommand dns_service_show = designateclient.v2.cli.service_statuses:ShowServiceStatusCommand ptr_record_list = designateclient.v2.cli.reverse:ListFloatingIPCommand ptr_record_set = designateclient.v2.cli.reverse:SetFloatingIPCommand ptr_record_show = designateclient.v2.cli.reverse:ShowFloatingIPCommand ptr_record_unset = designateclient.v2.cli.reverse:UnsetFloatingIPCommand recordset_create = designateclient.v2.cli.recordsets:CreateRecordSetCommand recordset_delete = designateclient.v2.cli.recordsets:DeleteRecordSetCommand recordset_list = designateclient.v2.cli.recordsets:ListRecordSetsCommand recordset_set = designateclient.v2.cli.recordsets:SetRecordSetCommand recordset_show = designateclient.v2.cli.recordsets:ShowRecordSetCommand tld_create = designateclient.v2.cli.tlds:CreateTLDCommand tld_delete = designateclient.v2.cli.tlds:DeleteTLDCommand tld_list = designateclient.v2.cli.tlds:ListTLDsCommand tld_set = designateclient.v2.cli.tlds:SetTLDCommand tld_show = designateclient.v2.cli.tlds:ShowTLDCommand tsigkey_create = designateclient.v2.cli.tsigkeys:CreateTSIGKeyCommand tsigkey_delete = designateclient.v2.cli.tsigkeys:DeleteTSIGKeyCommand tsigkey_list = designateclient.v2.cli.tsigkeys:ListTSIGKeysCommand tsigkey_set = designateclient.v2.cli.tsigkeys:SetTSIGKeyCommand tsigkey_show = designateclient.v2.cli.tsigkeys:ShowTSIGKeyCommand zone_abandon = designateclient.v2.cli.zones:AbandonZoneCommand zone_axfr = designateclient.v2.cli.zones:AXFRZoneCommand zone_blacklist_create = designateclient.v2.cli.blacklists:CreateBlacklistCommand zone_blacklist_delete = designateclient.v2.cli.blacklists:DeleteBlacklistCommand zone_blacklist_list = designateclient.v2.cli.blacklists:ListBlacklistsCommand zone_blacklist_set = designateclient.v2.cli.blacklists:SetBlacklistCommand zone_blacklist_show = designateclient.v2.cli.blacklists:ShowBlacklistCommand zone_create = designateclient.v2.cli.zones:CreateZoneCommand zone_delete = designateclient.v2.cli.zones:DeleteZoneCommand zone_export_create = designateclient.v2.cli.zones:ExportZoneCommand zone_export_delete = designateclient.v2.cli.zones:DeleteZoneExportCommand zone_export_list = designateclient.v2.cli.zones:ListZoneExportsCommand zone_export_show = designateclient.v2.cli.zones:ShowZoneExportCommand zone_export_showfile = designateclient.v2.cli.zones:ShowZoneExportFileCommand zone_import_create = designateclient.v2.cli.zones:ImportZoneCommand zone_import_delete = designateclient.v2.cli.zones:DeleteZoneImportCommand zone_import_list = designateclient.v2.cli.zones:ListZoneImportsCommand zone_import_show = designateclient.v2.cli.zones:ShowZoneImportCommand zone_list = designateclient.v2.cli.zones:ListZonesCommand zone_move = designateclient.v2.cli.zones:PoolMoveZoneCommand zone_set = designateclient.v2.cli.zones:SetZoneCommand zone_share_create = designateclient.v2.cli.zones:ShareZoneCommand zone_share_delete = designateclient.v2.cli.zones:DeleteSharedZoneCommand zone_share_list = designateclient.v2.cli.zones:ListSharedZonesCommand zone_share_show = designateclient.v2.cli.zones:ShowSharedZoneCommand zone_show = designateclient.v2.cli.zones:ShowZoneCommand zone_transfer_accept_list = designateclient.v2.cli.zones:ListTransferAcceptsCommand zone_transfer_accept_request = designateclient.v2.cli.zones:AcceptTransferRequestCommand zone_transfer_accept_show = designateclient.v2.cli.zones:ShowTransferAcceptCommand zone_transfer_request_create = designateclient.v2.cli.zones:CreateTransferRequestCommand zone_transfer_request_delete = designateclient.v2.cli.zones:DeleteTransferRequestCommand zone_transfer_request_list = designateclient.v2.cli.zones:ListTransferRequestsCommand zone_transfer_request_set = designateclient.v2.cli.zones:SetTransferRequestCommand zone_transfer_request_show = designateclient.v2.cli.zones:ShowTransferRequestCommand ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031659.0 python-designateclient-6.1.0/python_designateclient.egg-info/not-zip-safe0000664000175000017500000000000100000000000026705 0ustar00zuulzuul00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031659.0 python-designateclient-6.1.0/python_designateclient.egg-info/pbr.json0000664000175000017500000000005600000000000026136 0ustar00zuulzuul00000000000000{"git_version": "4f98ae3", "is_release": true}././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031659.0 python-designateclient-6.1.0/python_designateclient.egg-info/requires.txt0000664000175000017500000000031500000000000027056 0ustar00zuulzuul00000000000000cliff!=2.9.0,>=2.8.0 debtcollector>=1.2.0 jsonschema>=3.2.0 keystoneauth1>=3.4.0 osc-lib>=1.8.0 oslo.serialization!=2.19.1,>=2.18.0 oslo.utils>=3.33.0 pbr!=2.1.0,>=2.0.0 requests>=2.14.2 stevedore>=1.20.0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031659.0 python-designateclient-6.1.0/python_designateclient.egg-info/top_level.txt0000664000175000017500000000002000000000000027201 0ustar00zuulzuul00000000000000designateclient ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5213392 python-designateclient-6.1.0/releasenotes/0000775000175000017500000000000000000000000020713 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5533388 python-designateclient-6.1.0/releasenotes/notes/0000775000175000017500000000000000000000000022043 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/notes/.placeholder0000664000175000017500000000000000000000000024314 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/notes/Add-shared-zones-support-4be565f3d1c6356c.yaml0000664000175000017500000000044700000000000031756 0ustar00zuulzuul00000000000000--- features: - Adds zone share commands to support sharing zones with additional projects. - Adds a ``--delete-shares`` option to zone delete to delete existing zone shares along with the zone. Without this option, you cannot delete a zone that has been shared with other projects. ././@PaxHeader0000000000000000000000000000021400000000000011452 xustar0000000000000000118 path=python-designateclient-6.1.0/releasenotes/notes/add-hard-delete-option-for-zone-delete-e16652c8e72fc023.yaml 22 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/notes/add-hard-delete-option-for-zone-delete-e16652c8e72fc0000664000175000017500000000023300000000000033103 0ustar00zuulzuul00000000000000--- upgrade: - | Added option``hard-delete`` for zone delete API. This will allow user to delete zone-files on the backend when zone is deleted. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/notes/bug-1940544-9ed7805341dec1ba.yaml0000664000175000017500000000017500000000000026632 0ustar00zuulzuul00000000000000--- fixes: - | Include SECONDARY zone type to 'zone list' command output. Previously only PRIMARY zones were shown././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/notes/drop-py2-c4e50d006fa4446c.yaml0000664000175000017500000000031000000000000026601 0ustar00zuulzuul00000000000000--- upgrade: - | Python 2.7 support has been dropped. The last release of python-designateclient to support Python 2.7 is 3.1. The minimum version of Python now supported is Python 3.6. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/notes/drop-python-3-6-and-3-7-7d815c897c330d9c.yaml0000664000175000017500000000020000000000000030723 0ustar00zuulzuul00000000000000--- upgrade: - | Python 3.6 & 3.7 support has been dropped. The minimum version of Python now supported is Python 3.8.././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/notes/quota-commands-7ff037bddae95771.yaml0000664000175000017500000000040500000000000030165 0ustar00zuulzuul00000000000000--- features: - added openstack dns quota command + list + set + reset sub commands All sub commands can be scoped to a project using --project-id If the --project-id does not match the current project id it will set X-Auth-All-Projects:True ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/notes/remove-api-v1-4e507128b344082b.yaml0000664000175000017500000000020500000000000027302 0ustar00zuulzuul00000000000000--- upgrade: - | Removed V1 API support from the client, as it was permanently removed in the Queens release of Designate. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/notes/remove-records-argument-8eda058e3bf028ca.yaml0000664000175000017500000000031700000000000032056 0ustar00zuulzuul00000000000000--- upgrade: - | The following commands no longer support the ``--records`` argument. Use the ``--record`` argument instead. - ``openstack recordset create`` - ``openstack recordset set`` ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5573387 python-designateclient-6.1.0/releasenotes/source/0000775000175000017500000000000000000000000022213 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/2023.1.rst0000664000175000017500000000020200000000000023464 0ustar00zuulzuul00000000000000=========================== 2023.1 Series Release Notes =========================== .. release-notes:: :branch: stable/2023.1 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/2023.2.rst0000664000175000017500000000020200000000000023465 0ustar00zuulzuul00000000000000=========================== 2023.2 Series Release Notes =========================== .. release-notes:: :branch: stable/2023.2 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/2024.1.rst0000664000175000017500000000020200000000000023465 0ustar00zuulzuul00000000000000=========================== 2024.1 Series Release Notes =========================== .. release-notes:: :branch: stable/2024.1 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5573387 python-designateclient-6.1.0/releasenotes/source/_static/0000775000175000017500000000000000000000000023641 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/_static/.placeholder0000664000175000017500000000000000000000000026112 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5573387 python-designateclient-6.1.0/releasenotes/source/_templates/0000775000175000017500000000000000000000000024350 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/_templates/.placeholder0000664000175000017500000000000000000000000026621 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/conf.py0000664000175000017500000002200300000000000023507 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. # Designate Release Notes documentation build configuration file, created by # sphinx-quickstart on Tue Nov 3 17:40:50 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'openstackdocstheme', 'reno.sphinxext', ] # openstackdocstheme options openstackdocs_repo_name = 'openstack/python-designateclient' openstackdocs_bug_project = 'python-designateclient' openstackdocs_bug_tag = '' openstackdocs_auto_name = False html_theme = 'openstackdocs' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Designate Client Release Notes' copyright = '2015, Designate Developers' # Release notes are version independent. # The full version, including alpha/beta/rc tags. release = '' # The short X.Y version. version = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'native' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'DesignateClientReleaseNotesdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'DesignateClientReleaseNotes.tex', 'Designate Client Release Notes ' 'Documentation', 'Designate Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'designateclientreleasenotes', 'Designate Client Release Notes ' 'Documentation', ['Designate Developers'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'DesignateClientReleaseNotes', 'Designate Client Release Notes ' 'Documentation', 'Designate Developers', 'DesignateClientReleaseNotes', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/index.rst0000664000175000017500000000044500000000000024057 0ustar00zuulzuul00000000000000================================ Designate Client Release Notes ================================ .. toctree:: :maxdepth: 1 unreleased 2024.1 2023.2 2023.1 zed yoga xena wallaby victoria ussuri train stein rocky queens pike ocata newton ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/newton.rst0000664000175000017500000000023200000000000024254 0ustar00zuulzuul00000000000000=================================== Newton Series Release Notes =================================== .. release-notes:: :branch: origin/stable/newton ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/ocata.rst0000664000175000017500000000023000000000000024027 0ustar00zuulzuul00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/pike.rst0000664000175000017500000000021700000000000023675 0ustar00zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/queens.rst0000664000175000017500000000022300000000000024242 0ustar00zuulzuul00000000000000=================================== Queens Series Release Notes =================================== .. release-notes:: :branch: stable/queens ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/rocky.rst0000664000175000017500000000022100000000000024067 0ustar00zuulzuul00000000000000=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/stein.rst0000664000175000017500000000022100000000000024062 0ustar00zuulzuul00000000000000=================================== Stein Series Release Notes =================================== .. release-notes:: :branch: stable/stein ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/train.rst0000664000175000017500000000017600000000000024066 0ustar00zuulzuul00000000000000========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/unreleased.rst0000664000175000017500000000015700000000000025077 0ustar00zuulzuul00000000000000============================== Current Series Release Notes ============================== .. release-notes::././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/ussuri.rst0000664000175000017500000000020200000000000024271 0ustar00zuulzuul00000000000000=========================== Ussuri Series Release Notes =========================== .. release-notes:: :branch: stable/ussuri ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/victoria.rst0000664000175000017500000000022000000000000024557 0ustar00zuulzuul00000000000000============================= Victoria Series Release Notes ============================= .. release-notes:: :branch: unmaintained/victoria ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/wallaby.rst0000664000175000017500000000021400000000000024375 0ustar00zuulzuul00000000000000============================ Wallaby Series Release Notes ============================ .. release-notes:: :branch: unmaintained/wallaby ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/xena.rst0000664000175000017500000000020000000000000023670 0ustar00zuulzuul00000000000000========================= Xena Series Release Notes ========================= .. release-notes:: :branch: unmaintained/xena ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/yoga.rst0000664000175000017500000000020000000000000023674 0ustar00zuulzuul00000000000000========================= Yoga Series Release Notes ========================= .. release-notes:: :branch: unmaintained/yoga ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/releasenotes/source/zed.rst0000664000175000017500000000017400000000000023531 0ustar00zuulzuul00000000000000======================== Zed Series Release Notes ======================== .. release-notes:: :branch: unmaintained/zed ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/requirements.txt0000664000175000017500000000105300000000000021505 0ustar00zuulzuul00000000000000# Requirements lower bounds listed here are our best effort to keep them up to # date but we do not test them so no guarantee of having them all correct. If # you find any incorrect lower bounds, let us know or propose a fix. cliff!=2.9.0,>=2.8.0 # Apache-2.0 jsonschema>=3.2.0 # MIT osc-lib>=1.8.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 pbr!=2.1.0,>=2.0.0 # Apache-2.0 keystoneauth1>=3.4.0 # Apache-2.0 requests>=2.14.2 # Apache-2.0 stevedore>=1.20.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1725031659.5573387 python-designateclient-6.1.0/setup.cfg0000664000175000017500000001233700000000000020051 0ustar00zuulzuul00000000000000[metadata] name = python-designateclient description = OpenStack DNS-as-a-Service - Client long_description = file: README.rst license = Apache License, Version 2.0 author = OpenStack author_email = openstack-discuss@lists.openstack.org url = https://docs.openstack.org/python-designateclient/latest python_requires = >=3.8 classifiers = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Topic :: Internet :: Name Service (DNS) [files] packages = designateclient [entry_points] designateclient.versions = 2 = designateclient.v2.client:Client openstack.cli.extension = dns = designateclient.osc.plugin openstack.dns.v2 = zone_blacklist_create = designateclient.v2.cli.blacklists:CreateBlacklistCommand zone_blacklist_list = designateclient.v2.cli.blacklists:ListBlacklistsCommand zone_blacklist_show = designateclient.v2.cli.blacklists:ShowBlacklistCommand zone_blacklist_set = designateclient.v2.cli.blacklists:SetBlacklistCommand zone_blacklist_delete = designateclient.v2.cli.blacklists:DeleteBlacklistCommand tld_create = designateclient.v2.cli.tlds:CreateTLDCommand tld_list = designateclient.v2.cli.tlds:ListTLDsCommand tld_show = designateclient.v2.cli.tlds:ShowTLDCommand tld_set = designateclient.v2.cli.tlds:SetTLDCommand tld_delete = designateclient.v2.cli.tlds:DeleteTLDCommand zone_create = designateclient.v2.cli.zones:CreateZoneCommand zone_list = designateclient.v2.cli.zones:ListZonesCommand zone_show = designateclient.v2.cli.zones:ShowZoneCommand zone_set = designateclient.v2.cli.zones:SetZoneCommand zone_delete = designateclient.v2.cli.zones:DeleteZoneCommand zone_abandon = designateclient.v2.cli.zones:AbandonZoneCommand zone_axfr = designateclient.v2.cli.zones:AXFRZoneCommand zone_move = designateclient.v2.cli.zones:PoolMoveZoneCommand zone_export_create = designateclient.v2.cli.zones:ExportZoneCommand zone_export_list = designateclient.v2.cli.zones:ListZoneExportsCommand zone_export_show = designateclient.v2.cli.zones:ShowZoneExportCommand zone_export_delete = designateclient.v2.cli.zones:DeleteZoneExportCommand zone_export_showfile = designateclient.v2.cli.zones:ShowZoneExportFileCommand zone_import_create = designateclient.v2.cli.zones:ImportZoneCommand zone_import_list = designateclient.v2.cli.zones:ListZoneImportsCommand zone_import_show = designateclient.v2.cli.zones:ShowZoneImportCommand zone_import_delete = designateclient.v2.cli.zones:DeleteZoneImportCommand zone_transfer_request_create = designateclient.v2.cli.zones:CreateTransferRequestCommand zone_transfer_request_list = designateclient.v2.cli.zones:ListTransferRequestsCommand zone_transfer_request_show = designateclient.v2.cli.zones:ShowTransferRequestCommand zone_transfer_request_set = designateclient.v2.cli.zones:SetTransferRequestCommand zone_transfer_request_delete = designateclient.v2.cli.zones:DeleteTransferRequestCommand zone_transfer_accept_request = designateclient.v2.cli.zones:AcceptTransferRequestCommand zone_transfer_accept_list = designateclient.v2.cli.zones:ListTransferAcceptsCommand zone_transfer_accept_show = designateclient.v2.cli.zones:ShowTransferAcceptCommand zone_share_create = designateclient.v2.cli.zones:ShareZoneCommand zone_share_list = designateclient.v2.cli.zones:ListSharedZonesCommand zone_share_show = designateclient.v2.cli.zones:ShowSharedZoneCommand zone_share_delete = designateclient.v2.cli.zones:DeleteSharedZoneCommand recordset_create = designateclient.v2.cli.recordsets:CreateRecordSetCommand recordset_list = designateclient.v2.cli.recordsets:ListRecordSetsCommand recordset_show = designateclient.v2.cli.recordsets:ShowRecordSetCommand recordset_set = designateclient.v2.cli.recordsets:SetRecordSetCommand recordset_delete = designateclient.v2.cli.recordsets:DeleteRecordSetCommand ptr_record_list = designateclient.v2.cli.reverse:ListFloatingIPCommand ptr_record_show = designateclient.v2.cli.reverse:ShowFloatingIPCommand ptr_record_set = designateclient.v2.cli.reverse:SetFloatingIPCommand ptr_record_unset = designateclient.v2.cli.reverse:UnsetFloatingIPCommand dns_service_list = designateclient.v2.cli.service_statuses:ListServiceStatusesCommand dns_service_show = designateclient.v2.cli.service_statuses:ShowServiceStatusCommand dns_quota_list = designateclient.v2.cli.quotas:ListQuotasCommand dns_quota_set = designateclient.v2.cli.quotas:SetQuotasCommand dns_quota_reset = designateclient.v2.cli.quotas:ResetQuotasCommand tsigkey_create = designateclient.v2.cli.tsigkeys:CreateTSIGKeyCommand tsigkey_list = designateclient.v2.cli.tsigkeys:ListTSIGKeysCommand tsigkey_show = designateclient.v2.cli.tsigkeys:ShowTSIGKeyCommand tsigkey_set = designateclient.v2.cli.tsigkeys:SetTSIGKeyCommand tsigkey_delete = designateclient.v2.cli.tsigkeys:DeleteTSIGKeyCommand [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/setup.py0000664000175000017500000000127100000000000017735 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=1725031631.0 python-designateclient-6.1.0/test-requirements.txt0000664000175000017500000000052100000000000022461 0ustar00zuulzuul00000000000000# Hacking already pins down pep8, pyflakes and flake8 hacking>=6.1.0,<6.2.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 oslo.config>=5.2.0 # Apache-2.0 oslotest>=3.2.0 # Apache-2.0 python-subunit>=1.0.0 # Apache-2.0/BSD requests-mock>=1.2.0 # Apache-2.0 stestr>=2.0.0 # Apache-2.0 reno>=3.1.0 # Apache-2.0 tempest>=25.0.0 # Apache-2.0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1725031631.0 python-designateclient-6.1.0/tox.ini0000664000175000017500000000604000000000000017535 0ustar00zuulzuul00000000000000[tox] envlist = py3,flake8 minversion = 3.1.0 skip_missing_interpreters = true # this allows tox to infer the base python from the environment name # and override any basepython configured in this file ignore_basepython_conflict = true [testenv] usedevelop = True install_command = pip install {opts} {packages} setenv = VIRTUAL_ENV={envdir} LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=C deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt allowlist_externals = find sh rm commands = find . -type f -name "*.pyc" -delete stestr run --slowest {posargs} passenv = http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY [testenv:docs] 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:flake8] commands = flake8 [testenv:pep8] commands = flake8 [testenv:pyflakes] commands = flake8 [testenv:cover] setenv = PYTHON=coverage run --source designateclient --parallel-mode commands = stestr run {posargs} coverage combine coverage html -d cover coverage xml -o cover/coverage.xml [testenv:venv] commands = {posargs} [testenv:functional] usedevelop = False setenv = {[testenv]setenv} OS_TEST_PATH=designateclient/functionaltests/ passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_LOG_CAPTURE OS_DEBUG TEMPEST_CONFIG [testenv:functional-py36] setenv = {[testenv:functional]setenv} [testenv:functional-py37] setenv = {[testenv:functional]setenv} [testenv:functional-py38] setenv = {[testenv:functional]setenv} [testenv:releasenotes] deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/doc/requirements.txt commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [flake8] # ignored flake8 codes: # H302 import only modules # H404 multi line docstring should start with a summary # H405 multi line docstring summary not separated with an empty line # H904 Wrap long lines in parentheses instead of a backslash # W504 line break after binary operator # See designate for other ignored codes that may apply here ignore = H105,H302,H404,H405,W504,H904 builtins = _ exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,tools [flake8:local-plugins] extension = D701 = checks:mutable_default_arguments D703 = checks:check_explicit_underscore_import D704 = checks:no_import_graduated_oslo_libraries D705 = checks:use_timeutils_utcnow D706 = checks:no_translate_debug_logs D707 = checks:check_no_basestring D708 = checks:check_python3_xrange D709 = checks:check_no_log_audit D710 = checks:check_no_log_warn D711 = checks:check_line_continuation_no_backslash paths = ./designateclient/hacking