├── .coveragerc ├── .gitignore ├── .gitreview ├── .stestr.conf ├── .zuul.yaml ├── CONTRIBUTING.rst ├── LICENSE ├── README.rst ├── designateclient ├── __init__.py ├── client.py ├── exceptions.py ├── functionaltests │ ├── __init__.py │ ├── base.py │ ├── client.py │ ├── config.py │ ├── datagen.py │ ├── models.py │ └── v2 │ │ ├── __init__.py │ │ ├── fixtures.py │ │ ├── test_blacklist.py │ │ ├── test_recordsets.py │ │ ├── test_shared_zone.py │ │ ├── test_tlds.py │ │ ├── test_tsigkeys.py │ │ ├── test_zone.py │ │ ├── test_zone_export.py │ │ ├── test_zone_import.py │ │ └── test_zone_transfer.py ├── hacking │ ├── __init__.py │ └── checks.py ├── osc │ ├── __init__.py │ └── plugin.py ├── tests │ ├── __init__.py │ ├── base.py │ ├── osc │ │ ├── __init__.py │ │ ├── resources │ │ │ ├── __init__.py │ │ │ ├── recordset_create.json │ │ │ ├── recordset_list.json │ │ │ ├── recordset_list_all.json │ │ │ ├── zone_create.json │ │ │ └── zone_list.json │ │ └── v2 │ │ │ ├── __init__.py │ │ │ ├── test_recordsets.py │ │ │ └── test_zone.py │ ├── test_designateclient.py │ ├── test_exceptions.py │ ├── test_utils.py │ └── v2 │ │ ├── __init__.py │ │ ├── test_blacklists.py │ │ ├── test_client.py │ │ ├── test_limits.py │ │ ├── test_nameservers.py │ │ ├── test_recordsets.py │ │ ├── test_reverse.py │ │ ├── test_service_statuses.py │ │ ├── test_timeout.py │ │ ├── test_tlds.py │ │ ├── test_tsigkeys.py │ │ └── test_zones.py ├── utils.py ├── v2 │ ├── __init__.py │ ├── base.py │ ├── blacklists.py │ ├── cli │ │ ├── __init__.py │ │ ├── blacklists.py │ │ ├── common.py │ │ ├── quotas.py │ │ ├── recordsets.py │ │ ├── reverse.py │ │ ├── service_statuses.py │ │ ├── tlds.py │ │ ├── tsigkeys.py │ │ └── zones.py │ ├── client.py │ ├── limits.py │ ├── nameservers.py │ ├── pools.py │ ├── quotas.py │ ├── recordsets.py │ ├── reverse.py │ ├── service_statuses.py │ ├── tlds.py │ ├── tsigkeys.py │ ├── utils.py │ └── zones.py └── version.py ├── doc ├── examples │ ├── recordset_create.py │ ├── recordset_crud.py │ ├── zone_create_primary.py │ ├── zone_create_secondary.py │ ├── zone_list_nameservers.py │ └── zone_list_paging.py ├── requirements.txt └── source │ ├── cli │ └── index.rst │ ├── conf.py │ ├── contributor │ ├── contributing.rst │ ├── functional-tests.rst │ └── index.rst │ ├── index.rst │ ├── install │ └── index.rst │ ├── reference │ └── index.rst │ └── user │ ├── bindings.rst │ ├── index.rst │ └── shell-v2.rst ├── python-designateclient.sublime-project ├── releasenotes ├── notes │ ├── .placeholder │ ├── Add-shared-zones-support-4be565f3d1c6356c.yaml │ ├── add-hard-delete-option-for-zone-delete-e16652c8e72fc023.yaml │ ├── bug-1940544-9ed7805341dec1ba.yaml │ ├── drop-py2-c4e50d006fa4446c.yaml │ ├── drop-python-3-6-and-3-7-7d815c897c330d9c.yaml │ ├── quota-commands-7ff037bddae95771.yaml │ ├── remove-api-v1-4e507128b344082b.yaml │ ├── remove-py38-355959306e686af7.yaml │ └── remove-records-argument-8eda058e3bf028ca.yaml └── source │ ├── 2023.1.rst │ ├── 2023.2.rst │ ├── 2024.1.rst │ ├── 2024.2.rst │ ├── 2025.1.rst │ ├── _static │ └── .placeholder │ ├── _templates │ └── .placeholder │ ├── conf.py │ ├── index.rst │ ├── newton.rst │ ├── ocata.rst │ ├── pike.rst │ ├── queens.rst │ ├── rocky.rst │ ├── stein.rst │ ├── train.rst │ ├── unreleased.rst │ ├── ussuri.rst │ ├── victoria.rst │ ├── wallaby.rst │ ├── xena.rst │ ├── yoga.rst │ └── zed.rst ├── requirements.txt ├── setup.cfg ├── setup.py ├── test-requirements.txt └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | source = designateclient 4 | omit = designateclient/tests/*,designateclient/functionaltests/*,designateclient/hacking/* 5 | 6 | [report] 7 | ignore_errors = True -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.dat 3 | TAGS 4 | *.egg 5 | *.egg-info 6 | .eggs 7 | build 8 | .coverage 9 | .tox 10 | cover 11 | venv 12 | .venv 13 | *.sublime-workspace 14 | *.sqlite 15 | var/* 16 | etc/*.conf 17 | etc/*.ini 18 | AUTHORS 19 | ChangeLog 20 | doc/source/reference/api/* 21 | doc/build/* 22 | dist 23 | designateclient/versioninfo 24 | .stestr/ 25 | .testrepository/ 26 | *.log 27 | .idea/ 28 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=review.opendev.org 3 | port=29418 4 | project=openstack/python-designateclient.git 5 | -------------------------------------------------------------------------------- /.stestr.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | test_path=${OS_TEST_PATH:-./designateclient/tests} 3 | top_dir=./ 4 | 5 | -------------------------------------------------------------------------------- /.zuul.yaml: -------------------------------------------------------------------------------- 1 | - project: 2 | templates: 3 | - check-requirements 4 | - designate-devstack-jobs 5 | - openstack-cover-jobs 6 | - openstack-python3-jobs 7 | - openstackclient-plugin-jobs 8 | - publish-openstack-docs-pti 9 | - release-notes-jobs-python3 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | If you would like to contribute to the development of OpenStack, 2 | you must follow the steps documented at: 3 | 4 | https://docs.openstack.org/infra/manual/developers.html 5 | 6 | Once those steps have been completed, changes to OpenStack 7 | should be submitted for review via the Gerrit tool, following 8 | the workflow documented at: 9 | 10 | https://docs.openstack.org/infra/manual/developers.html#development-workflow 11 | 12 | Pull requests submitted through GitHub will be ignored. 13 | 14 | Bugs should be filed on Launchpad, not GitHub: 15 | 16 | https://bugs.launchpad.net/python-designateclient 17 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ===================================== 2 | Python bindings to the Designate API 3 | ===================================== 4 | 5 | .. image:: https://img.shields.io/pypi/v/python-designateclient.svg 6 | :target: https://pypi.org/project/python-designateclient/ 7 | :alt: Latest Version 8 | 9 | This is a client library for Designate built on the Designate API. It 10 | provides a Python API (the ``designateclient`` module) and a command-line tool 11 | (``designate``). 12 | 13 | Development takes place via the usual OpenStack processes as outlined in the 14 | `developer guide `_. The master 15 | repository is in `Git `_. 16 | 17 | See release notes and more at ``_. 18 | 19 | * License: Apache License, Version 2.0 20 | * `PyPi`_ - package installation 21 | * `Online Documentation`_ 22 | * `Bugs`_ - issue tracking 23 | * `Blueprints`_ - feature specifications 24 | * `Source`_ 25 | * `How to Contribute`_ 26 | * `Release Notes`_ 27 | 28 | .. _PyPi: https://pypi.org/project/python-designateclient 29 | .. _Online Documentation: https://docs.openstack.org/python-designateclient/latest/ 30 | .. _Bugs: https://bugs.launchpad.net/python-designateclient 31 | .. _Blueprints: https://blueprints.launchpad.net/python-designateclient 32 | .. _Source: https://git.openstack.org/cgit/openstack/python-designateclient 33 | .. _How to Contribute: https://docs.openstack.org/infra/manual/developers.html 34 | .. _Release Notes: https://docs.openstack.org/releasenotes/python-designateclient 35 | -------------------------------------------------------------------------------- /designateclient/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Huawei, Inc. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | # 15 | 16 | from designateclient import version 17 | 18 | 19 | __version__ = version.version_info.version_string() 20 | -------------------------------------------------------------------------------- /designateclient/client.py: -------------------------------------------------------------------------------- 1 | # Copyright 2012 Managed I.T. 2 | # 3 | # Author: Kiall Mac Innes 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import abc 18 | 19 | from stevedore import extension 20 | from urllib import parse 21 | 22 | from oslo_serialization import jsonutils 23 | 24 | from designateclient import exceptions 25 | 26 | 27 | class Controller(metaclass=abc.ABCMeta): 28 | 29 | def __init__(self, client): 30 | self.client = client 31 | 32 | def build_url(self, url, criterion=None, marker=None, limit=None): 33 | params = criterion or {} 34 | 35 | if marker is not None: 36 | params['marker'] = marker 37 | if limit is not None: 38 | params['limit'] = limit 39 | 40 | q = parse.urlencode(params) if params else '' 41 | return '{url}{params}'.format( 42 | url=url, 43 | params=f'?{q}' 44 | ) 45 | 46 | def _serialize(self, kwargs): 47 | headers = kwargs.get('headers') 48 | content_type = headers.get('Content-Type') if headers else None 49 | 50 | if 'data' in kwargs and content_type in {None, 'application/json'}: 51 | kwargs['data'] = jsonutils.dumps(kwargs['data']) 52 | 53 | def _post(self, url, response_key=None, **kwargs): 54 | self._serialize(kwargs) 55 | 56 | resp, body = self.client.session.post(url, **kwargs) 57 | if response_key is not None: 58 | return body[response_key] 59 | return body 60 | 61 | def _get(self, url, response_key=None): 62 | resp, body = self.client.session.get(url) 63 | if response_key is not None: 64 | return body[response_key] 65 | return body 66 | 67 | def _patch(self, url, response_key=None, **kwargs): 68 | self._serialize(kwargs) 69 | 70 | resp, body = self.client.session.patch(url, **kwargs) 71 | if response_key is not None: 72 | return body[response_key] 73 | return body 74 | 75 | def _put(self, url, response_key=None, **kwargs): 76 | self._serialize(kwargs) 77 | 78 | resp, body = self.client.session.put(url, **kwargs) 79 | if response_key is not None: 80 | return body[response_key] 81 | return body 82 | 83 | def _delete(self, url, response_key=None, **kwargs): 84 | resp, body = self.client.session.delete(url, **kwargs) 85 | if response_key is not None: 86 | return body[response_key] 87 | return body 88 | 89 | 90 | class CrudController(Controller, metaclass=abc.ABCMeta): 91 | 92 | @abc.abstractmethod 93 | def list(self, *args, **kw): 94 | """ 95 | List a resource 96 | """ 97 | 98 | @abc.abstractmethod 99 | def get(self, *args, **kw): 100 | """ 101 | Get a resource 102 | """ 103 | 104 | @abc.abstractmethod 105 | def create(self, *args, **kw): 106 | """ 107 | Create a resource 108 | """ 109 | 110 | @abc.abstractmethod 111 | def update(self, *args, **kw): 112 | """ 113 | Update a resource 114 | """ 115 | 116 | @abc.abstractmethod 117 | def delete(self, *args, **kw): 118 | """ 119 | Delete a resource 120 | """ 121 | 122 | 123 | def get_versions(): 124 | mgr = extension.ExtensionManager('designateclient.versions') 125 | return {ep.name: ep.plugin for ep in mgr.extensions} 126 | 127 | 128 | def Client(version, *args, **kwargs): # noqa 129 | versions = get_versions() 130 | if version not in versions: 131 | msg = 'Version {} is not supported, use one of ({})'.format( 132 | version, list(versions.keys()) 133 | ) 134 | raise exceptions.UnsupportedVersion(msg) 135 | return versions[version](*args, **kwargs) 136 | -------------------------------------------------------------------------------- /designateclient/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2012 Managed I.T. 2 | # 3 | # Author: Kiall Mac Innes 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | 18 | class Base(Exception): 19 | def __init__(self, message=None): 20 | if not message: 21 | message = self.__class__.__name__ 22 | super().__init__(message) 23 | 24 | 25 | class UnsupportedVersion(Base): 26 | pass 27 | 28 | 29 | class ResourceNotFound(Base): 30 | pass 31 | 32 | 33 | class NoUniqueMatch(Base): 34 | pass 35 | 36 | 37 | class RemoteError(Base): 38 | def __init__(self, message=None, code=None, type=None, errors=None, 39 | request_id=None, **ignore): 40 | err_message = self._get_error_message(message, type, errors) 41 | self.message = err_message 42 | self.code = code 43 | self.type = type 44 | self.errors = errors 45 | self.request_id = request_id 46 | 47 | super().__init__(err_message) 48 | 49 | def _get_error_message(self, _message, _type, _errors): 50 | # Try to get a useful error msg if 'message' has nothing 51 | if not _message: 52 | if _errors and 'errors' in _errors: 53 | err_msg = list() 54 | for err in _errors['errors']: 55 | if 'message' in err: 56 | err_msg.append(err['message']) 57 | _message = '. '.join(err_msg) 58 | elif _type: 59 | _message = str(_type) 60 | return _message 61 | 62 | 63 | class Unknown(RemoteError): 64 | pass 65 | 66 | 67 | class BadRequest(RemoteError): 68 | pass 69 | 70 | 71 | class Forbidden(RemoteError): 72 | pass 73 | 74 | 75 | class Conflict(RemoteError): 76 | pass 77 | 78 | 79 | class NotFound(RemoteError): 80 | pass 81 | 82 | 83 | class OverQuota(RemoteError): 84 | pass 85 | -------------------------------------------------------------------------------- /designateclient/functionaltests/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig( 4 | filename='functional-tests.log', 5 | filemode='w', 6 | level=logging.DEBUG, 7 | ) 8 | -------------------------------------------------------------------------------- /designateclient/functionaltests/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2015 Rackspace 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | from tempest.lib.cli import base 17 | from tempest.lib.exceptions import CommandFailed 18 | 19 | from designateclient.functionaltests import client 20 | from designateclient.functionaltests import config 21 | 22 | 23 | class BaseDesignateTest(base.ClientTestBase): 24 | 25 | def _get_clients(self): 26 | config.read_config() 27 | return client.DesignateCLI.as_user('default') 28 | 29 | def ensure_tld_exists(self, tld): 30 | try: 31 | self.clients.as_user('admin').tld_create(tld) 32 | except CommandFailed: 33 | pass 34 | 35 | def _is_entity_in_list(self, entity, entity_list): 36 | """Determines if the given entity exists in the given list. 37 | 38 | Uses the id for comparison. 39 | 40 | Certain entities (e.g. zone import, export) cannot be made 41 | comparable in a list of CLI output results, because the fields 42 | in a list command can be different from those in a show command. 43 | 44 | """ 45 | return any([entity_record.id == entity.id 46 | for entity_record in entity_list]) 47 | -------------------------------------------------------------------------------- /designateclient/functionaltests/config.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2015 Rackspace 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import os 18 | 19 | from oslo_config import cfg 20 | 21 | cfg.CONF.register_group(cfg.OptGroup( 22 | name='identity', title="Configuration for Keystone auth" 23 | )) 24 | 25 | cfg.CONF.register_group(cfg.OptGroup( 26 | name='designateclient', title="Configuration for the Designate client" 27 | )) 28 | 29 | cfg.CONF.register_opts([ 30 | cfg.StrOpt('uri', help="The Keystone v2 endpoint"), 31 | cfg.StrOpt('uri_v3', help="The Keystone v3 endpoint"), 32 | cfg.StrOpt('auth_version', default='v2'), 33 | cfg.StrOpt('region', default='RegionOne'), 34 | 35 | cfg.StrOpt('username'), 36 | cfg.StrOpt('tenant_name'), 37 | cfg.StrOpt('password', secret=True), 38 | cfg.StrOpt('domain_name'), 39 | 40 | cfg.StrOpt('alt_username'), 41 | cfg.StrOpt('alt_tenant_name'), 42 | cfg.StrOpt('alt_password', secret=True), 43 | cfg.StrOpt('alt_domain_name'), 44 | 45 | cfg.StrOpt('admin_username'), 46 | cfg.StrOpt('admin_tenant_name'), 47 | cfg.StrOpt('admin_password', secret=True), 48 | cfg.StrOpt('admin_domain_name'), 49 | 50 | cfg.StrOpt("override_endpoint", 51 | help="use this url instead of the url in the service catalog"), 52 | cfg.StrOpt("override_token", 53 | help="with the override endpoint, pass this token to the api"), 54 | ], group='identity') 55 | 56 | 57 | cfg.CONF.register_opts([ 58 | cfg.StrOpt('directory', 59 | help='the directory containing the client executable'), 60 | ], group='designateclient') 61 | 62 | 63 | def find_config_file(): 64 | return os.environ.get( 65 | 'TEMPEST_CONFIG', '/opt/stack/tempest/etc/tempest.conf') 66 | 67 | 68 | def read_config(): 69 | cfg.CONF(args=[], default_config_files=[find_config_file()]) 70 | -------------------------------------------------------------------------------- /designateclient/functionaltests/datagen.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2015 Rackspace 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | import random 17 | import string 18 | 19 | 20 | def random_digits(n=8): 21 | return ''.join([random.choice(string.digits) for _ in range(n)]) 22 | 23 | 24 | def random_tld(name='testtld'): 25 | return f'{name}{random_digits()}' 26 | 27 | 28 | def random_tsigkey_name(name='testtsig'): 29 | return f'{name}{random_digits()}' 30 | 31 | 32 | def random_tsigkey_secret(name='test-secret'): 33 | return f'{name}-{random_digits(254 - len(name))}' 34 | 35 | 36 | def random_zone_name(name='testdomain', tld='com'): 37 | return f'{name}{random_digits()}.{tld}.' 38 | 39 | 40 | def random_a_recordset_name(zone_name, recordset_name='testrecord'): 41 | return f'{recordset_name}{random_digits()}.{zone_name}' 42 | 43 | 44 | def random_blacklist(name='testblacklist'): 45 | return f'{name}{random_digits()}' 46 | 47 | 48 | def random_zone_file(name='testzoneimport'): 49 | return ( 50 | '$ORIGIN {0}{1}.com.\n' 51 | '$TTL 300\n' 52 | '{0}{1}.com. 300 IN SOA ns.{0}{1}.com. ' 53 | 'nsadmin.{0}{1}.com. 42 42 42 42 42\n' 54 | '{0}{1}.com. 300 IN NS ns.{0}{1}.com.\n' 55 | '{0}{1}.com. 300 IN MX 10 mail.{0}{1}.com.\n' 56 | 'ns.{0}{1}.com. 300 IN A 10.0.0.1\n' 57 | 'mail.{0}{1}.com. 300 IN A 10.0.0.2\n'.format( 58 | name, random_digits() 59 | ) 60 | ) 61 | -------------------------------------------------------------------------------- /designateclient/functionaltests/models.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2015 Rackspace 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | from tempest.lib.cli import output_parser 17 | 18 | 19 | class Model: 20 | 21 | def __str__(self): 22 | return str(self.__dict__) 23 | 24 | 25 | class FieldValueModel(Model): 26 | """This converts cli output from messy lists/dicts to neat attributes.""" 27 | 28 | def __init__(self, out): 29 | """This parses output with fields and values like: 30 | 31 | +----------------+------------------------------+ 32 | | Field | Value | 33 | +----------------+------------------------------+ 34 | | action | CREATE | 35 | | created_at | 2015-08-20T17:22:17.000000 | 36 | | description | None | 37 | +----------------+------------------------------+ 38 | 39 | These are then accessible as: 40 | 41 | model.action 42 | model.created_at 43 | model.description 44 | 45 | """ 46 | table = output_parser.table(out) 47 | 48 | # Because the output_parser handles Values with multiple lines 49 | # in additional Field/Value pairs with Field name '', the following 50 | # code is necessary to aggregate Values. 51 | # 52 | # The list of Field/Value pairs is in-order, so we can append Value 53 | # continuation to the previously seen Field, with a newline separator. 54 | value_lines = [] 55 | prev_field = None 56 | for field, value in table['values']: 57 | if field == '': 58 | value_lines.append(value) 59 | setattr(self, prev_field, '\n'.join(value_lines)) 60 | else: 61 | setattr(self, field, value) 62 | prev_field = field 63 | value_lines = [value] 64 | 65 | 66 | class ListEntryModel(Model): 67 | 68 | def __init__(self, fields, values): 69 | for k, v in zip(fields, values): 70 | setattr(self, k, v) 71 | 72 | 73 | class ListModel(Model, list): 74 | 75 | def __init__(self, out): 76 | """This parses an output table with any number of headers, and any 77 | number of entries: 78 | 79 | +--------------------------------------+----------+---------+ 80 | | id | name | type | 81 | +--------------------------------------+----------+---------+ 82 | | e658a875-1024-4f88-a347-e5b244ec5a10 | aaa.com. | PRIMARY | 83 | +--------------------------------------+----------+---------+ 84 | | 98d1fb5f-2954-448e-988e-6f1df0f24c52 | bbb.com. | PRIMARY | 85 | +--------------------------------------+----------+---------+ 86 | 87 | These are then accessible as: 88 | 89 | model[0].name == 'aaa.com.' 90 | model[1].name == 'bbb.com.' 91 | 92 | """ 93 | table = output_parser.table(out) 94 | for entry in table['values']: 95 | self.append(ListEntryModel(table['headers'], entry)) 96 | -------------------------------------------------------------------------------- /designateclient/functionaltests/v2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/python-designateclient/1505c27dc6a8b5b092b7fff87f7d9cd7d64da154/designateclient/functionaltests/v2/__init__.py -------------------------------------------------------------------------------- /designateclient/functionaltests/v2/test_blacklist.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2015 Rackspace 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | from tempest.lib.exceptions import CommandFailed 17 | 18 | from designateclient.functionaltests.base import BaseDesignateTest 19 | from designateclient.functionaltests.datagen import random_blacklist 20 | from designateclient.functionaltests.v2.fixtures import BlacklistFixture 21 | 22 | 23 | class TestBlacklist(BaseDesignateTest): 24 | 25 | def setUp(self): 26 | super().setUp() 27 | pattern = random_blacklist() 28 | self.blacklist = self.useFixture(BlacklistFixture( 29 | pattern=pattern, 30 | description='A random blacklist', 31 | )).blacklist 32 | 33 | self.assertEqual(self.blacklist.pattern, pattern) 34 | self.assertEqual(self.blacklist.description, 'A random blacklist') 35 | 36 | def test_zone_blacklist_list(self): 37 | blacklists = self.clients.as_user('admin').zone_blacklist_list() 38 | self.assertGreater(len(blacklists), 0) 39 | 40 | def test_zone_blacklist_create_and_show(self): 41 | client = self.clients.as_user('admin') 42 | blacklist = client.zone_blacklist_show(self.blacklist.id) 43 | 44 | self.assertEqual(self.blacklist.created_at, blacklist.created_at) 45 | self.assertEqual(self.blacklist.description, blacklist.description) 46 | self.assertEqual(self.blacklist.id, blacklist.id) 47 | self.assertEqual(self.blacklist.pattern, blacklist.pattern) 48 | self.assertEqual(self.blacklist.updated_at, blacklist.updated_at) 49 | 50 | def test_zone_blacklist_delete(self): 51 | client = self.clients.as_user('admin') 52 | client.zone_blacklist_delete(self.blacklist.id) 53 | self.assertRaises(CommandFailed, client.zone_blacklist_show, 54 | self.blacklist.id) 55 | 56 | def test_zone_blacklist_set(self): 57 | client = self.clients.as_user('admin') 58 | updated_pattern = random_blacklist('updatedblacklist') 59 | blacklist = client.zone_blacklist_set( 60 | id=self.blacklist.id, 61 | pattern=updated_pattern, 62 | description='An updated blacklist', 63 | ) 64 | 65 | self.assertEqual(blacklist.created_at, self.blacklist.created_at) 66 | self.assertEqual(blacklist.description, 'An updated blacklist') 67 | self.assertEqual(blacklist.id, self.blacklist.id) 68 | self.assertEqual(blacklist.pattern, updated_pattern) 69 | self.assertNotEqual(blacklist.updated_at, self.blacklist.updated_at) 70 | 71 | def test_zone_blacklist_set_no_description(self): 72 | client = self.clients.as_user('admin') 73 | blacklist = client.zone_blacklist_set( 74 | id=self.blacklist.id, 75 | no_description=True, 76 | ) 77 | self.assertEqual(blacklist.description, 'None') 78 | 79 | def test_cannot_set_description_with_no_description_flag(self): 80 | client = self.clients.as_user('admin') 81 | self.assertRaises(CommandFailed, client.zone_blacklist_set, 82 | self.blacklist.id, 83 | pattern=random_blacklist(), 84 | description='new description', 85 | no_description=True) 86 | 87 | 88 | class TestBlacklistNegative(BaseDesignateTest): 89 | 90 | def test_invalid_blacklist_command(self): 91 | client = self.clients.as_user('admin') 92 | cmd = 'zone blacklist notacommand' 93 | self.assertRaises(CommandFailed, client.openstack, cmd) 94 | 95 | def test_blacklist_create_invalid_flag(self): 96 | client = self.clients.as_user('admin') 97 | cmd = 'zone blacklist create --pattern helloworld --notaflag invalid' 98 | self.assertRaises(CommandFailed, client.openstack, cmd) 99 | -------------------------------------------------------------------------------- /designateclient/functionaltests/v2/test_recordsets.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2015 Rackspace 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | from tempest.lib.exceptions import CommandFailed 17 | 18 | from designateclient.functionaltests.base import BaseDesignateTest 19 | from designateclient.functionaltests.datagen import random_a_recordset_name 20 | from designateclient.functionaltests.datagen import random_zone_name 21 | from designateclient.functionaltests.v2.fixtures import RecordsetFixture 22 | from designateclient.functionaltests.v2.fixtures import ZoneFixture 23 | 24 | 25 | class TestRecordset(BaseDesignateTest): 26 | 27 | def setUp(self): 28 | super().setUp() 29 | self.ensure_tld_exists('com') 30 | self.zone = self.useFixture(ZoneFixture( 31 | name=random_zone_name(), 32 | email='test@example.com', 33 | )).zone 34 | 35 | name = random_a_recordset_name(self.zone.name) 36 | self.recordset = self.useFixture(RecordsetFixture( 37 | zone_id=self.zone.id, 38 | name=name, 39 | record='1.2.3.4', 40 | description='An a recordset', 41 | type='A', 42 | ttl=1234, 43 | )).recordset 44 | 45 | self.assertEqual(self.recordset.name, name) 46 | self.assertEqual(self.recordset.records, '1.2.3.4') 47 | self.assertEqual(self.recordset.description, 'An a recordset') 48 | self.assertEqual(self.recordset.type, 'A') 49 | self.assertEqual(self.recordset.ttl, '1234') 50 | 51 | def test_recordset_list(self): 52 | rsets = self.clients.recordset_list(self.zone.id) 53 | self.assertGreater(len(rsets), 0) 54 | 55 | def test_recordset_create_and_show(self): 56 | rset = self.clients.recordset_show(self.zone.id, self.recordset.id) 57 | self.assertTrue(hasattr(self.recordset, 'action')) 58 | self.assertTrue(hasattr(rset, 'action')) 59 | self.assertEqual(self.recordset.created_at, rset.created_at) 60 | self.assertEqual(self.recordset.description, rset.description) 61 | self.assertEqual(self.recordset.id, rset.id) 62 | self.assertEqual(self.recordset.name, rset.name) 63 | self.assertEqual(self.recordset.records, rset.records) 64 | self.assertEqual(self.recordset.status, rset.status) 65 | self.assertEqual(self.recordset.ttl, rset.ttl) 66 | self.assertEqual(self.recordset.type, rset.type) 67 | self.assertEqual(self.recordset.updated_at, rset.updated_at) 68 | self.assertEqual(self.recordset.version, rset.version) 69 | self.assertEqual(self.recordset.zone_id, self.zone.id) 70 | 71 | def test_recordset_delete(self): 72 | rset = self.clients.recordset_delete(self.zone.id, self.recordset.id) 73 | self.assertEqual(rset.action, 'DELETE') 74 | self.assertEqual(rset.status, 'PENDING') 75 | 76 | def test_recordset_set(self): 77 | rset = self.clients.recordset_set( 78 | self.zone.id, 79 | self.recordset.id, 80 | record='2.3.4.5', 81 | ttl=2345, 82 | description='Updated description', 83 | ) 84 | 85 | self.assertEqual(rset.records, '2.3.4.5') 86 | self.assertEqual(rset.ttl, '2345') 87 | self.assertEqual(rset.description, 'Updated description') 88 | 89 | def test_recordset_set_clear_ttl_and_description(self): 90 | rset = self.clients.recordset_set( 91 | self.zone.id, 92 | self.recordset.id, 93 | no_description=True, 94 | no_ttl=True, 95 | ) 96 | 97 | self.assertEqual(rset.description, 'None') 98 | self.assertEqual(rset.ttl, 'None') 99 | 100 | 101 | class TestRecordsetNegative(BaseDesignateTest): 102 | 103 | def test_invalid_option_on_recordset_create(self): 104 | cmd = ('recordset create de47d30b-41c5-4e38-b2c5-e0b908e19ec7 ' 105 | 'aaa.desig.com. --type A --record 1.2.3.4 ' 106 | '--invalid "not valid"') 107 | self.assertRaises(CommandFailed, self.clients.openstack, cmd) 108 | 109 | def test_invalid_recordset_command(self): 110 | cmd = 'recordset hopefullynotvalid' 111 | self.assertRaises(CommandFailed, self.clients.openstack, cmd) 112 | -------------------------------------------------------------------------------- /designateclient/functionaltests/v2/test_shared_zone.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Cloudification GmbH. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | from designateclient.functionaltests.base import BaseDesignateTest 17 | from designateclient.functionaltests.client import DesignateCLI 18 | from designateclient.functionaltests.datagen import random_zone_name 19 | from designateclient.functionaltests.v2.fixtures import SharedZoneFixture 20 | from designateclient.functionaltests.v2.fixtures import ZoneFixture 21 | 22 | 23 | class TestSharedZone(BaseDesignateTest): 24 | 25 | def setUp(self): 26 | super().setUp() 27 | self.ensure_tld_exists('com') 28 | fixture = self.useFixture(ZoneFixture( 29 | name=random_zone_name(), 30 | email='test@example.com', 31 | )) 32 | self.zone = fixture.zone 33 | self.target_client = DesignateCLI.as_user('alt') 34 | 35 | def test_list_shared_zones(self): 36 | shared_zone = self.useFixture(SharedZoneFixture( 37 | zone_id=self.zone.id, 38 | target_tenant_id=self.target_client.project_id 39 | )).zone_share 40 | 41 | shared_zones = self.clients.shared_zone_list(self.zone.id) 42 | self.assertGreater(len(shared_zones), 0) 43 | self.assertTrue(self._is_entity_in_list(shared_zone, shared_zones)) 44 | 45 | def test_share_and_show_shared_zone(self): 46 | shared_zone = self.useFixture(SharedZoneFixture( 47 | zone_id=self.zone.id, 48 | target_tenant_id=self.target_client.project_id 49 | )).zone_share 50 | 51 | fetched_shared_zone = self.clients.shared_zone_show(self.zone.id, 52 | shared_zone.id) 53 | 54 | self.assertEqual( 55 | shared_zone.created_at, fetched_shared_zone.created_at) 56 | self.assertEqual(shared_zone.id, fetched_shared_zone.id) 57 | self.assertEqual( 58 | shared_zone.project_id, fetched_shared_zone.project_id) 59 | self.assertEqual(shared_zone.zone_id, fetched_shared_zone.zone_id) 60 | 61 | def test_unshare_zone(self): 62 | shared_zone = self.useFixture(SharedZoneFixture( 63 | zone_id=self.zone.id, 64 | target_tenant_id=self.target_client.project_id 65 | )).zone_share 66 | 67 | shared_zones = self.clients.shared_zone_list(self.zone.id) 68 | self.assertTrue(self._is_entity_in_list(shared_zone, shared_zones)) 69 | 70 | self.clients.unshare_zone(self.zone.id, shared_zone.id) 71 | 72 | shared_zones = self.clients.shared_zone_list(self.zone.id) 73 | self.assertFalse(self._is_entity_in_list(shared_zone, shared_zones)) 74 | -------------------------------------------------------------------------------- /designateclient/functionaltests/v2/test_tlds.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2015 Rackspace 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | from tempest.lib.exceptions import CommandFailed 17 | 18 | from designateclient.functionaltests.base import BaseDesignateTest 19 | from designateclient.functionaltests.datagen import random_tld 20 | from designateclient.functionaltests.v2.fixtures import TLDFixture 21 | 22 | 23 | class TestTld(BaseDesignateTest): 24 | 25 | def setUp(self): 26 | super().setUp() 27 | tld_name = random_tld() 28 | self.tld = self.useFixture(TLDFixture( 29 | name=tld_name, 30 | description='A random tld', 31 | )).tld 32 | 33 | self.assertEqual(self.tld.name, tld_name) 34 | self.assertEqual(self.tld.description, 'A random tld') 35 | 36 | def test_tld_list(self): 37 | tlds = self.clients.as_user('admin').tld_list() 38 | self.assertGreater(len(tlds), 0) 39 | 40 | def test_tld_create_and_show(self): 41 | tld = self.clients.as_user('admin').tld_show(self.tld.id) 42 | self.assertEqual(tld.name, self.tld.name) 43 | self.assertEqual(tld.created_at, self.tld.created_at) 44 | self.assertEqual(tld.id, self.tld.id) 45 | self.assertEqual(tld.name, self.tld.name) 46 | self.assertEqual(tld.updated_at, self.tld.updated_at) 47 | 48 | def test_tld_delete(self): 49 | client = self.clients.as_user('admin') 50 | client.tld_delete(self.tld.id) 51 | self.assertRaises(CommandFailed, client.tld_show, self.tld.id) 52 | 53 | def test_tld_set(self): 54 | client = self.clients.as_user('admin') 55 | updated_name = random_tld('updated') 56 | tld = client.tld_set(self.tld.id, name=updated_name, 57 | description='An updated tld') 58 | self.assertEqual(tld.description, 'An updated tld') 59 | self.assertEqual(tld.name, updated_name) 60 | 61 | def test_tld_set_no_description(self): 62 | client = self.clients.as_user('admin') 63 | tld = client.tld_set(self.tld.id, no_description=True) 64 | self.assertEqual(tld.description, 'None') 65 | 66 | def test_no_set_tld_with_description_and_no_description(self): 67 | client = self.clients.as_user('admin') 68 | self.assertRaises(CommandFailed, client.tld_set, self.tld.id, 69 | description='An updated tld', 70 | no_description=True) 71 | 72 | 73 | class TestTldNegative(BaseDesignateTest): 74 | 75 | def test_tld_invalid_commmand(self): 76 | client = self.clients.as_user('admin') 77 | self.assertRaises(CommandFailed, client.openstack, 'tld notacommand') 78 | 79 | def test_tld_create_invalid_flag(self): 80 | client = self.clients.as_user('admin') 81 | self.assertRaises(CommandFailed, client.openstack, 82 | 'tld create --notanoption "junk"') 83 | -------------------------------------------------------------------------------- /designateclient/functionaltests/v2/test_tsigkeys.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 SAP SE 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | from tempest.lib.exceptions import CommandFailed 17 | 18 | from designateclient.functionaltests.base import BaseDesignateTest 19 | from designateclient.functionaltests.datagen import random_tsigkey_name 20 | from designateclient.functionaltests.datagen import random_tsigkey_secret 21 | from designateclient.functionaltests.datagen import random_zone_name 22 | from designateclient.functionaltests.v2.fixtures import TSIGKeyFixture 23 | from designateclient.functionaltests.v2.fixtures import ZoneFixture 24 | 25 | 26 | class TestTSIGKey(BaseDesignateTest): 27 | def setUp(self): 28 | super().setUp() 29 | self.ensure_tsigkey_exists('com') 30 | self.zone = self.useFixture(ZoneFixture( 31 | name=random_zone_name(), 32 | email='test@example.com', 33 | )).zone 34 | tsig_name = random_tsigkey_name() 35 | tsig_algorithm = "hmac-sha256" 36 | tsig_secret = random_tsigkey_secret() 37 | tsig_scope = 'ZONE' 38 | self.tsig = self.useFixture(TSIGKeyFixture( 39 | name=tsig_name, 40 | algorithm=tsig_algorithm, 41 | secret=tsig_secret, 42 | scope=tsig_scope, 43 | resource_id=self.zone.id 44 | )).tsig 45 | 46 | self.assertEqual(self.tsig.name, tsig_name) 47 | self.assertEqual(self.tsig.algorithm, tsig_algorithm) 48 | self.assertEqual(self.tsig.secret, tsig_secret) 49 | self.assertEqual(self.tsig.scope, tsig_scope) 50 | self.assertEqual(self.tsig.resource_id, self.zone.id) 51 | 52 | def test_tsigkey_list(self): 53 | tsigkeys = self.clients.as_user('admin').tsigkey_list() 54 | self.assertGreater(len(tsigkeys), 0) 55 | 56 | def test_tsigkey_create_and_show(self): 57 | tsigkey = self.clients.as_user('admin').tsigkey_show(self.tsigkey.id) 58 | self.assertEqual(tsigkey.name, self.tsigkey.name) 59 | self.assertEqual(tsigkey.created_at, self.tsigkey.created_at) 60 | self.assertEqual(tsigkey.id, self.tsigkey.id) 61 | self.assertEqual(self.tsig.algorithm, self.tsig_algorithm) 62 | self.assertEqual(self.tsig.secret, self.tsig_secret) 63 | self.assertEqual(self.tsig.scope, self.tsig_scope) 64 | self.assertEqual(self.tsig.resource_id, self.zone.id) 65 | self.assertEqual(tsigkey.updated_at, self.tsigkey.updated_at) 66 | 67 | def test_tsigkey_delete(self): 68 | client = self.clients.as_user('admin') 69 | client.tsigkey_delete(self.tsigkey.id) 70 | self.assertRaises(CommandFailed, client.tsigkey_show, self.tsigkey.id) 71 | 72 | def test_tsigkey_set(self): 73 | client = self.clients.as_user('admin') 74 | updated_name = random_tsigkey_name('updated') 75 | tsigkey = client.tsigkey_set(self.tsigkey.id, name=updated_name, 76 | secret='An updated tsigsecret') 77 | self.assertEqual(tsigkey.secret, 'An updated tsigsecret') 78 | self.assertEqual(tsigkey.name, updated_name) 79 | 80 | 81 | class TestTSIGKeyNegative(BaseDesignateTest): 82 | def test_tsigkey_invalid_commmand(self): 83 | client = self.clients.as_user('admin') 84 | self.assertRaises(CommandFailed, client.openstack, 85 | 'tsigkey notacommand') 86 | 87 | def test_tsigkey_create_invalid_flag(self): 88 | client = self.clients.as_user('admin') 89 | self.assertRaises(CommandFailed, client.openstack, 90 | 'tsigkey create --notanoption "junk"') 91 | -------------------------------------------------------------------------------- /designateclient/functionaltests/v2/test_zone.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2015 Rackspace 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | from tempest.lib.exceptions import CommandFailed 17 | 18 | from designateclient.functionaltests.base import BaseDesignateTest 19 | from designateclient.functionaltests.datagen import random_zone_name 20 | from designateclient.functionaltests.v2.fixtures import ZoneFixture 21 | 22 | 23 | class TestZone(BaseDesignateTest): 24 | 25 | def setUp(self): 26 | super().setUp() 27 | self.ensure_tld_exists('com') 28 | self.fixture = self.useFixture(ZoneFixture( 29 | name=random_zone_name(), 30 | email='test@example.com', 31 | )) 32 | self.zone = self.fixture.zone 33 | 34 | def test_zone_list(self): 35 | zones = self.clients.zone_list() 36 | self.assertGreater(len(zones), 0) 37 | 38 | def test_zone_create_and_show(self): 39 | zone = self.clients.zone_show(self.zone.id) 40 | self.assertTrue(hasattr(zone, 'action')) 41 | self.assertEqual(self.zone.created_at, zone.created_at) 42 | self.assertEqual(self.zone.description, zone.description) 43 | self.assertEqual(self.zone.email, zone.email) 44 | self.assertEqual(self.zone.id, zone.id) 45 | self.assertEqual(self.zone.masters, zone.masters) 46 | self.assertEqual(self.zone.name, zone.name) 47 | self.assertEqual(self.zone.pool_id, zone.pool_id) 48 | self.assertEqual(self.zone.project_id, zone.project_id) 49 | self.assertEqual(self.zone.serial, zone.serial) 50 | self.assertTrue(hasattr(zone, 'status')) 51 | self.assertEqual(self.zone.transferred_at, zone.transferred_at) 52 | self.assertEqual(self.zone.ttl, zone.ttl) 53 | self.assertEqual(self.zone.type, zone.type) 54 | self.assertEqual(self.zone.updated_at, zone.updated_at) 55 | self.assertEqual(self.zone.version, zone.version) 56 | 57 | def test_zone_delete(self): 58 | zone = self.clients.zone_delete(self.zone.id) 59 | self.assertEqual(zone.action, 'DELETE') 60 | self.assertEqual(zone.status, 'PENDING') 61 | 62 | def test_zone_set(self): 63 | ttl = int(self.zone.ttl) + 123 64 | email = f'updated{self.zone.email}' 65 | description = 'new description' 66 | 67 | zone = self.clients.zone_set(self.zone.id, ttl=ttl, email=email, 68 | description=description) 69 | self.assertEqual(ttl, int(zone.ttl)) 70 | self.assertEqual(email, zone.email) 71 | self.assertEqual(description, zone.description) 72 | 73 | def test_invalid_option_on_zone_create(self): 74 | cmd = 'zone create {} --invalid "not a valid option"'.format( 75 | random_zone_name() 76 | ) 77 | self.assertRaises(CommandFailed, self.clients.openstack, cmd) 78 | 79 | def test_invalid_zone_command(self): 80 | cmd = 'zone hopefullynotacommand' 81 | self.assertRaises(CommandFailed, self.clients.openstack, cmd) 82 | 83 | 84 | class TestsPassingZoneFlags(BaseDesignateTest): 85 | 86 | def setUp(self): 87 | super().setUp() 88 | self.ensure_tld_exists('com') 89 | 90 | def test_zone_create_primary_with_all_args(self): 91 | zone_name = random_zone_name() 92 | fixture = self.useFixture(ZoneFixture( 93 | name=zone_name, 94 | email='primary@example.com', 95 | description='A primary zone', 96 | ttl=2345, 97 | type='PRIMARY', 98 | )) 99 | zone = fixture.zone 100 | self.assertEqual(zone_name, zone.name) 101 | self.assertEqual('primary@example.com', zone.email) 102 | self.assertEqual('A primary zone', zone.description) 103 | self.assertEqual('2345', zone.ttl) 104 | self.assertEqual('PRIMARY', zone.type) 105 | 106 | def test_zone_create_secondary_with_all_args(self): 107 | zone_name = random_zone_name() 108 | fixture = self.useFixture(ZoneFixture( 109 | name=zone_name, 110 | description='A secondary zone', 111 | type='SECONDARY', 112 | masters='127.0.0.1', 113 | )) 114 | zone = fixture.zone 115 | self.assertEqual(zone_name, zone.name) 116 | self.assertEqual('A secondary zone', zone.description) 117 | self.assertEqual('SECONDARY', zone.type) 118 | self.assertEqual('127.0.0.1', zone.masters) 119 | 120 | def test_zone_set_secondary_masters(self): 121 | fixture = self.useFixture(ZoneFixture( 122 | name=random_zone_name(), 123 | description='A secondary zone', 124 | type='SECONDARY', 125 | masters='127.0.0.1', 126 | )) 127 | zone = fixture.zone 128 | self.assertEqual('127.0.0.1', zone.masters) 129 | 130 | zone = self.clients.zone_set(zone.id, masters='127.0.0.2') 131 | self.assertEqual('127.0.0.2', zone.masters) 132 | -------------------------------------------------------------------------------- /designateclient/functionaltests/v2/test_zone_export.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2016 Rackspace 3 | 4 | Author: Rahman Syed 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | from designateclient.functionaltests.base import BaseDesignateTest 19 | from designateclient.functionaltests.datagen import random_zone_name 20 | from designateclient.functionaltests.v2.fixtures import ExportFixture 21 | from designateclient.functionaltests.v2.fixtures import ZoneFixture 22 | 23 | 24 | class TestZoneExport(BaseDesignateTest): 25 | 26 | def setUp(self): 27 | super().setUp() 28 | self.ensure_tld_exists('com') 29 | fixture = self.useFixture(ZoneFixture( 30 | name=random_zone_name(), 31 | email='test@example.com', 32 | )) 33 | self.zone = fixture.zone 34 | 35 | def test_list_zone_exports(self): 36 | zone_export = self.useFixture(ExportFixture( 37 | zone=self.zone 38 | )).zone_export 39 | 40 | zone_exports = self.clients.zone_export_list() 41 | self.assertGreater(len(zone_exports), 0) 42 | self.assertTrue(self._is_entity_in_list(zone_export, zone_exports)) 43 | 44 | def test_create_and_show_zone_export(self): 45 | zone_export = self.useFixture(ExportFixture( 46 | zone=self.zone 47 | )).zone_export 48 | 49 | fetched_export = self.clients.zone_export_show(zone_export.id) 50 | 51 | self.assertEqual(zone_export.created_at, fetched_export.created_at) 52 | self.assertEqual(zone_export.id, fetched_export.id) 53 | self.assertEqual(zone_export.message, fetched_export.message) 54 | self.assertEqual(zone_export.project_id, fetched_export.project_id) 55 | self.assertEqual(zone_export.zone_id, fetched_export.zone_id) 56 | 57 | def test_delete_zone_export(self): 58 | zone_export = self.useFixture(ExportFixture( 59 | zone=self.zone 60 | )).zone_export 61 | 62 | zone_exports = self.clients.zone_export_list() 63 | self.assertTrue(self._is_entity_in_list(zone_export, zone_exports)) 64 | 65 | self.clients.zone_export_delete(zone_export.id) 66 | 67 | zone_exports = self.clients.zone_export_list() 68 | self.assertFalse(self._is_entity_in_list(zone_export, zone_exports)) 69 | 70 | def test_show_export_file(self): 71 | zone_export = self.useFixture(ExportFixture( 72 | zone=self.zone 73 | )).zone_export 74 | 75 | fetched_export = self.clients.zone_export_showfile(zone_export.id) 76 | 77 | self.assertIn('$ORIGIN', fetched_export.data) 78 | self.assertIn('$TTL', fetched_export.data) 79 | self.assertIn('SOA', fetched_export.data) 80 | self.assertIn('NS', fetched_export.data) 81 | self.assertIn(self.zone.name, fetched_export.data) 82 | -------------------------------------------------------------------------------- /designateclient/functionaltests/v2/test_zone_import.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2016 Rackspace 3 | 4 | Author: Rahman Syed 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | from designateclient.functionaltests.base import BaseDesignateTest 19 | from designateclient.functionaltests.datagen import random_zone_file 20 | from designateclient.functionaltests.v2.fixtures import ImportFixture 21 | 22 | 23 | class TestZoneImport(BaseDesignateTest): 24 | 25 | def setUp(self): 26 | super().setUp() 27 | self.ensure_tld_exists('com') 28 | self.zone_file_contents = random_zone_file() 29 | 30 | def test_list_zone_imports(self): 31 | zone_import = self.useFixture(ImportFixture( 32 | zone_file_contents=self.zone_file_contents 33 | )).zone_import 34 | 35 | zone_imports = self.clients.zone_import_list() 36 | self.assertGreater(len(zone_imports), 0) 37 | self.assertTrue(self._is_entity_in_list(zone_import, zone_imports)) 38 | 39 | def test_create_and_show_zone_import(self): 40 | zone_import = self.useFixture(ImportFixture( 41 | zone_file_contents=self.zone_file_contents 42 | )).zone_import 43 | 44 | fetched_import = self.clients.zone_import_show(zone_import.id) 45 | 46 | self.assertEqual(zone_import.created_at, fetched_import.created_at) 47 | self.assertEqual(zone_import.id, fetched_import.id) 48 | self.assertEqual(zone_import.project_id, fetched_import.project_id) 49 | 50 | # check both statuses to avoid a race condition, causing test failure. 51 | # we don't know when the import completes. 52 | self.assertIn(fetched_import.status, ['PENDING', 'COMPLETE']) 53 | 54 | def test_delete_zone_import(self): 55 | zone_import = self.useFixture(ImportFixture( 56 | zone_file_contents=self.zone_file_contents 57 | )).zone_import 58 | 59 | zone_imports = self.clients.zone_import_list() 60 | self.assertTrue(self._is_entity_in_list(zone_import, zone_imports)) 61 | 62 | self.clients.zone_import_delete(zone_import.id) 63 | 64 | zone_imports = self.clients.zone_import_list() 65 | self.assertFalse(self._is_entity_in_list(zone_import, zone_imports)) 66 | -------------------------------------------------------------------------------- /designateclient/functionaltests/v2/test_zone_transfer.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2015 Rackspace 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | import unittest 17 | 18 | from tempest.lib.exceptions import CommandFailed 19 | 20 | from designateclient.functionaltests.base import BaseDesignateTest 21 | from designateclient.functionaltests.client import DesignateCLI 22 | from designateclient.functionaltests.datagen import random_zone_name 23 | from designateclient.functionaltests.v2.fixtures import TransferRequestFixture 24 | from designateclient.functionaltests.v2.fixtures import ZoneFixture 25 | 26 | 27 | class TestZoneTransferRequest(BaseDesignateTest): 28 | 29 | def setUp(self): 30 | super().setUp() 31 | self.ensure_tld_exists('com') 32 | fixture = self.useFixture(ZoneFixture( 33 | name=random_zone_name(), 34 | email='test@example.com', 35 | )) 36 | self.zone = fixture.zone 37 | 38 | def test_list_zone_transfer_request(self): 39 | self.useFixture(TransferRequestFixture(self.zone)) 40 | xfrs = self.clients.zone_transfer_request_list() 41 | self.assertGreater(len(xfrs), 0) 42 | 43 | def test_create_and_show_zone_transfer_request(self): 44 | transfer_request = self.useFixture(TransferRequestFixture( 45 | zone=self.zone, 46 | user='default', 47 | target_user='alt', 48 | )).transfer_request 49 | 50 | fetched_xfr = self.clients.zone_transfer_request_show( 51 | transfer_request.id) 52 | 53 | self.assertEqual(fetched_xfr.created_at, transfer_request.created_at) 54 | self.assertEqual(fetched_xfr.description, transfer_request.description) 55 | self.assertEqual(fetched_xfr.id, transfer_request.id) 56 | self.assertEqual(fetched_xfr.key, transfer_request.key) 57 | self.assertEqual(fetched_xfr.links, transfer_request.links) 58 | self.assertEqual(fetched_xfr.target_project_id, 59 | transfer_request.target_project_id) 60 | self.assertEqual(fetched_xfr.updated_at, transfer_request.updated_at) 61 | self.assertEqual(fetched_xfr.status, transfer_request.status) 62 | self.assertEqual(fetched_xfr.zone_id, self.zone.id) 63 | self.assertEqual(fetched_xfr.zone_name, self.zone.name) 64 | 65 | def test_delete_zone_transfer_request(self): 66 | transfer_request = self.useFixture(TransferRequestFixture( 67 | zone=self.zone, 68 | user='default', 69 | target_user='alt', 70 | )).transfer_request 71 | 72 | self.clients.zone_transfer_request_delete(transfer_request.id) 73 | self.assertRaises(CommandFailed, 74 | self.clients.zone_transfer_request_show, 75 | transfer_request.id) 76 | 77 | @unittest.skip("Fails because `zone transfer request set` returns nothing") 78 | def test_set_zone_transfer_request(self): 79 | transfer_request = self.useFixture(TransferRequestFixture( 80 | zone=self.zone, 81 | description="old description", 82 | )).transfer_request 83 | 84 | self.assertEqual(transfer_request.description, "old description") 85 | 86 | updated_xfr = self.clients.zone_transfer_request_set( 87 | transfer_request.id, 88 | description="updated description") 89 | self.assertEqual(updated_xfr.description, "updated description") 90 | 91 | 92 | class TestZoneTransferAccept(BaseDesignateTest): 93 | 94 | def setUp(self): 95 | super().setUp() 96 | self.ensure_tld_exists('com') 97 | fixture = self.useFixture(ZoneFixture( 98 | name=random_zone_name(), 99 | email='test@example.com', 100 | )) 101 | self.zone = fixture.zone 102 | 103 | self.target_client = DesignateCLI.as_user('alt') 104 | fixture = self.useFixture(TransferRequestFixture( 105 | zone=self.zone, 106 | user='default', 107 | target_user='alt', 108 | target_project_id=self.target_client.project_id, 109 | )) 110 | self.transfer_request = fixture.transfer_request 111 | 112 | def test_zone_transfer_accept_list(self): 113 | self.useFixture(TransferRequestFixture(self.zone)) 114 | list_transfer_accepts = self.clients.zone_transfer_accept_list() 115 | self.assertGreater(len(list_transfer_accepts), 0) 116 | 117 | def test_zone_transfer_accept_request(self): 118 | self.target_client.zone_transfer_accept_request( 119 | id=self.transfer_request.id, 120 | key=self.transfer_request.key, 121 | ) 122 | self.target_client.zone_show(self.zone.id) 123 | self.assertRaises(CommandFailed, self.clients.zone_show, self.zone.id) 124 | -------------------------------------------------------------------------------- /designateclient/hacking/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/python-designateclient/1505c27dc6a8b5b092b7fff87f7d9cd7d64da154/designateclient/hacking/__init__.py -------------------------------------------------------------------------------- /designateclient/osc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/python-designateclient/1505c27dc6a8b5b092b7fff87f7d9cd7d64da154/designateclient/osc/__init__.py -------------------------------------------------------------------------------- /designateclient/osc/plugin.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """OpenStackClient plugin for DNS service.""" 18 | 19 | import os 20 | 21 | from osc_lib import utils as oscutils 22 | 23 | 24 | DEFAULT_API_VERSION = '2' 25 | 26 | API_NAME = 'dns' 27 | API_VERSION_OPTION = 'os_dns_api_version' 28 | API_VERSIONS = { 29 | '2': 'designateclient.v2.client.Client', 30 | } 31 | 32 | 33 | def make_client(instance): 34 | cls = oscutils.get_client_class( 35 | API_NAME, instance._api_version[API_NAME], 36 | API_VERSIONS) 37 | kwargs = oscutils.build_kwargs_dict('endpoint_type', instance._interface) 38 | 39 | return cls(session=instance.session, 40 | region_name=instance._region_name, **kwargs) 41 | 42 | 43 | def build_option_parser(parser): 44 | """Hook to add global options.""" 45 | parser.add_argument( 46 | '--os-dns-api-version', 47 | metavar='', 48 | default=os.environ.get('OS_DNS_API_VERSION', '2'), 49 | help=('DNS API version, default=' + 50 | DEFAULT_API_VERSION + 51 | ' (Env: OS_DNS_API_VERSION)')) 52 | 53 | return parser 54 | -------------------------------------------------------------------------------- /designateclient/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/python-designateclient/1505c27dc6a8b5b092b7fff87f7d9cd7d64da154/designateclient/tests/__init__.py -------------------------------------------------------------------------------- /designateclient/tests/base.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2011 OpenStack Foundation 2 | # Copyright (c) 2015 Hewlett-Packard Development Company, L.P. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import os 17 | 18 | import fixtures 19 | from keystoneauth1 import session as keystone_session 20 | from oslo_serialization import jsonutils 21 | from oslotest import base as test 22 | from requests_mock.contrib import fixture as req_fixture 23 | from urllib import parse as urlparse 24 | 25 | from designateclient import client 26 | from designateclient.utils import AdapterWithTimeout 27 | 28 | _TRUE_VALUES = ('True', 'true', '1', 'yes') 29 | 30 | 31 | class TestCase(test.BaseTestCase): 32 | 33 | """Test case base class for all unit tests.""" 34 | 35 | def setUp(self): 36 | """Run before each test method to initialize test environment.""" 37 | 38 | super().setUp() 39 | test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0) 40 | try: 41 | test_timeout = int(test_timeout) 42 | except ValueError: 43 | # If timeout value is invalid do not set a timeout. 44 | test_timeout = 0 45 | if test_timeout > 0: 46 | self.useFixture(fixtures.Timeout(test_timeout, gentle=True)) 47 | 48 | self.useFixture(fixtures.NestedTempfile()) 49 | self.useFixture(fixtures.TempHomeDir()) 50 | 51 | if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES: 52 | stdout = self.useFixture(fixtures.StringStream('stdout')).stream 53 | self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) 54 | if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES: 55 | stderr = self.useFixture(fixtures.StringStream('stderr')).stream 56 | self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) 57 | 58 | self.log_fixture = self.useFixture(fixtures.FakeLogger()) 59 | 60 | 61 | class APITestCase(TestCase): 62 | """Test case base class for all unit tests.""" 63 | 64 | TEST_URL = "http://127.0.0.1:9001/" 65 | VERSION = None 66 | 67 | def setUp(self): 68 | """Run before each test method to initialize test environment.""" 69 | super(TestCase, self).setUp() 70 | self.log_fixture = self.useFixture(fixtures.FakeLogger()) 71 | 72 | self.requests = self.useFixture(req_fixture.Fixture()) 73 | self.client = self.get_client() 74 | 75 | def get_base(self, base_url=None): 76 | if not base_url: 77 | base_url = f'{self.TEST_URL}v{self.VERSION}' 78 | return base_url 79 | 80 | def stub_url(self, method, parts=None, base_url=None, json=None, **kwargs): 81 | base_url = self.get_base(base_url) 82 | 83 | if json: 84 | kwargs['text'] = jsonutils.dumps(json) 85 | headers = kwargs.setdefault('headers', {}) 86 | headers['Content-Type'] = 'application/json' 87 | 88 | if parts: 89 | url = '/'.join([p.strip('/') for p in [base_url] + parts]) 90 | else: 91 | url = base_url 92 | 93 | url = url.replace("/?", "?") 94 | self.requests.register_uri(method, url, **kwargs) 95 | 96 | def get_client(self, version=None, session=None): 97 | version = version or self.VERSION 98 | session = session or keystone_session.Session() 99 | adapted = AdapterWithTimeout( 100 | session=session, endpoint_override=self.get_base()) 101 | return client.Client(version, session=adapted) 102 | 103 | def assertRequestBodyIs(self, body=None, json=None): 104 | last_request_body = self.requests.last_request.body 105 | if json: 106 | val = jsonutils.loads(last_request_body) 107 | self.assertEqual(json, val) 108 | elif body: 109 | self.assertEqual(body, last_request_body) 110 | 111 | def assertQueryStringIs(self, qs=''): 112 | """Verify the QueryString matches what is expected. 113 | 114 | The qs parameter should be of the format \'foo=bar&abc=xyz\' 115 | """ 116 | expected = urlparse.parse_qs(qs, keep_blank_values=True) 117 | parts = urlparse.urlparse(self.requests.last_request.url) 118 | querystring = urlparse.parse_qs(parts.query, keep_blank_values=True) 119 | self.assertEqual(expected, querystring) 120 | 121 | def assertQueryStringContains(self, **kwargs): 122 | """Verify the query string contains the expected parameters. 123 | 124 | This method is used to verify that the query string for the most recent 125 | request made contains all the parameters provided as ``kwargs``, and 126 | that the value of each parameter contains the value for the kwarg. If 127 | the value for the kwarg is an empty string (''), then all that's 128 | verified is that the parameter is present. 129 | 130 | """ 131 | parts = urlparse.urlparse(self.requests.last_request.url) 132 | qs = urlparse.parse_qs(parts.query, keep_blank_values=True) 133 | 134 | for k, v in kwargs.items(): 135 | self.assertIn(k, qs) 136 | self.assertIn(v, qs[k]) 137 | 138 | def assertRequestHeaderEqual(self, name, val): 139 | """Verify that the last request made contains a header and its value 140 | 141 | The request must have already been made. 142 | """ 143 | headers = self.requests.last_request.headers 144 | self.assertEqual(val, headers.get(name)) 145 | -------------------------------------------------------------------------------- /designateclient/tests/osc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/python-designateclient/1505c27dc6a8b5b092b7fff87f7d9cd7d64da154/designateclient/tests/osc/__init__.py -------------------------------------------------------------------------------- /designateclient/tests/osc/resources/__init__.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | import json 14 | import os.path 15 | 16 | path = os.path.dirname(os.path.realpath(__file__)) 17 | 18 | 19 | def load(action): 20 | with open(os.path.join(path, f'{action}.json')) as fp: 21 | return json.load(fp) 22 | -------------------------------------------------------------------------------- /designateclient/tests/osc/resources/recordset_create.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "a00ac483-d74d-4555-b30f-2e0a176287e2", 3 | "zone_id": "6f106adb-0896-4114-b34f-4ac8dfee9465", 4 | "project_id": "15a6ae43e4dd498383e127e5f2521301", 5 | "name": "example.internal.example.org.", 6 | "zone_name": "internal.example.org.", 7 | "type": "A", 8 | "records": [ 9 | "127.0.0.1", 10 | "127.0.0.2" 11 | ], 12 | "description": null, 13 | "ttl": null, 14 | "status": "PENDING", 15 | "action": "CREATE", 16 | "version": 1, 17 | "created_at": "2021-12-05T23:47:57.000000", 18 | "updated_at": null, 19 | "links": { 20 | "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets/a00ac483-d74d-4555-b30f-2e0a176287e2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /designateclient/tests/osc/resources/recordset_list.json: -------------------------------------------------------------------------------- 1 | { 2 | "recordsets": [ 3 | { 4 | "id": "393d65fe-c0f1-4a55-a91e-bf6c1725dddc", 5 | "zone_id": "6f106adb-0896-4114-b34f-4ac8dfee9465", 6 | "project_id": "15a6ae43e4dd498383e127e5f2521301", 7 | "name": "internal.example.org.", 8 | "zone_name": "internal.example.org.", 9 | "type": "NS", 10 | "records": [ 11 | "ns1.example.org." 12 | ], 13 | "description": null, 14 | "ttl": null, 15 | "status": "ACTIVE", 16 | "action": "NONE", 17 | "version": 1, 18 | "created_at": "2021-12-05T23:45:54.000000", 19 | "updated_at": null, 20 | "links": { 21 | "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets/393d65fe-c0f1-4a55-a91e-bf6c1725dddc" 22 | } 23 | }, 24 | { 25 | "id": "4055914e-0f2b-4c47-a8d3-287a5286fe92", 26 | "zone_id": "6f106adb-0896-4114-b34f-4ac8dfee9465", 27 | "project_id": "15a6ae43e4dd498383e127e5f2521301", 28 | "name": "internal.example.org.", 29 | "zone_name": "internal.example.org.", 30 | "type": "SOA", 31 | "records": [ 32 | "ns1.example.org. fake.example.org. 1638748076 3584 600 86400 3600" 33 | ], 34 | "description": null, 35 | "ttl": null, 36 | "status": "ACTIVE", 37 | "action": "NONE", 38 | "version": 4, 39 | "created_at": "2021-12-05T23:45:54.000000", 40 | "updated_at": "2021-12-05T23:47:57.000000", 41 | "links": { 42 | "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets/4055914e-0f2b-4c47-a8d3-287a5286fe92" 43 | } 44 | }, 45 | { 46 | "id": "a00ac483-d74d-4555-b30f-2e0a176287e2", 47 | "zone_id": "6f106adb-0896-4114-b34f-4ac8dfee9465", 48 | "project_id": "15a6ae43e4dd498383e127e5f2521301", 49 | "name": "example.internal.example.org.", 50 | "zone_name": "internal.example.org.", 51 | "type": "A", 52 | "records": [ 53 | "127.0.0.2", 54 | "127.0.0.1" 55 | ], 56 | "description": null, 57 | "ttl": null, 58 | "status": "ACTIVE", 59 | "action": "NONE", 60 | "version": 1, 61 | "created_at": "2021-12-05T23:47:57.000000", 62 | "updated_at": null, 63 | "links": { 64 | "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets/a00ac483-d74d-4555-b30f-2e0a176287e2" 65 | } 66 | } 67 | ], 68 | "links": { 69 | "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets" 70 | }, 71 | "metadata": { 72 | "total_count": 3 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /designateclient/tests/osc/resources/recordset_list_all.json: -------------------------------------------------------------------------------- 1 | { 2 | "recordsets": [ 3 | { 4 | "id": "393d65fe-c0f1-4a55-a91e-bf6c1725dddc", 5 | "zone_id": "6f106adb-0896-4114-b34f-4ac8dfee9465", 6 | "project_id": "15a6ae43e4dd498383e127e5f2521301", 7 | "name": "internal.example.org.", 8 | "zone_name": "internal.example.org.", 9 | "type": "NS", 10 | "records": [ 11 | "ns1.example.org." 12 | ], 13 | "description": null, 14 | "ttl": null, 15 | "status": "ACTIVE", 16 | "action": "NONE", 17 | "version": 1, 18 | "created_at": "2021-12-05T23:45:54.000000", 19 | "updated_at": null, 20 | "links": { 21 | "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets/393d65fe-c0f1-4a55-a91e-bf6c1725dddc" 22 | } 23 | }, 24 | { 25 | "id": "4055914e-0f2b-4c47-a8d3-287a5286fe92", 26 | "zone_id": "6f106adb-0896-4114-b34f-4ac8dfee9465", 27 | "project_id": "15a6ae43e4dd498383e127e5f2521301", 28 | "name": "internal.example.org.", 29 | "zone_name": "internal.example.org.", 30 | "type": "SOA", 31 | "records": [ 32 | "ns1.example.org. fake.example.org. 1638748076 3584 600 86400 3600" 33 | ], 34 | "description": null, 35 | "ttl": null, 36 | "status": "ACTIVE", 37 | "action": "NONE", 38 | "version": 4, 39 | "created_at": "2021-12-05T23:45:54.000000", 40 | "updated_at": "2021-12-05T23:47:57.000000", 41 | "links": { 42 | "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets/4055914e-0f2b-4c47-a8d3-287a5286fe92" 43 | } 44 | }, 45 | { 46 | "id": "a00ac483-d74d-4555-b30f-2e0a176287e2", 47 | "zone_id": "6f106adb-0896-4114-b34f-4ac8dfee9465", 48 | "project_id": "15a6ae43e4dd498383e127e5f2521301", 49 | "name": "example.internal.example.org.", 50 | "zone_name": "internal.example.org.", 51 | "type": "A", 52 | "records": [ 53 | "127.0.0.2", 54 | "127.0.0.1" 55 | ], 56 | "description": null, 57 | "ttl": null, 58 | "status": "ACTIVE", 59 | "action": "NONE", 60 | "version": 1, 61 | "created_at": "2021-12-05T23:47:57.000000", 62 | "updated_at": null, 63 | "links": { 64 | "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465/recordsets/a00ac483-d74d-4555-b30f-2e0a176287e2" 65 | } 66 | }, 67 | { 68 | "id": "2f5b0a00-2f03-426c-a5fc-5c1c85a5156b", 69 | "zone_id": "a5f8e94e-af03-4e3e-b3eb-688a3b9f3b31", 70 | "project_id": "15a6ae43e4dd498383e127e5f2521301", 71 | "name": "external.example.org.", 72 | "zone_name": "external.example.org.", 73 | "type": "SOA", 74 | "records": [ 75 | "ns1.example.org. another_fake.example.org. 1638748236 3554 600 86400 3600" 76 | ], 77 | "description": null, 78 | "ttl": null, 79 | "status": "PENDING", 80 | "action": "CREATE", 81 | "version": 1, 82 | "created_at": "2021-12-05T23:50:36.000000", 83 | "updated_at": null, 84 | "links": { 85 | "self": "http://10.0.1.203/dns/v2/zones/a5f8e94e-af03-4e3e-b3eb-688a3b9f3b31/recordsets/2f5b0a00-2f03-426c-a5fc-5c1c85a5156b" 86 | } 87 | }, 88 | { 89 | "id": "4aacd04d-2546-43d9-98cd-58960a0a2494", 90 | "zone_id": "a5f8e94e-af03-4e3e-b3eb-688a3b9f3b31", 91 | "project_id": "15a6ae43e4dd498383e127e5f2521301", 92 | "name": "external.example.org.", 93 | "zone_name": "external.example.org.", 94 | "type": "NS", 95 | "records": [ 96 | "ns1.example.org." 97 | ], 98 | "description": null, 99 | "ttl": null, 100 | "status": "PENDING", 101 | "action": "CREATE", 102 | "version": 1, 103 | "created_at": "2021-12-05T23:50:36.000000", 104 | "updated_at": null, 105 | "links": { 106 | "self": "http://10.0.1.203/dns/v2/zones/a5f8e94e-af03-4e3e-b3eb-688a3b9f3b31/recordsets/4aacd04d-2546-43d9-98cd-58960a0a2494" 107 | } 108 | } 109 | ], 110 | "links": { 111 | "self": "http://10.0.1.203/dns/v2/recordsets" 112 | }, 113 | "metadata": { 114 | "total_count": 5 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /designateclient/tests/osc/resources/zone_create.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "6f106adb-0896-4114-b34f-4ac8dfee9465", 3 | "pool_id": "794ccc2c-d751-44fe-b57f-8894c9f5c842", 4 | "project_id": "15a6ae43e4dd498383e127e5f2521301", 5 | "name": "internal.example.org.", 6 | "email": "fake@example.org", 7 | "description": null, 8 | "ttl": 3600, 9 | "serial": 1638747953, 10 | "status": "PENDING", 11 | "action": "CREATE", 12 | "version": 1, 13 | "attributes": {}, 14 | "type": "PRIMARY", 15 | "masters": [], 16 | "created_at": "2021-12-05T23:45:54.000000", 17 | "updated_at": null, 18 | "transferred_at": null, 19 | "links": { 20 | "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /designateclient/tests/osc/resources/zone_list.json: -------------------------------------------------------------------------------- 1 | { 2 | "zones": [ 3 | { 4 | "id": "6f106adb-0896-4114-b34f-4ac8dfee9465", 5 | "pool_id": "794ccc2c-d751-44fe-b57f-8894c9f5c842", 6 | "project_id": "15a6ae43e4dd498383e127e5f2521301", 7 | "name": "internal.example.org.", 8 | "email": "fake@example.org", 9 | "description": null, 10 | "ttl": 3600, 11 | "serial": 1638748076, 12 | "status": "ACTIVE", 13 | "action": "NONE", 14 | "version": 8, 15 | "attributes": {}, 16 | "type": "PRIMARY", 17 | "masters": [], 18 | "created_at": "2021-12-05T23:45:54.000000", 19 | "updated_at": "2021-12-05T23:48:02.000000", 20 | "transferred_at": null, 21 | "links": { 22 | "self": "http://10.0.1.203/dns/v2/zones/6f106adb-0896-4114-b34f-4ac8dfee9465" 23 | } 24 | }, 25 | { 26 | "id": "a5f8e94e-af03-4e3e-b3eb-688a3b9f3b31", 27 | "pool_id": "794ccc2c-d751-44fe-b57f-8894c9f5c842", 28 | "project_id": "15a6ae43e4dd498383e127e5f2521301", 29 | "name": "external.example.org.", 30 | "email": "another_fake@example.org", 31 | "description": null, 32 | "ttl": 3600, 33 | "serial": 1638748236, 34 | "status": "ACTIVE", 35 | "action": "NONE", 36 | "version": 2, 37 | "attributes": {}, 38 | "type": "PRIMARY", 39 | "masters": [], 40 | "created_at": "2021-12-05T23:50:36.000000", 41 | "updated_at": "2021-12-05T23:50:42.000000", 42 | "transferred_at": null, 43 | "links": { 44 | "self": "http://10.0.1.203/dns/v2/zones/a5f8e94e-af03-4e3e-b3eb-688a3b9f3b31" 45 | } 46 | } 47 | ], 48 | "links": { 49 | "self": "http://10.0.1.203/dns/v2/zones?type=PRIMARY" 50 | }, 51 | "metadata": { 52 | "total_count": 2 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /designateclient/tests/osc/v2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/python-designateclient/1505c27dc6a8b5b092b7fff87f7d9cd7d64da154/designateclient/tests/osc/v2/__init__.py -------------------------------------------------------------------------------- /designateclient/tests/osc/v2/test_recordsets.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | from unittest import mock 14 | 15 | from osc_lib.tests import utils 16 | 17 | from designateclient.tests.osc import resources 18 | from designateclient.v2 import base 19 | from designateclient.v2.cli import recordsets 20 | 21 | 22 | class TestDesignateCreateRecordSets(utils.TestCommand): 23 | def setUp(self): 24 | super().setUp() 25 | self.app.client_manager.dns = mock.MagicMock() 26 | self.cmd = recordsets.CreateRecordSetCommand(self.app, None) 27 | self.dns_client = self.app.client_manager.dns 28 | 29 | def test_create_recordset(self): 30 | arg_list = [ 31 | '6f106adb-0896-4114-b34f-4ac8dfee9465', 32 | 'example', 33 | '--type', 'A', 34 | '--record', '127.0.0.1', 35 | '--record', '127.0.0.2', 36 | ] 37 | verify_args = [ 38 | ('zone_id', '6f106adb-0896-4114-b34f-4ac8dfee9465'), 39 | ('name', 'example'), 40 | ('type', 'A'), 41 | ('record', ['127.0.0.1', '127.0.0.2']), 42 | ] 43 | 44 | body = resources.load('recordset_create') 45 | self.dns_client.recordsets.create.return_value = body 46 | 47 | parsed_args = self.check_parser(self.cmd, arg_list, verify_args) 48 | columns, data = self.cmd.take_action(parsed_args) 49 | 50 | results = list(data) 51 | 52 | self.assertEqual(14, len(results)) 53 | 54 | 55 | class TestDesignateListRecordSets(utils.TestCommand): 56 | def setUp(self): 57 | super().setUp() 58 | self.app.client_manager.dns = mock.MagicMock() 59 | self.cmd = recordsets.ListRecordSetsCommand(self.app, None) 60 | self.dns_client = self.app.client_manager.dns 61 | 62 | def test_list_recordsets(self): 63 | arg_list = ['6f106adb-0896-4114-b34f-4ac8dfee9465'] 64 | verify_args = [ 65 | ('zone_id', '6f106adb-0896-4114-b34f-4ac8dfee9465'), 66 | ] 67 | 68 | body = resources.load('recordset_list') 69 | result = base.DesignateList() 70 | result.extend(body['recordsets']) 71 | 72 | self.dns_client.recordsets.list.return_value = result 73 | 74 | parsed_args = self.check_parser(self.cmd, arg_list, verify_args) 75 | columns, data = self.cmd.take_action(parsed_args) 76 | 77 | results = list(data) 78 | 79 | self.assertEqual(3, len(results)) 80 | 81 | def test_list_all_recordsets(self): 82 | arg_list = ['all'] 83 | verify_args = [ 84 | ('zone_id', 'all'), 85 | ] 86 | 87 | body = resources.load('recordset_list_all') 88 | result = base.DesignateList() 89 | result.extend(body['recordsets']) 90 | 91 | self.dns_client.recordsets.list_all_zones.return_value = result 92 | 93 | parsed_args = self.check_parser(self.cmd, arg_list, verify_args) 94 | columns, data = self.cmd.take_action(parsed_args) 95 | 96 | results = list(data) 97 | 98 | self.assertEqual(5, len(results)) 99 | 100 | def test_list_recordsets_with_long_option(self): 101 | 102 | arg_list = ['6f106adb-0896-4114-b34f-4ac8dfee9465', '--long'] 103 | verify_args = [ 104 | ('zone_id', '6f106adb-0896-4114-b34f-4ac8dfee9465'), 105 | ('long', True) 106 | ] 107 | 108 | body = resources.load('recordset_list') 109 | result = base.DesignateList() 110 | result.extend(body['recordsets']) 111 | self.dns_client.recordsets.list.return_value = result 112 | 113 | parsed_args = self.check_parser(self.cmd, arg_list, verify_args) 114 | columns, data = self.cmd.take_action(parsed_args) 115 | 116 | self.assertIn('ttl', columns) 117 | self.assertIn('version', columns) 118 | self.assertIn('description', columns) 119 | -------------------------------------------------------------------------------- /designateclient/tests/osc/v2/test_zone.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | from unittest import mock 14 | 15 | from osc_lib.tests import utils 16 | 17 | from designateclient.tests.osc import resources 18 | from designateclient.v2 import base 19 | from designateclient.v2.cli import zones 20 | 21 | 22 | class TestDesignateCreateZone(utils.TestCommand): 23 | def setUp(self): 24 | super().setUp() 25 | self.app.client_manager.dns = mock.MagicMock() 26 | self.cmd = zones.CreateZoneCommand(self.app, None) 27 | self.dns_client = self.app.client_manager.dns 28 | 29 | def test_create_zone(self): 30 | arg_list = [ 31 | 'example.devstack.org.', 32 | '--email', 'fake@devstack.org', 33 | ] 34 | verify_args = [ 35 | ('name', 'example.devstack.org.'), 36 | ('email', 'fake@devstack.org'), 37 | ] 38 | 39 | body = resources.load('zone_create') 40 | self.dns_client.zones.create.return_value = body 41 | 42 | parsed_args = self.check_parser(self.cmd, arg_list, verify_args) 43 | columns, data = self.cmd.take_action(parsed_args) 44 | 45 | results = list(data) 46 | 47 | self.assertEqual(17, len(results)) 48 | 49 | 50 | class TestDesignateListZones(utils.TestCommand): 51 | def setUp(self): 52 | super().setUp() 53 | self.app.client_manager.dns = mock.MagicMock() 54 | self.cmd = zones.ListZonesCommand(self.app, None) 55 | self.dns_client = self.app.client_manager.dns 56 | 57 | def test_list_zones(self): 58 | arg_list = [] 59 | verify_args = [] 60 | 61 | body = resources.load('zone_list') 62 | result = base.DesignateList() 63 | result.extend(body['zones']) 64 | 65 | self.dns_client.zones.list.return_value = result 66 | 67 | parsed_args = self.check_parser(self.cmd, arg_list, verify_args) 68 | columns, data = self.cmd.take_action(parsed_args) 69 | 70 | results = list(data) 71 | 72 | self.assertEqual(2, len(results)) 73 | 74 | def test_list_zones_with_long_option(self): 75 | arg_list = ['--long'] 76 | verify_args = [('long', True)] 77 | 78 | body = resources.load('zone_list') 79 | result = base.DesignateList() 80 | result.extend(body['zones']) 81 | 82 | self.dns_client.zones.list.return_value = result 83 | 84 | parsed_args = self.check_parser(self.cmd, arg_list, verify_args) 85 | columns, data = self.cmd.take_action(parsed_args) 86 | 87 | self.assertIn('ttl', columns) 88 | self.assertIn('pool_id', columns) 89 | self.assertIn('email', columns) 90 | self.assertIn('attributes', columns) 91 | self.assertIn('masters', columns) 92 | -------------------------------------------------------------------------------- /designateclient/tests/test_designateclient.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | """ 14 | test_designateclient 15 | ---------------------------------- 16 | 17 | Tests for `designateclient` module. 18 | """ 19 | 20 | import designateclient 21 | from designateclient.tests import base 22 | 23 | 24 | class ClientTestCase(base.TestCase): 25 | 26 | def test_module_version(self): 27 | self.assertTrue(hasattr(designateclient, '__version__')) 28 | -------------------------------------------------------------------------------- /designateclient/tests/test_exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Rackspace Inc. 2 | # 3 | # Author: James Li 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from designateclient import exceptions 18 | from designateclient.tests import base 19 | 20 | 21 | class RemoteErrorTestCase(base.TestCase): 22 | response_dict = { 23 | 'message': None, 24 | 'code': 500, 25 | 'type': None, 26 | 'errors': None, 27 | 'request_id': 1234 28 | } 29 | 30 | def test_get_error_message(self): 31 | expected_msg = 'something wrong' 32 | self.response_dict['message'] = expected_msg 33 | remote_err = exceptions.RemoteError(**self.response_dict) 34 | self.assertEqual(expected_msg, remote_err.message) 35 | 36 | def test_get_error_message_with_errors(self): 37 | expected_msg = "u'nodot.com' is not a 'domainname'" 38 | errors = {"errors": [ 39 | {"path": ["name"], 40 | "message": expected_msg, 41 | "validator": "format", 42 | "validator_value": "domainname"} 43 | ] 44 | } 45 | self.response_dict['message'] = None 46 | self.response_dict['errors'] = errors 47 | remote_err = exceptions.RemoteError(**self.response_dict) 48 | self.assertEqual(expected_msg, remote_err.message) 49 | 50 | def test_get_error_message_with_type(self): 51 | expected_msg = 'invalid_object' 52 | self.response_dict['message'] = None 53 | self.response_dict['errors'] = None 54 | self.response_dict['type'] = expected_msg 55 | remote_err = exceptions.RemoteError(**self.response_dict) 56 | self.assertEqual(expected_msg, remote_err.message) 57 | 58 | def test_get_error_message_with_unknown_response(self): 59 | expected_msg = 'invalid_object' 60 | self.response_dict['message'] = expected_msg 61 | self.response_dict['unknown'] = 'fake' 62 | remote_err = exceptions.RemoteError(**self.response_dict) 63 | self.assertEqual(expected_msg, remote_err.message) 64 | -------------------------------------------------------------------------------- /designateclient/tests/test_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Thales Services SAS 2 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 3 | # not use this file except in compliance with the License. You may obtain 4 | # a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 10 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 11 | # License for the specific language governing permissions and limitations 12 | # under the License. 13 | 14 | from unittest import mock 15 | import uuid 16 | 17 | from designateclient import exceptions 18 | from designateclient.tests import base 19 | from designateclient import utils 20 | 21 | 22 | LIST_MOCK_RESPONSE = [ 23 | {'id': '13579bdf-0000-0000-abcd-000000000001', 'name': 'abcd'}, 24 | {'id': '13579bdf-0000-0000-baba-000000000001', 'name': 'baba'}, 25 | {'id': '13579bdf-0000-0000-baba-000000000002', 'name': 'baba'}, 26 | ] 27 | 28 | 29 | class UtilsTestCase(base.TestCase): 30 | 31 | def _find_resourceid_by_name_or_id(self, name_or_id, by_name=False): 32 | resource_client = mock.Mock() 33 | resource_client.list.return_value = LIST_MOCK_RESPONSE 34 | resourceid = utils.find_resourceid_by_name_or_id( 35 | resource_client, name_or_id) 36 | self.assertEqual(by_name, resource_client.list.called) 37 | return resourceid 38 | 39 | def test_find_resourceid_with_hyphen_uuid(self): 40 | expected = str(uuid.uuid4()) 41 | observed = self._find_resourceid_by_name_or_id(expected) 42 | self.assertEqual(expected, observed) 43 | 44 | def test_find_resourceid_with_nonhyphen_uuid(self): 45 | expected = str(uuid.uuid4()) 46 | fakeid = expected.replace('-', '') 47 | observed = self._find_resourceid_by_name_or_id(fakeid) 48 | self.assertEqual(expected, observed) 49 | 50 | def test_find_resourceid_with_unique_resource(self): 51 | observed = self._find_resourceid_by_name_or_id('abcd', by_name=True) 52 | self.assertEqual('13579bdf-0000-0000-abcd-000000000001', observed) 53 | 54 | def test_find_resourceid_with_nonexistent_resource(self): 55 | self.assertRaises(exceptions.ResourceNotFound, 56 | self._find_resourceid_by_name_or_id, 57 | 'taz', by_name=True) 58 | 59 | def test_find_resourceid_with_multiple_resources(self): 60 | self.assertRaises(exceptions.NoUniqueMatch, 61 | self._find_resourceid_by_name_or_id, 62 | 'baba', by_name=True) 63 | -------------------------------------------------------------------------------- /designateclient/tests/v2/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | import uuid 17 | 18 | from designateclient.tests import base 19 | 20 | 21 | class CrudMixin: 22 | path_prefix = None 23 | 24 | def new_ref(self, **kwargs): 25 | kwargs.setdefault('id', uuid.uuid4().hex) 26 | return kwargs 27 | 28 | def stub_entity(self, method, parts=None, entity=None, id=None, **kwargs): 29 | if entity: 30 | kwargs['json'] = entity 31 | 32 | if not parts: 33 | parts = [self.RESOURCE] 34 | 35 | if self.path_prefix: 36 | parts.insert(0, self.path_prefix) 37 | 38 | if id: 39 | if not parts: 40 | parts = [] 41 | 42 | parts.append(id) 43 | 44 | self.stub_url(method, parts=parts, **kwargs) 45 | 46 | def assertList(self, expected, actual): 47 | self.assertEqual(len(expected), len(actual)) 48 | for i in expected: 49 | self.assertIn(i, actual) 50 | 51 | 52 | class APIV2TestCase(base.APITestCase): 53 | VERSION = "2" 54 | -------------------------------------------------------------------------------- /designateclient/tests/v2/test_blacklists.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | import uuid 17 | 18 | from designateclient.tests import v2 19 | 20 | 21 | class TestBlacklists(v2.APIV2TestCase, v2.CrudMixin): 22 | RESOURCE = 'blacklists' 23 | 24 | def new_ref(self, **kwargs): 25 | ref = super().new_ref(**kwargs) 26 | ref.setdefault("pattern", uuid.uuid4().hex) 27 | return ref 28 | 29 | def test_create(self): 30 | ref = self.new_ref() 31 | 32 | self.stub_url("POST", parts=[self.RESOURCE], json=ref) 33 | 34 | values = ref.copy() 35 | del values["id"] 36 | 37 | self.client.blacklists.create(**values) 38 | self.assertRequestBodyIs(json=values) 39 | 40 | def test_create_with_description(self): 41 | ref = self.new_ref(description="My Blacklist") 42 | 43 | self.stub_url("POST", parts=[self.RESOURCE], json=ref) 44 | 45 | values = ref.copy() 46 | del values["id"] 47 | 48 | self.client.blacklists.create(**values) 49 | self.assertRequestBodyIs(json=values) 50 | 51 | def test_get(self): 52 | ref = self.new_ref() 53 | 54 | self.stub_entity("GET", entity=ref, id=ref["id"]) 55 | 56 | response = self.client.blacklists.get(ref["id"]) 57 | self.assertEqual(ref, response) 58 | 59 | def test_list(self): 60 | items = [ 61 | self.new_ref(), 62 | self.new_ref() 63 | ] 64 | 65 | self.stub_url("GET", parts=[self.RESOURCE], json={"blacklists": items}) 66 | 67 | listed = self.client.blacklists.list() 68 | self.assertList(items, listed) 69 | self.assertQueryStringIs("") 70 | 71 | def test_update(self): 72 | ref = self.new_ref() 73 | 74 | self.stub_entity("PATCH", entity=ref, id=ref["id"]) 75 | 76 | values = ref.copy() 77 | del values["id"] 78 | 79 | self.client.blacklists.update(ref["id"], values) 80 | self.assertRequestBodyIs(json=values) 81 | 82 | def test_delete(self): 83 | ref = self.new_ref() 84 | 85 | self.stub_entity("DELETE", id=ref["id"]) 86 | 87 | self.client.blacklists.delete(ref["id"]) 88 | self.assertRequestBodyIs(None) 89 | -------------------------------------------------------------------------------- /designateclient/tests/v2/test_client.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Federico Ceratto 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from keystoneauth1 import adapter 18 | from keystoneauth1 import session as keystone_session 19 | 20 | from designateclient.tests.base import TestCase 21 | from designateclient.v2.client import Client 22 | 23 | 24 | class TestClient(TestCase): 25 | def test_init(self): 26 | self.assertRaises(ValueError, Client) 27 | 28 | def test_init_with_session(self): 29 | session = keystone_session.Session() 30 | adapted = adapter.Adapter(session=session) 31 | client = Client(session=adapted) 32 | assert client.session 33 | 34 | def test_init_with_session_timeout(self): 35 | session = keystone_session.Session() 36 | client = Client( 37 | session=session, 38 | timeout=1) 39 | assert client.session.timeout == 1 40 | 41 | def test_init_with_auth(self): 42 | session = keystone_session.Session() 43 | client = Client( 44 | auth='http://127.0.0.1:22/', 45 | session=session) 46 | assert client.session.auth 47 | -------------------------------------------------------------------------------- /designateclient/tests/v2/test_limits.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | from designateclient.tests import v2 17 | 18 | 19 | class TestLimits(v2.APIV2TestCase, v2.CrudMixin): 20 | def test_get(self): 21 | ref = {"max_zones": "foo"} 22 | self.stub_url("GET", parts=["limits"], json=ref) 23 | 24 | limits = self.client.limits.get() 25 | self.assertEqual(ref, limits) 26 | -------------------------------------------------------------------------------- /designateclient/tests/v2/test_nameservers.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | from unittest.mock import patch 17 | 18 | from designateclient.tests import v2 19 | from designateclient.v2 import zones 20 | 21 | 22 | class TestLimits(v2.APIV2TestCase, v2.CrudMixin): 23 | @patch.object(zones.ZoneController, "list") 24 | def test_get(self, zones_get): 25 | zones_get.return_value = [{"id": "foo"}] 26 | 27 | ref = [{ 28 | "hostname": "ns1.example.com.", 29 | "priority": 1 30 | }] 31 | parts = ["zones", "foo", "nameservers"] 32 | self.stub_url("GET", parts=parts, json={"nameservers": ref}) 33 | 34 | response = self.client.nameservers.list("foo") 35 | self.assertEqual(ref, response) 36 | -------------------------------------------------------------------------------- /designateclient/tests/v2/test_reverse.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | import uuid 17 | 18 | from designateclient.tests import v2 19 | 20 | FIP_ID = '{}:{}'.format(str(uuid.uuid4()), "RegionOne") 21 | 22 | 23 | class TestFloatingIP(v2.APIV2TestCase, v2.CrudMixin): 24 | def test_set(self): 25 | name = "foo.com." 26 | 27 | ref = { 28 | "ptrdname": name, 29 | "description": "foo" 30 | } 31 | 32 | parts = ["reverse", "floatingips", FIP_ID] 33 | self.stub_url("PATCH", parts=parts, json=ref) 34 | 35 | self.client.floatingips.set(FIP_ID, name, "foo") 36 | 37 | def test_list(self): 38 | ref = [ 39 | {"ptrdname": "foo.com."} 40 | ] 41 | 42 | self.stub_url("GET", parts=["reverse", "floatingips"], 43 | json={"floatingips": ref}) 44 | 45 | self.client.floatingips.list() 46 | 47 | def test_get(self): 48 | ref = { 49 | "ptrdname": "foo.com." 50 | } 51 | 52 | parts = ["reverse", "floatingips", FIP_ID] 53 | self.stub_url("GET", parts=parts, json=ref) 54 | 55 | self.client.floatingips.get(FIP_ID) 56 | 57 | def test_unset(self): 58 | parts = ["reverse", "floatingips", FIP_ID] 59 | self.stub_url("PATCH", parts=parts, json={"ptdrname": None}) 60 | 61 | self.client.floatingips.unset(FIP_ID) 62 | self.assertRequestBodyIs(None) 63 | -------------------------------------------------------------------------------- /designateclient/tests/v2/test_service_statuses.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Hewlett Packard Enterprise Development Company LP 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | from designateclient.tests import v2 17 | 18 | 19 | class TestServiceStatuses(v2.APIV2TestCase, v2.CrudMixin): 20 | RESOURCE = 'service_statuses' 21 | 22 | def new_ref(self, **kwargs): 23 | ref = super().new_ref(**kwargs) 24 | ref["name"] = "foo" 25 | return ref 26 | 27 | def test_get(self): 28 | ref = self.new_ref() 29 | 30 | self.stub_entity("GET", entity=ref, id=ref["id"]) 31 | 32 | response = self.client.service_statuses.get(ref["id"]) 33 | self.assertEqual(ref, response) 34 | 35 | def test_list(self): 36 | items = [ 37 | self.new_ref(), 38 | self.new_ref() 39 | ] 40 | 41 | self.stub_url("GET", parts=[self.RESOURCE], 42 | json={"service_statuses": items}) 43 | 44 | listed = self.client.service_statuses.list() 45 | self.assertList(items, listed) 46 | self.assertQueryStringIs("") 47 | -------------------------------------------------------------------------------- /designateclient/tests/v2/test_timeout.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Federico Ceratto 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from keystoneauth1.identity import generic 18 | from keystoneauth1 import session as keystone_session 19 | from unittest.mock import Mock 20 | 21 | from designateclient.tests import v2 22 | from designateclient.v2.client import Client 23 | 24 | 25 | def create_session(timeout=None): 26 | auth = generic.Password(auth_url='', username='', password='', 27 | tenant_name='') 28 | return keystone_session.Session(auth=auth, timeout=timeout) 29 | 30 | 31 | class TestTimeout(v2.APIV2TestCase, v2.CrudMixin): 32 | 33 | def setUp(self): 34 | super().setUp() 35 | 36 | # Mock methods in KeyStone's Session 37 | self._saved_methods = ( 38 | keystone_session.Session.get_auth_headers, 39 | keystone_session.Session.get_endpoint, 40 | keystone_session.Session._send_request, 41 | ) 42 | 43 | resp = Mock() 44 | resp.text = '' 45 | resp.status_code = 200 46 | 47 | keystone_session.Session.get_auth_headers = Mock( 48 | return_value=[] 49 | ) 50 | keystone_session.Session.get_endpoint = Mock( 51 | return_value='foo' 52 | ) 53 | keystone_session.Session._send_request = Mock( 54 | return_value=resp, 55 | ) 56 | self.mock_send_request = keystone_session.Session._send_request 57 | 58 | def tearDown(self): 59 | super().tearDown() 60 | ( 61 | keystone_session.Session.get_auth_headers, 62 | keystone_session.Session.get_endpoint, 63 | keystone_session.Session._send_request, 64 | ) = self._saved_methods 65 | 66 | def _call_request_and_check_timeout(self, client, timeout): 67 | """call the mocked _send_request() and check if the timeout was set 68 | """ 69 | client.limits.get() 70 | self.assertTrue(self.mock_send_request.called) 71 | kw = self.mock_send_request.call_args[1] 72 | if timeout is None: 73 | self.assertNotIn('timeout', kw) 74 | else: 75 | self.assertEqual(timeout, kw['timeout']) 76 | 77 | def test_no_timeout(self): 78 | session = create_session(timeout=None) 79 | client = Client(session=session) 80 | self.assertIsNone(session.timeout) 81 | self.assertIsNone(client.session.timeout) 82 | self._call_request_and_check_timeout(client, None) 83 | 84 | def test_timeout_in_session(self): 85 | session = create_session(timeout=1) 86 | client = Client(session=session) 87 | self.assertEqual(1, session.timeout) 88 | self.assertIsNone(client.session.timeout) 89 | self._call_request_and_check_timeout(client, 1) 90 | 91 | def test_timeout_override_session_timeout(self): 92 | # The adapter timeout should override the session timeout 93 | session = create_session(timeout=10) 94 | self.assertEqual(10, session.timeout) 95 | client = Client(session=session, timeout=2) 96 | self.assertEqual(2, client.session.timeout) 97 | self._call_request_and_check_timeout(client, 2) 98 | 99 | def test_timeout_update(self): 100 | session = create_session(timeout=1) 101 | client = Client(session=session) 102 | self.assertEqual(1, session.timeout) 103 | self.assertIsNone(client.session.timeout) 104 | self._call_request_and_check_timeout(client, 1) 105 | 106 | session.timeout = 2 107 | self.assertEqual(2, session.timeout) 108 | 109 | self._call_request_and_check_timeout(client, 2) 110 | -------------------------------------------------------------------------------- /designateclient/tests/v2/test_tlds.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | import uuid 17 | 18 | from designateclient.tests import v2 19 | 20 | 21 | class TestTlds(v2.APIV2TestCase, v2.CrudMixin): 22 | RESOURCE = 'tlds' 23 | 24 | def new_ref(self, **kwargs): 25 | ref = super().new_ref(**kwargs) 26 | ref.setdefault("name", uuid.uuid4().hex) 27 | return ref 28 | 29 | def test_create(self): 30 | ref = self.new_ref() 31 | 32 | self.stub_url("POST", parts=[self.RESOURCE], json=ref) 33 | 34 | values = ref.copy() 35 | del values["id"] 36 | 37 | self.client.tlds.create(**values) 38 | self.assertRequestBodyIs(json=values) 39 | 40 | def test_create_with_description(self): 41 | ref = self.new_ref(description="My TLD") 42 | 43 | self.stub_url("POST", parts=[self.RESOURCE], json=ref) 44 | 45 | values = ref.copy() 46 | del values["id"] 47 | 48 | self.client.tlds.create(**values) 49 | self.assertRequestBodyIs(json=values) 50 | 51 | def test_get(self): 52 | ref = self.new_ref() 53 | 54 | self.stub_entity("GET", entity=ref, id=ref["id"]) 55 | 56 | response = self.client.tlds.get(ref["id"]) 57 | self.assertEqual(ref, response) 58 | 59 | def test_get_by_name(self): 60 | ref = self.new_ref(name="www") 61 | 62 | self.stub_entity("GET", entity=ref, id=ref["id"]) 63 | self.stub_url("GET", parts=[self.RESOURCE], json={"tlds": [ref]}) 64 | 65 | response = self.client.tlds.get(ref['name']) 66 | 67 | self.assertEqual("GET", self.requests.request_history[0].method) 68 | self.assertEqual( 69 | "http://127.0.0.1:9001/v2/tlds?name=www", 70 | self.requests.request_history[0].url) 71 | 72 | self.assertEqual(ref, response) 73 | 74 | def test_list(self): 75 | items = [ 76 | self.new_ref(), 77 | self.new_ref() 78 | ] 79 | 80 | self.stub_url("GET", parts=[self.RESOURCE], json={"tlds": items}) 81 | 82 | listed = self.client.tlds.list() 83 | self.assertList(items, listed) 84 | self.assertQueryStringIs("") 85 | 86 | def test_update(self): 87 | ref = self.new_ref() 88 | 89 | self.stub_entity("PATCH", entity=ref, id=ref["id"]) 90 | 91 | values = ref.copy() 92 | del values["id"] 93 | 94 | self.client.tlds.update(ref["id"], values) 95 | self.assertRequestBodyIs(json=values) 96 | 97 | def test_delete(self): 98 | ref = self.new_ref() 99 | 100 | self.stub_entity("DELETE", id=ref["id"]) 101 | 102 | self.client.tlds.delete(ref["id"]) 103 | self.assertRequestBodyIs(None) 104 | -------------------------------------------------------------------------------- /designateclient/tests/v2/test_tsigkeys.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 SAP SE 2 | # 3 | # Author: Rudolf Vriend 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | import uuid 17 | 18 | from designateclient.tests import v2 19 | 20 | 21 | class TestTSIGKeys(v2.APIV2TestCase, v2.CrudMixin): 22 | RESOURCE = 'tsigkeys' 23 | 24 | def new_ref(self, **kwargs): 25 | ref = super().new_ref(**kwargs) 26 | ref.setdefault("name", uuid.uuid4().hex) 27 | ref.setdefault("algorithm", "hmac-sha256") 28 | ref.setdefault("secret", uuid.uuid4().hex) 29 | ref.setdefault("scope", "POOL") 30 | ref.setdefault("resource_id", uuid.uuid4().hex) 31 | return ref 32 | 33 | def test_create(self): 34 | ref = self.new_ref() 35 | 36 | self.stub_url("POST", parts=[self.RESOURCE], json=ref) 37 | 38 | values = ref.copy() 39 | del values["id"] 40 | 41 | self.client.tsigkeys.create(**values) 42 | self.assertRequestBodyIs(json=values) 43 | 44 | def test_get(self): 45 | ref = self.new_ref() 46 | 47 | self.stub_entity("GET", entity=ref, id=ref["id"]) 48 | 49 | response = self.client.tsigkeys.get(ref["id"]) 50 | self.assertEqual(ref, response) 51 | 52 | def test_get_by_name(self): 53 | ref = self.new_ref(name="www") 54 | 55 | self.stub_entity("GET", entity=ref, id=ref["id"]) 56 | self.stub_url("GET", parts=[self.RESOURCE], json={"tsigkeys": [ref]}) 57 | 58 | response = self.client.tsigkeys.get(ref['name']) 59 | 60 | self.assertEqual("GET", self.requests.request_history[0].method) 61 | self.assertEqual( 62 | "http://127.0.0.1:9001/v2/tsigkeys?name=www", 63 | self.requests.request_history[0].url) 64 | 65 | self.assertEqual(ref, response) 66 | 67 | def test_list(self): 68 | items = [ 69 | self.new_ref(), 70 | self.new_ref() 71 | ] 72 | 73 | self.stub_url("GET", parts=[self.RESOURCE], json={"tsigkeys": items}) 74 | 75 | listed = self.client.tsigkeys.list() 76 | self.assertList(items, listed) 77 | self.assertQueryStringIs("") 78 | 79 | def test_update(self): 80 | ref = self.new_ref() 81 | 82 | self.stub_entity("PATCH", entity=ref, id=ref["id"]) 83 | 84 | values = ref.copy() 85 | del values["id"] 86 | 87 | self.client.tsigkeys.update(ref["id"], values) 88 | self.assertRequestBodyIs(json=values) 89 | 90 | def test_delete(self): 91 | ref = self.new_ref() 92 | 93 | self.stub_entity("DELETE", id=ref["id"]) 94 | 95 | self.client.tsigkeys.delete(ref["id"]) 96 | self.assertRequestBodyIs(None) 97 | -------------------------------------------------------------------------------- /designateclient/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2012 Managed I.T. 2 | # 3 | # Author: Kiall Mac Innes 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import uuid 18 | 19 | from keystoneauth1 import adapter 20 | 21 | from designateclient import exceptions 22 | 23 | 24 | def get_item_properties(item, fields, mixed_case_fields=(), formatters=None): 25 | """Return a tuple containing the item properties. 26 | 27 | :param item: a single item resource (e.g. Server, Tenant, etc) 28 | :param fields: tuple of strings with the desired field names 29 | :param mixed_case_fields: tuple of field names to preserve case 30 | :param formatters: dictionary mapping field names to callables 31 | to format the values 32 | """ 33 | if formatters is None: 34 | formatters = {} 35 | row = [] 36 | 37 | for field in fields: 38 | if field in formatters: 39 | row.append(formatters[field](item)) 40 | else: 41 | if field in mixed_case_fields: 42 | field_name = field.replace(' ', '_') 43 | else: 44 | field_name = field.lower().replace(' ', '_') 45 | if (not hasattr(item, field_name) and 46 | (isinstance(item, dict) and field_name in item)): 47 | data = item[field_name] 48 | else: 49 | data = getattr(item, field_name, '') 50 | if data is None: 51 | data = '' 52 | row.append(data) 53 | return tuple(row) 54 | 55 | 56 | def get_columns(data): 57 | """ 58 | Some row's might have variable count of columns, ensure that we have the 59 | same. 60 | 61 | :param data: Results in [{}, {]}] 62 | """ 63 | columns = set() 64 | 65 | def _seen(col): 66 | columns.add(str(col)) 67 | 68 | map(lambda item: map(_seen, list(item.keys())), data) 69 | return list(columns) 70 | 71 | 72 | def find_resourceid_by_name_or_id(resource_client, name_or_id): 73 | """Find resource id from its id or name.""" 74 | try: 75 | # Try to return an uuid 76 | return str(uuid.UUID(name_or_id)) 77 | except ValueError: 78 | # Not an uuid => assume it is resource name 79 | pass 80 | 81 | resources = resource_client.list() 82 | candidate_ids = [r['id'] for r in resources if r.get('name') == name_or_id] 83 | if not candidate_ids: 84 | raise exceptions.ResourceNotFound( 85 | f'Could not find resource with name "{name_or_id}"') 86 | elif len(candidate_ids) > 1: 87 | str_ids = ','.join(candidate_ids) 88 | raise exceptions.NoUniqueMatch( 89 | f'Multiple resources with name "{name_or_id}": {str_ids}') 90 | return candidate_ids[0] 91 | 92 | 93 | class AdapterWithTimeout(adapter.Adapter): 94 | """adapter.Adapter wraps around a Session. 95 | 96 | The user can pass a timeout keyword that will apply only to 97 | the Designate Client, in order: 98 | 99 | - timeout keyword passed to ``request()`` 100 | - timeout keyword passed to ``AdapterWithTimeout()`` 101 | - timeout attribute on keystone session 102 | """ 103 | def __init__(self, *args, **kw): 104 | self.timeout = kw.pop('timeout', None) 105 | super(self.__class__, self).__init__(*args, **kw) 106 | 107 | def request(self, *args, **kwargs): 108 | if self.timeout is not None: 109 | kwargs.setdefault('timeout', self.timeout) 110 | 111 | return super().request(*args, **kwargs) 112 | -------------------------------------------------------------------------------- /designateclient/v2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/python-designateclient/1505c27dc6a8b5b092b7fff87f7d9cd7d64da154/designateclient/v2/__init__.py -------------------------------------------------------------------------------- /designateclient/v2/base.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Hewlett Packard Enterprise Development Company LP 2 | # 3 | # Author: Graham Hayes 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from designateclient import client 18 | from designateclient.v2.utils import parse_query_from_url 19 | 20 | 21 | class DesignateList(list): 22 | 23 | next_link_criterion = {} 24 | next_page = False 25 | 26 | 27 | class V2Controller(client.Controller): 28 | 29 | def _get(self, url, response_key=None, **kwargs): 30 | resp, body = self.client.session.get(url, **kwargs) 31 | 32 | if response_key is not None: 33 | data = DesignateList() 34 | data.extend(body[response_key]) 35 | 36 | if 'next' in body.get('links', {}): 37 | data.next_page = True 38 | data.next_link_criterion = parse_query_from_url( 39 | body['links']['next']) 40 | 41 | return data 42 | 43 | return body 44 | -------------------------------------------------------------------------------- /designateclient/v2/blacklists.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | from designateclient.v2.base import V2Controller 17 | 18 | 19 | class BlacklistController(V2Controller): 20 | def create(self, pattern, description=None): 21 | data = { 22 | 'pattern': pattern, 23 | } 24 | 25 | if description is not None: 26 | data['description'] = description 27 | 28 | return self._post('/blacklists', data=data) 29 | 30 | def list(self, criterion=None, marker=None, limit=None): 31 | url = self.build_url('/blacklists', criterion, marker, limit) 32 | 33 | return self._get(url, response_key='blacklists') 34 | 35 | def get(self, blacklist_id): 36 | url = f'/blacklists/{blacklist_id}' 37 | 38 | return self._get(url) 39 | 40 | def update(self, blacklist_id, values): 41 | url = f'/blacklists/{blacklist_id}' 42 | 43 | return self._patch(url, data=values) 44 | 45 | def delete(self, blacklist_id): 46 | url = f'/blacklists/{blacklist_id}' 47 | 48 | return self._delete(url) 49 | -------------------------------------------------------------------------------- /designateclient/v2/cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/python-designateclient/1505c27dc6a8b5b092b7fff87f7d9cd7d64da154/designateclient/v2/cli/__init__.py -------------------------------------------------------------------------------- /designateclient/v2/cli/blacklists.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import logging 18 | 19 | from osc_lib.command import command 20 | 21 | from designateclient import utils 22 | from designateclient.v2.cli import common 23 | from designateclient.v2.utils import get_all 24 | 25 | 26 | LOG = logging.getLogger(__name__) 27 | 28 | 29 | def _format_blacklist(blacklist): 30 | # Remove unneeded fields for display output formatting 31 | blacklist.pop('links', None) 32 | 33 | 34 | class ListBlacklistsCommand(command.Lister): 35 | """List blacklists""" 36 | 37 | columns = ['id', 'pattern', 'description'] 38 | 39 | def get_parser(self, prog_name): 40 | parser = super().get_parser(prog_name) 41 | 42 | common.add_all_common_options(parser) 43 | 44 | return parser 45 | 46 | def take_action(self, parsed_args): 47 | client = self.app.client_manager.dns 48 | common.set_all_common_headers(client, parsed_args) 49 | 50 | cols = self.columns 51 | data = get_all(client.blacklists.list) 52 | return cols, (utils.get_item_properties(s, cols) for s in data) 53 | 54 | 55 | class ShowBlacklistCommand(command.ShowOne): 56 | """Show blacklist details""" 57 | 58 | def get_parser(self, prog_name): 59 | parser = super().get_parser(prog_name) 60 | 61 | parser.add_argument('id', help='Blacklist ID') 62 | 63 | common.add_all_common_options(parser) 64 | 65 | return parser 66 | 67 | def take_action(self, parsed_args): 68 | client = self.app.client_manager.dns 69 | common.set_all_common_headers(client, parsed_args) 70 | data = client.blacklists.get(parsed_args.id) 71 | _format_blacklist(data) 72 | return zip(*sorted(data.items())) 73 | 74 | 75 | class CreateBlacklistCommand(command.ShowOne): 76 | """Create new blacklist""" 77 | 78 | def get_parser(self, prog_name): 79 | parser = super().get_parser(prog_name) 80 | 81 | parser.add_argument('--pattern', help='Blacklist pattern', 82 | required=True) 83 | parser.add_argument('--description', help='Description') 84 | 85 | common.add_all_common_options(parser) 86 | 87 | return parser 88 | 89 | def take_action(self, parsed_args): 90 | client = self.app.client_manager.dns 91 | common.set_all_common_headers(client, parsed_args) 92 | 93 | data = client.blacklists.create( 94 | parsed_args.pattern, parsed_args.description) 95 | 96 | _format_blacklist(data) 97 | return zip(*sorted(data.items())) 98 | 99 | 100 | class SetBlacklistCommand(command.ShowOne): 101 | """Set blacklist properties""" 102 | 103 | def get_parser(self, prog_name): 104 | parser = super().get_parser(prog_name) 105 | 106 | parser.add_argument('id', help='Blacklist ID') 107 | parser.add_argument('--pattern', help='Blacklist pattern') 108 | 109 | description_group = parser.add_mutually_exclusive_group() 110 | description_group.add_argument('--description', help='Description') 111 | description_group.add_argument('--no-description', action='store_true') 112 | 113 | common.add_all_common_options(parser) 114 | 115 | return parser 116 | 117 | def take_action(self, parsed_args): 118 | data = {} 119 | 120 | if parsed_args.pattern: 121 | data['pattern'] = parsed_args.pattern 122 | 123 | if parsed_args.no_description: 124 | data['description'] = None 125 | elif parsed_args.description: 126 | data['description'] = parsed_args.description 127 | 128 | client = self.app.client_manager.dns 129 | common.set_all_common_headers(client, parsed_args) 130 | 131 | updated = client.blacklists.update(parsed_args.id, data) 132 | 133 | _format_blacklist(updated) 134 | return zip(*sorted(updated.items())) 135 | 136 | 137 | class DeleteBlacklistCommand(command.Command): 138 | """Delete blacklist""" 139 | 140 | def get_parser(self, prog_name): 141 | parser = super().get_parser(prog_name) 142 | 143 | parser.add_argument('id', help='Blacklist ID') 144 | 145 | common.add_all_common_options(parser) 146 | 147 | return parser 148 | 149 | def take_action(self, parsed_args): 150 | client = self.app.client_manager.dns 151 | common.set_all_common_headers(client, parsed_args) 152 | client.blacklists.delete(parsed_args.id) 153 | 154 | LOG.info('Blacklist %s was deleted', parsed_args.id) 155 | -------------------------------------------------------------------------------- /designateclient/v2/cli/common.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Hewlett Packard Enterprise Development Company LP 2 | # 3 | # Author: Graham Hayes 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | 18 | def add_all_projects_option(parser): 19 | parser.add_argument( 20 | '--all-projects', 21 | default=False, 22 | action='store_true', 23 | help='Show results from all projects. Default: False' 24 | ) 25 | 26 | 27 | def add_edit_managed_option(parser): 28 | parser.add_argument( 29 | '--edit-managed', 30 | default=False, 31 | action='store_true', 32 | help='Edit resources marked as managed. Default: False' 33 | ) 34 | 35 | 36 | def add_hard_delete_option(parser): 37 | parser.add_argument( 38 | '--hard-delete', 39 | default=False, 40 | action='store_true', 41 | help='Delete zone along-with backend zone resources (i.e. files). ' 42 | 'Default: False' 43 | ) 44 | 45 | 46 | def add_sudo_project_id_option(parser): 47 | parser.add_argument( 48 | '--sudo-project-id', 49 | default=None, 50 | help='Project ID to impersonate for this command. Default: None' 51 | ) 52 | 53 | 54 | def add_all_common_options(parser): 55 | add_all_projects_option(parser) 56 | add_sudo_project_id_option(parser) 57 | 58 | 59 | def set_all_projects(client, value): 60 | client.session.all_projects = value 61 | 62 | 63 | def set_sudo_project_id(client, value): 64 | client.session.sudo_project_id = value 65 | 66 | 67 | def set_edit_managed(client, value): 68 | client.session.edit_managed = value 69 | 70 | 71 | def set_hard_delete(client, value): 72 | client.session.hard_delete = value 73 | 74 | 75 | def set_all_common_headers(client, parsed_args): 76 | 77 | if (parsed_args.all_projects is not None and 78 | isinstance(parsed_args.all_projects, bool)): 79 | set_all_projects(client, parsed_args.all_projects) 80 | 81 | if (hasattr(parsed_args, 'edit_managed') and 82 | parsed_args.edit_managed is not None and 83 | isinstance(parsed_args.edit_managed, bool)): 84 | set_edit_managed(client, parsed_args.edit_managed) 85 | 86 | if (parsed_args.sudo_project_id is not None and 87 | isinstance(parsed_args.sudo_project_id, str)): 88 | set_sudo_project_id(client, parsed_args.sudo_project_id) 89 | 90 | if (hasattr(parsed_args, 'hard_delete') and 91 | parsed_args.hard_delete is not None and 92 | isinstance(parsed_args.hard_delete, bool)): 93 | set_hard_delete(client, parsed_args.hard_delete) 94 | -------------------------------------------------------------------------------- /designateclient/v2/cli/quotas.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | import itertools 17 | import logging 18 | 19 | from cliff import command 20 | from cliff import show 21 | 22 | from designateclient.v2.cli import common 23 | 24 | LOG = logging.getLogger(__name__) 25 | 26 | 27 | DNS_QUOTAS = { 28 | "api_export_size": "api-export-size", 29 | "recordset_records": "recordset-records", 30 | "zone_records": "zone-records", 31 | "zone_recordsets": "zone-recordsets", 32 | "zones": "zones" 33 | } 34 | 35 | 36 | class ListQuotasCommand(show.ShowOne): 37 | """List quotas""" 38 | 39 | # columns = ['resource', 'hard_limit'] 40 | 41 | def get_parser(self, prog_name): 42 | parser = super().get_parser(prog_name) 43 | 44 | common.add_all_common_options(parser) 45 | 46 | parser.add_argument( 47 | '--project-id', 48 | help='Project ID Default: current project') 49 | 50 | return parser 51 | 52 | def take_action(self, parsed_args): 53 | client = self.app.client_manager.dns 54 | common.set_all_common_headers(client, parsed_args) 55 | 56 | proj_id = client.session.get_project_id() 57 | 58 | if parsed_args.project_id and parsed_args.project_id != proj_id: 59 | proj_id = parsed_args.project_id 60 | common.set_all_projects(client, True) 61 | 62 | data = client.quotas.list(proj_id) 63 | return zip(*sorted(data.items())) 64 | 65 | 66 | class SetQuotasCommand(show.ShowOne): 67 | """Set quotas""" 68 | 69 | def _build_options_list(self): 70 | return itertools.chain(DNS_QUOTAS.items()) 71 | 72 | def get_parser(self, prog_name): 73 | parser = super().get_parser(prog_name) 74 | 75 | common.add_all_common_options(parser) 76 | 77 | parser.add_argument('--project-id', help='Project ID') 78 | for k, v in self._build_options_list(): 79 | parser.add_argument( 80 | f'--{v}', 81 | metavar=f'<{v}>', 82 | dest=k, 83 | type=int, 84 | help=f'New value for the {v} quota', 85 | ) 86 | 87 | return parser 88 | 89 | def take_action(self, parsed_args): 90 | 91 | client = self.app.client_manager.dns 92 | common.set_all_common_headers(client, parsed_args) 93 | 94 | quotas = {} 95 | for k, v in DNS_QUOTAS.items(): 96 | value = getattr(parsed_args, k, None) 97 | if value is not None: 98 | quotas[k] = value 99 | 100 | proj_id = parsed_args.project_id or client.session.get_project_id() 101 | 102 | if parsed_args.project_id != client.session.get_project_id(): 103 | common.set_all_projects(client, True) 104 | 105 | updated = client.quotas.update(proj_id, quotas) 106 | 107 | return zip(*sorted(updated.items())) 108 | 109 | 110 | class ResetQuotasCommand(command.Command): 111 | """Reset quotas""" 112 | 113 | def get_parser(self, prog_name): 114 | parser = super().get_parser(prog_name) 115 | 116 | common.add_all_common_options(parser) 117 | 118 | parser.add_argument('--project-id', help='Project ID') 119 | 120 | return parser 121 | 122 | def take_action(self, parsed_args): 123 | client = self.app.client_manager.dns 124 | common.set_all_common_headers(client, parsed_args) 125 | 126 | proj_id = parsed_args.project_id or client.session.get_project_id() 127 | 128 | if parsed_args.project_id != client.session.get_project_id(): 129 | common.set_all_projects(client, True) 130 | 131 | client.quotas.reset(proj_id) 132 | 133 | LOG.info('Quota for project %s was reset', parsed_args.project_id) 134 | -------------------------------------------------------------------------------- /designateclient/v2/cli/reverse.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import logging 18 | 19 | from osc_lib.command import command 20 | 21 | from designateclient import utils 22 | from designateclient.v2.cli import common 23 | from designateclient.v2.utils import get_all 24 | 25 | 26 | LOG = logging.getLogger(__name__) 27 | 28 | 29 | def _format_floatingip(fip): 30 | # Remove unneeded fields for display output formatting 31 | fip.pop('links', None) 32 | 33 | 34 | class ListFloatingIPCommand(command.Lister): 35 | """List floatingip ptr records""" 36 | 37 | columns = ['id', 'ptrdname', 'description', 'ttl'] 38 | 39 | def get_parser(self, prog_name): 40 | parser = super().get_parser(prog_name) 41 | 42 | common.add_all_common_options(parser) 43 | 44 | return parser 45 | 46 | def take_action(self, parsed_args): 47 | client = self.app.client_manager.dns 48 | common.set_all_common_headers(client, parsed_args) 49 | 50 | cols = self.columns 51 | data = get_all(client.floatingips.list) 52 | return cols, (utils.get_item_properties(s, cols) for s in data) 53 | 54 | 55 | class ShowFloatingIPCommand(command.ShowOne): 56 | """Show floatingip ptr record details""" 57 | 58 | def get_parser(self, prog_name): 59 | parser = super().get_parser(prog_name) 60 | 61 | parser.add_argument('floatingip_id', help='Floating IP ID in format ' 62 | 'region:floatingip_id') 63 | 64 | common.add_all_common_options(parser) 65 | 66 | return parser 67 | 68 | def take_action(self, parsed_args): 69 | client = self.app.client_manager.dns 70 | common.set_all_common_headers(client, parsed_args) 71 | data = client.floatingips.get(parsed_args.floatingip_id) 72 | _format_floatingip(data) 73 | return zip(*sorted(data.items())) 74 | 75 | 76 | class SetFloatingIPCommand(command.ShowOne): 77 | """Set floatingip ptr record""" 78 | 79 | def get_parser(self, prog_name): 80 | parser = super().get_parser(prog_name) 81 | 82 | parser.add_argument('floatingip_id', help='Floating IP ID in format ' 83 | 'region:floatingip_id') 84 | parser.add_argument('ptrdname', help='PTRD Name') 85 | 86 | description_group = parser.add_mutually_exclusive_group() 87 | description_group.add_argument('--description', help='Description') 88 | description_group.add_argument('--no-description', action='store_true') 89 | 90 | ttl_group = parser.add_mutually_exclusive_group() 91 | ttl_group.add_argument('--ttl', type=int, help='TTL') 92 | ttl_group.add_argument('--no-ttl', action='store_true') 93 | 94 | common.add_all_common_options(parser) 95 | 96 | return parser 97 | 98 | def take_action(self, parsed_args): 99 | data = {} 100 | 101 | if parsed_args.no_description: 102 | data['description'] = None 103 | elif parsed_args.description: 104 | data['description'] = parsed_args.description 105 | 106 | if parsed_args.no_ttl: 107 | data['ttl'] = None 108 | elif parsed_args.ttl: 109 | data['ttl'] = parsed_args.ttl 110 | 111 | client = self.app.client_manager.dns 112 | common.set_all_common_headers(client, parsed_args) 113 | 114 | fip = client.floatingips.set( 115 | parsed_args.floatingip_id, 116 | parsed_args.ptrdname, 117 | parsed_args.description, 118 | parsed_args.ttl) 119 | 120 | _format_floatingip(fip) 121 | return zip(*sorted(fip.items())) 122 | 123 | 124 | class UnsetFloatingIPCommand(command.Command): 125 | """Unset floatingip ptr record""" 126 | 127 | def get_parser(self, prog_name): 128 | parser = super().get_parser(prog_name) 129 | 130 | parser.add_argument('floatingip_id', help='Floating IP ID in format ' 131 | 'region:floatingip_id') 132 | 133 | common.add_all_common_options(parser) 134 | 135 | return parser 136 | 137 | def take_action(self, parsed_args): 138 | client = self.app.client_manager.dns 139 | common.set_all_common_headers(client, parsed_args) 140 | client.floatingips.unset(parsed_args.floatingip_id) 141 | LOG.info('FloatingIP PTR %s was unset', parsed_args.floatingip_id) 142 | -------------------------------------------------------------------------------- /designateclient/v2/cli/service_statuses.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Hewlett Packard Enterprise Development Company LP 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import logging 18 | 19 | from osc_lib.command import command 20 | 21 | from designateclient import utils 22 | from designateclient.v2.cli import common 23 | from designateclient.v2 import utils as v2_utils 24 | 25 | 26 | LOG = logging.getLogger(__name__) 27 | 28 | 29 | def _format_status(status): 30 | status.pop('links', None) 31 | # Remove unneeded fields for display output formatting 32 | for k in ('capabilities', 'stats'): 33 | status[k] = '\n'.join(status[k]) if status[k] else '-' 34 | return status 35 | 36 | 37 | class ListServiceStatusesCommand(command.Lister): 38 | """List service statuses""" 39 | 40 | columns = ['id', 'hostname', 'service_name', 'status', 'stats', 41 | 'capabilities'] 42 | 43 | def get_parser(self, prog_name): 44 | parser = super().get_parser(prog_name) 45 | 46 | parser.add_argument('--hostname', help='Hostname', required=False) 47 | parser.add_argument('--service_name', help='Service Name', 48 | required=False) 49 | parser.add_argument('--status', help='Status', required=False) 50 | common.add_all_common_options(parser) 51 | 52 | return parser 53 | 54 | def take_action(self, parsed_args): 55 | client = self.app.client_manager.dns 56 | common.set_all_common_headers(client, parsed_args) 57 | 58 | cols = self.columns 59 | 60 | criterion = {} 61 | for i in ['hostname', 'service_name', 'status']: 62 | v = getattr(parsed_args, i) 63 | if v is not None: 64 | criterion[i] = v 65 | 66 | data = v2_utils.get_all(client.service_statuses.list, 67 | criterion=criterion) 68 | 69 | for i, s in enumerate(data): 70 | data[i] = _format_status(s) 71 | 72 | return cols, (utils.get_item_properties(s, cols) for s in data) 73 | 74 | 75 | class ShowServiceStatusCommand(command.ShowOne): 76 | """Show service status details""" 77 | 78 | def get_parser(self, prog_name): 79 | parser = super().get_parser(prog_name) 80 | 81 | parser.add_argument('id', help='Service Status ID') 82 | 83 | common.add_all_common_options(parser) 84 | 85 | return parser 86 | 87 | def take_action(self, parsed_args): 88 | client = self.app.client_manager.dns 89 | common.set_all_common_headers(client, parsed_args) 90 | data = client.service_statuses.get(parsed_args.id) 91 | 92 | _format_status(data) 93 | return zip(*sorted(data.items())) 94 | -------------------------------------------------------------------------------- /designateclient/v2/cli/tlds.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import logging 18 | 19 | from osc_lib.command import command 20 | 21 | from designateclient import utils 22 | from designateclient.v2.cli import common 23 | from designateclient.v2.utils import get_all 24 | 25 | 26 | LOG = logging.getLogger(__name__) 27 | 28 | 29 | def _format_tld(tld): 30 | # Remove unneeded fields for display output formatting 31 | tld.pop('links', None) 32 | 33 | 34 | class ListTLDsCommand(command.Lister): 35 | """List tlds""" 36 | 37 | columns = ['id', 'name', 'description'] 38 | 39 | def get_parser(self, prog_name): 40 | parser = super().get_parser(prog_name) 41 | 42 | parser.add_argument('--name', help='TLD NAME') 43 | 44 | parser.add_argument('--description', help='TLD Description') 45 | 46 | common.add_all_common_options(parser) 47 | 48 | return parser 49 | 50 | def take_action(self, parsed_args): 51 | client = self.app.client_manager.dns 52 | common.set_all_common_headers(client, parsed_args) 53 | 54 | data = get_all(client.tlds.list) 55 | 56 | cols = self.columns 57 | return cols, (utils.get_item_properties(s, cols) for s in data) 58 | 59 | 60 | class ShowTLDCommand(command.ShowOne): 61 | """Show tld details""" 62 | 63 | def get_parser(self, prog_name): 64 | parser = super().get_parser(prog_name) 65 | 66 | parser.add_argument('id', help='TLD name or ID') 67 | 68 | common.add_all_common_options(parser) 69 | 70 | return parser 71 | 72 | def take_action(self, parsed_args): 73 | client = self.app.client_manager.dns 74 | common.set_all_common_headers(client, parsed_args) 75 | data = client.tlds.get(parsed_args.id) 76 | _format_tld(data) 77 | return zip(*sorted(data.items())) 78 | 79 | 80 | class CreateTLDCommand(command.ShowOne): 81 | """Create new tld""" 82 | 83 | def get_parser(self, prog_name): 84 | parser = super().get_parser(prog_name) 85 | 86 | parser.add_argument('--name', help='TLD Name', required=True) 87 | parser.add_argument('--description', help='Description') 88 | 89 | common.add_all_common_options(parser) 90 | 91 | return parser 92 | 93 | def take_action(self, parsed_args): 94 | client = self.app.client_manager.dns 95 | common.set_all_common_headers(client, parsed_args) 96 | data = client.tlds.create(parsed_args.name, parsed_args.description) 97 | _format_tld(data) 98 | return zip(*sorted(data.items())) 99 | 100 | 101 | class SetTLDCommand(command.ShowOne): 102 | """Set tld properties""" 103 | 104 | def get_parser(self, prog_name): 105 | parser = super().get_parser(prog_name) 106 | 107 | parser.add_argument('id', help='TLD name or ID') 108 | parser.add_argument('--name', help='TLD Name') 109 | description_group = parser.add_mutually_exclusive_group() 110 | description_group.add_argument('--description', help='Description') 111 | description_group.add_argument('--no-description', action='store_true') 112 | 113 | common.add_all_common_options(parser) 114 | 115 | return parser 116 | 117 | def take_action(self, parsed_args): 118 | data = {} 119 | 120 | if parsed_args.name: 121 | data['name'] = parsed_args.name 122 | 123 | if parsed_args.no_description: 124 | data['description'] = None 125 | elif parsed_args.description: 126 | data['description'] = parsed_args.description 127 | 128 | client = self.app.client_manager.dns 129 | common.set_all_common_headers(client, parsed_args) 130 | 131 | data = client.tlds.update(parsed_args.id, data) 132 | _format_tld(data) 133 | return zip(*sorted(data.items())) 134 | 135 | 136 | class DeleteTLDCommand(command.Command): 137 | """Delete tld""" 138 | 139 | def get_parser(self, prog_name): 140 | parser = super().get_parser(prog_name) 141 | 142 | parser.add_argument('id', help='TLD name or ID') 143 | 144 | common.add_all_common_options(parser) 145 | 146 | return parser 147 | 148 | def take_action(self, parsed_args): 149 | client = self.app.client_manager.dns 150 | common.set_all_common_headers(client, parsed_args) 151 | client.tlds.delete(parsed_args.id) 152 | 153 | LOG.info('TLD %s was deleted', parsed_args.id) 154 | -------------------------------------------------------------------------------- /designateclient/v2/cli/tsigkeys.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 SAP SE 2 | # 3 | # Author: Rudolf Vriend 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import logging 18 | 19 | from osc_lib.command import command 20 | 21 | from designateclient import utils 22 | from designateclient.v2.cli import common 23 | from designateclient.v2.utils import get_all 24 | 25 | LOG = logging.getLogger(__name__) 26 | 27 | 28 | def _format_tsigkey(tsigkey): 29 | # Remove unneeded fields for display output formatting 30 | tsigkey.pop('links', None) 31 | 32 | 33 | class ListTSIGKeysCommand(command.Lister): 34 | """List tsigkeys""" 35 | 36 | columns = ['id', 'name', 'algorithm', 'secret', 'scope', 'resource_id'] 37 | 38 | def get_parser(self, prog_name): 39 | parser = super().get_parser(prog_name) 40 | 41 | parser.add_argument('--name', help='TSIGKey NAME', required=False) 42 | parser.add_argument('--algorithm', help='TSIGKey algorithm', 43 | required=False) 44 | parser.add_argument('--scope', help='TSIGKey scope', required=False) 45 | 46 | common.add_all_common_options(parser) 47 | 48 | return parser 49 | 50 | def take_action(self, parsed_args): 51 | client = self.app.client_manager.dns 52 | common.set_all_common_headers(client, parsed_args) 53 | 54 | criterion = {} 55 | if parsed_args.name is not None: 56 | criterion['name'] = parsed_args.name 57 | if parsed_args.algorithm is not None: 58 | criterion['algorithm'] = parsed_args.algorithm 59 | if parsed_args.scope is not None: 60 | criterion['scope'] = parsed_args.scope 61 | 62 | data = get_all(client.tsigkeys.list, criterion) 63 | 64 | cols = self.columns 65 | return cols, (utils.get_item_properties(s, cols) for s in data) 66 | 67 | 68 | class ShowTSIGKeyCommand(command.ShowOne): 69 | """Show tsigkey details""" 70 | 71 | def get_parser(self, prog_name): 72 | parser = super().get_parser(prog_name) 73 | 74 | parser.add_argument('id', help='TSIGKey ID') 75 | 76 | common.add_all_common_options(parser) 77 | 78 | return parser 79 | 80 | def take_action(self, parsed_args): 81 | client = self.app.client_manager.dns 82 | common.set_all_common_headers(client, parsed_args) 83 | data = client.tsigkeys.get(parsed_args.id) 84 | _format_tsigkey(data) 85 | return zip(*sorted(data.items())) 86 | 87 | 88 | class CreateTSIGKeyCommand(command.ShowOne): 89 | """Create new tsigkey""" 90 | 91 | def get_parser(self, prog_name): 92 | parser = super().get_parser(prog_name) 93 | 94 | parser.add_argument('--name', help='TSIGKey Name', required=True) 95 | parser.add_argument('--algorithm', help='TSIGKey algorithm', 96 | required=True) 97 | parser.add_argument('--secret', help='TSIGKey secret', required=True) 98 | parser.add_argument('--scope', help='TSIGKey scope', required=True) 99 | parser.add_argument('--resource-id', help='TSIGKey resource_id', 100 | required=True) 101 | 102 | common.add_all_common_options(parser) 103 | 104 | return parser 105 | 106 | def take_action(self, parsed_args): 107 | client = self.app.client_manager.dns 108 | common.set_all_common_headers(client, parsed_args) 109 | data = client.tsigkeys.create(parsed_args.name, parsed_args.algorithm, 110 | parsed_args.secret, parsed_args.scope, 111 | parsed_args.resource_id) 112 | _format_tsigkey(data) 113 | return zip(*sorted(data.items())) 114 | 115 | 116 | class SetTSIGKeyCommand(command.ShowOne): 117 | """Set tsigkey properties""" 118 | 119 | def get_parser(self, prog_name): 120 | parser = super().get_parser(prog_name) 121 | 122 | parser.add_argument('id', help='TSIGKey ID') 123 | parser.add_argument('--name', help='TSIGKey Name') 124 | parser.add_argument('--algorithm', help='TSIGKey algorithm') 125 | parser.add_argument('--secret', help='TSIGKey secret') 126 | parser.add_argument('--scope', help='TSIGKey scope') 127 | 128 | common.add_all_common_options(parser) 129 | 130 | return parser 131 | 132 | def take_action(self, parsed_args): 133 | data = {} 134 | 135 | if parsed_args.name: 136 | data['name'] = parsed_args.name 137 | if parsed_args.algorithm: 138 | data['algorithm'] = parsed_args.algorithm 139 | if parsed_args.secret: 140 | data['secret'] = parsed_args.secret 141 | if parsed_args.scope: 142 | data['scope'] = parsed_args.scope 143 | 144 | client = self.app.client_manager.dns 145 | common.set_all_common_headers(client, parsed_args) 146 | 147 | data = client.tsigkeys.update(parsed_args.id, data) 148 | _format_tsigkey(data) 149 | return zip(*sorted(data.items())) 150 | 151 | 152 | class DeleteTSIGKeyCommand(command.Command): 153 | """Delete tsigkey""" 154 | 155 | def get_parser(self, prog_name): 156 | parser = super().get_parser(prog_name) 157 | 158 | parser.add_argument('id', help='TSIGKey ID') 159 | 160 | common.add_all_common_options(parser) 161 | 162 | return parser 163 | 164 | def take_action(self, parsed_args): 165 | client = self.app.client_manager.dns 166 | common.set_all_common_headers(client, parsed_args) 167 | client.tsigkeys.delete(parsed_args.id) 168 | 169 | LOG.info('TSIGKey %s was deleted', parsed_args.id) 170 | -------------------------------------------------------------------------------- /designateclient/v2/limits.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | from designateclient.v2.base import V2Controller 17 | 18 | 19 | class LimitController(V2Controller): 20 | def get(self): 21 | return self._get('/limits') 22 | -------------------------------------------------------------------------------- /designateclient/v2/nameservers.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | from designateclient.v2.base import V2Controller 17 | from designateclient.v2 import utils as v2_utils 18 | 19 | 20 | class NameServerController(V2Controller): 21 | def list(self, zone): 22 | zone = v2_utils.resolve_by_name(self.client.zones.list, zone) 23 | 24 | url = f'/zones/{zone}/nameservers' 25 | 26 | return self._get(url, response_key='nameservers') 27 | -------------------------------------------------------------------------------- /designateclient/v2/pools.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from designateclient.v2.base import V2Controller 17 | 18 | 19 | class PoolController(V2Controller): 20 | def list(self): 21 | url = '/pools' 22 | return self._get(url, response_key='pools') 23 | -------------------------------------------------------------------------------- /designateclient/v2/quotas.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | from designateclient.v2.base import V2Controller 17 | 18 | 19 | class QuotasController(V2Controller): 20 | def list(self, project_id): 21 | return self._get(f'/quotas/{project_id}') 22 | 23 | def update(self, project_id, values): 24 | return self._patch(f'/quotas/{project_id}', data=values) 25 | 26 | def reset(self, project_id): 27 | return self._delete(f'/quotas/{project_id}') 28 | -------------------------------------------------------------------------------- /designateclient/v2/recordsets.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | from oslo_utils import uuidutils 17 | 18 | from designateclient.v2.base import V2Controller 19 | from designateclient.v2 import utils as v2_utils 20 | 21 | 22 | class RecordSetController(V2Controller): 23 | def _canonicalize_record_name(self, zone, name): 24 | zone_info = None 25 | 26 | # If we get a zone name we'll need to get the ID of it before POST. 27 | if (isinstance(zone, str) and not 28 | uuidutils.is_uuid_like(zone)): 29 | zone_info = self.client.zones.get(zone) 30 | elif isinstance(zone, dict): 31 | zone_info = zone 32 | 33 | # We where given a name like "www" vs www.i.io., attempt to fix it on 34 | # the behalf of the actor. 35 | if not name.endswith('.'): 36 | if not isinstance(zone_info, dict): 37 | zone_info = self.client.zones.get(zone) 38 | 39 | name = '{}.{}'.format(name, zone_info['name']) 40 | 41 | return name, zone_info 42 | 43 | def create(self, zone, name, type_, records, description=None, 44 | ttl=None): 45 | name, zone_info = self._canonicalize_record_name(zone, name) 46 | 47 | data = { 48 | 'name': name, 49 | 'type': type_, 50 | 'records': records 51 | } 52 | 53 | if ttl is not None: 54 | data['ttl'] = ttl 55 | 56 | if description is not None: 57 | data['description'] = description 58 | 59 | if zone_info is not None: 60 | zone_id = zone_info['id'] 61 | else: 62 | zone_id = zone 63 | 64 | return self._post(f'/zones/{zone_id}/recordsets', data=data) 65 | 66 | def list(self, zone, criterion=None, marker=None, limit=None): 67 | zone = v2_utils.resolve_by_name(self.client.zones.list, zone) 68 | 69 | url = self.build_url( 70 | f'/zones/{zone}/recordsets', criterion, marker, limit 71 | ) 72 | 73 | return self._get(url, response_key='recordsets') 74 | 75 | def list_all_zones(self, criterion=None, marker=None, limit=None): 76 | url = self.build_url('/recordsets', criterion, marker, limit) 77 | 78 | return self._get(url, response_key='recordsets') 79 | 80 | def get(self, zone, recordset): 81 | zone = v2_utils.resolve_by_name(self.client.zones.list, zone) 82 | recordset = v2_utils.resolve_by_name(self.list, recordset, zone) 83 | 84 | url = self.build_url(f'/zones/{zone}/recordsets/{recordset}') 85 | 86 | return self._get(url) 87 | 88 | def update(self, zone, recordset, values): 89 | zone = v2_utils.resolve_by_name(self.client.zones.list, zone) 90 | recordset = v2_utils.resolve_by_name(self.list, recordset, zone) 91 | 92 | url = f'/zones/{zone}/recordsets/{recordset}' 93 | 94 | return self._put(url, data=values) 95 | 96 | def delete(self, zone, recordset): 97 | zone = v2_utils.resolve_by_name(self.client.zones.list, zone) 98 | recordset = v2_utils.resolve_by_name(self.list, recordset, zone) 99 | 100 | url = f'/zones/{zone}/recordsets/{recordset}' 101 | 102 | return self._delete(url) 103 | -------------------------------------------------------------------------------- /designateclient/v2/reverse.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | from designateclient.v2.base import V2Controller 17 | 18 | 19 | class FloatingIPController(V2Controller): 20 | def set(self, floatingip_id, ptrdname, description=None, ttl=None): 21 | data = { 22 | 'ptrdname': ptrdname 23 | } 24 | 25 | if description is not None: 26 | data['description'] = description 27 | 28 | if ttl is not None: 29 | data['ttl'] = ttl 30 | 31 | url = f'/reverse/floatingips/{floatingip_id}' 32 | return self._patch(url, data=data) 33 | 34 | def list(self, criterion=None): 35 | url = self.build_url('/reverse/floatingips', criterion) 36 | 37 | return self._get(url, response_key='floatingips') 38 | 39 | def get(self, floatingip_id): 40 | url = f'/reverse/floatingips/{floatingip_id}' 41 | 42 | return self._get(url) 43 | 44 | def unset(self, floatingip_id): 45 | data = {'ptrdname': None} 46 | 47 | url = f'/reverse/floatingips/{floatingip_id}' 48 | 49 | return self._patch(url, data=data) 50 | -------------------------------------------------------------------------------- /designateclient/v2/service_statuses.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Hewlett Packard Enterprise Development Company LP 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | from designateclient.v2 import base 17 | 18 | 19 | class ServiceStatusesController(base.V2Controller): 20 | def list(self, criterion=None, marker=None, limit=None): 21 | url = self.build_url('/service_statuses', criterion, marker, limit) 22 | 23 | return self._get(url, response_key='service_statuses') 24 | 25 | def get(self, service_status_id): 26 | url = f'/service_statuses/{service_status_id}' 27 | 28 | return self._get(url) 29 | -------------------------------------------------------------------------------- /designateclient/v2/tlds.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | from designateclient.v2.base import V2Controller 17 | from designateclient.v2 import utils as v2_utils 18 | 19 | 20 | class TLDController(V2Controller): 21 | def create(self, name, description=None): 22 | data = { 23 | 'name': name, 24 | } 25 | 26 | if description is not None: 27 | data['description'] = description 28 | 29 | return self._post('/tlds', data=data) 30 | 31 | def list(self, criterion=None, marker=None, limit=None): 32 | url = self.build_url('/tlds', criterion, marker, limit) 33 | 34 | return self._get(url, response_key='tlds') 35 | 36 | def get(self, tld): 37 | tld = v2_utils.resolve_by_name(self.list, tld) 38 | 39 | return self._get(f'/tlds/{tld}') 40 | 41 | def update(self, tld, values): 42 | tld = v2_utils.resolve_by_name(self.list, tld) 43 | 44 | return self._patch(f'/tlds/{tld}', data=values) 45 | 46 | def delete(self, tld): 47 | tld = v2_utils.resolve_by_name(self.list, tld) 48 | 49 | return self._delete(f'/tlds/{tld}') 50 | -------------------------------------------------------------------------------- /designateclient/v2/tsigkeys.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 SAP SE 2 | # 3 | # Author: Rudolf Vriend 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | from designateclient.v2.base import V2Controller 17 | from designateclient.v2 import utils as v2_utils 18 | 19 | 20 | class TSIGKeysController(V2Controller): 21 | def create(self, name, algorithm, secret, scope, resource_id): 22 | data = { 23 | 'name': name, 24 | 'algorithm': algorithm, 25 | 'secret': secret, 26 | 'scope': scope, 27 | 'resource_id': resource_id 28 | } 29 | 30 | return self._post('/tsigkeys', data=data) 31 | 32 | def list(self, criterion=None, marker=None, limit=None): 33 | url = self.build_url('/tsigkeys', criterion, marker, limit) 34 | 35 | return self._get(url, response_key='tsigkeys') 36 | 37 | def get(self, tsigkey): 38 | tsigkey = v2_utils.resolve_by_name(self.list, tsigkey) 39 | 40 | return self._get(f'/tsigkeys/{tsigkey}') 41 | 42 | def update(self, tsigkey, values): 43 | tsigkey = v2_utils.resolve_by_name(self.list, tsigkey) 44 | 45 | return self._patch(f'/tsigkeys/{tsigkey}', data=values) 46 | 47 | def delete(self, tsigkey): 48 | tsigkey = v2_utils.resolve_by_name(self.list, tsigkey) 49 | 50 | return self._delete(f'/tsigkeys/{tsigkey}') 51 | -------------------------------------------------------------------------------- /designateclient/v2/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Author: Endre Karlson 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from oslo_utils import uuidutils 18 | from urllib.parse import parse_qs 19 | from urllib.parse import urlparse 20 | 21 | from designateclient import exceptions 22 | 23 | 24 | def resolve_by_name(func, name, *args): 25 | """ 26 | Helper to resolve a "name" a'la foo.com to it's ID by using REST api's 27 | query support and filtering on name. 28 | """ 29 | if uuidutils.is_uuid_like(name): 30 | return name 31 | 32 | results = func(criterion={"name": f"{name}"}, *args) 33 | length = len(results) 34 | 35 | if length == 1: 36 | return results[0]["id"] 37 | elif length == 0: 38 | raise exceptions.NotFound(f"Name {name} didn't resolve") 39 | else: 40 | raise exceptions.NoUniqueMatch( 41 | "Multiple matches found for {name}, please use ID instead." 42 | ) 43 | 44 | 45 | def parse_query_from_url(url): 46 | """ 47 | Helper to get key bits of data from the "next" url returned 48 | from the API on collections 49 | :param url: 50 | :return: dict 51 | """ 52 | values = parse_qs(urlparse(url)[4]) 53 | return {k: values[k][0] for k in values.keys()} 54 | 55 | 56 | def get_all(function, criterion=None, args=None): 57 | """ 58 | 59 | :param function: Function to be called to get data 60 | :param criterion: dict of filters to be applied 61 | :param args: arguments to be given to the function 62 | :return: DesignateList() 63 | """ 64 | 65 | criterion = criterion or {} 66 | args = args or [] 67 | 68 | data = function(*args, criterion=criterion) 69 | returned_data = data 70 | while True: 71 | if data.next_page: 72 | for k, v in data.next_link_criterion.items(): 73 | criterion[k] = v 74 | data = function(*args, criterion=criterion) 75 | returned_data.extend(data) 76 | else: 77 | break 78 | 79 | return returned_data 80 | -------------------------------------------------------------------------------- /designateclient/version.py: -------------------------------------------------------------------------------- 1 | # Copyright 2012 Managed I.T. 2 | # 3 | # Author: Kiall Mac Innes 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | import pbr.version 17 | 18 | version_info = pbr.version.VersionInfo('python-designateclient') 19 | -------------------------------------------------------------------------------- /doc/examples/recordset_create.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | from designateclient.v2 import client 5 | from designateclient import exceptions 6 | 7 | from keystoneauth1.identity import generic 8 | from keystoneauth1 import session as keystone_session 9 | 10 | 11 | logging.basicConfig(level='DEBUG') 12 | 13 | """ 14 | Example script to create or get a domain and add some records to it. 15 | """ 16 | 17 | 18 | auth = generic.Password( 19 | auth_url=os.environ.get('OS_AUTH_URL'), 20 | username=os.environ.get('OS_USERNAME'), 21 | password=os.environ.get('OS_PASSWORD'), 22 | project_name=os.environ.get('OS_PROJECT_NAME'), 23 | project_domain_id='default', 24 | user_domain_id='default') 25 | 26 | session = keystone_session.Session(auth=auth) 27 | 28 | client = client.Client(session=session) 29 | 30 | 31 | try: 32 | zone = client.zones.create('i.io.', email='i@i.io') 33 | except exceptions.RemoteError: 34 | zone = {z['name']: z for z in client.zones.list()}['i.io.'] 35 | 36 | print("Recordset list...") 37 | for rs in client.recordsets.list(zone['id']): 38 | print(rs) 39 | 40 | # Here's an example of just passing "www" as the record name vs "www.i.io." 41 | records = ["10.0.0.1"] 42 | rs = client.recordsets.create(zone['id'], 'www', 'A', records) 43 | 44 | # Here we're replacing the records with new ones 45 | records = ["10.0.0.1", "10.0.0.5"] 46 | client.recordsets.update(zone['id'], rs['id'], {'records': records}) 47 | -------------------------------------------------------------------------------- /doc/examples/recordset_crud.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | from designateclient.v2 import client 5 | from designateclient import exceptions 6 | 7 | from keystoneauth1.identity import generic 8 | from keystoneauth1 import session as keystone_session 9 | 10 | 11 | logging.basicConfig(level='DEBUG') 12 | 13 | auth = generic.Password( 14 | auth_url=os.environ.get('OS_AUTH_URL'), 15 | username=os.environ.get('OS_USERNAME'), 16 | password=os.environ.get('OS_PASSWORD'), 17 | project_name=os.environ.get('OS_PROJECT_NAME'), 18 | project_domain_id='default', 19 | user_domain_id='default') 20 | 21 | session = keystone_session.Session(auth=auth) 22 | 23 | client = client.Client(session=session) 24 | 25 | 26 | try: 27 | zone = {z['name']: z for z in client.zones.list()}['i.io.'] 28 | client.zones.delete(zone['id']) 29 | except exceptions.NotFound: 30 | pass 31 | 32 | zone = client.zones.create(name='i.io.', email='i@i.io') 33 | 34 | # Clean all recordsets first in this zone (for sanity sake) 35 | for rrset in client.recordsets.list(zone['id']): 36 | if rrset['type'] in ('NS', 'SOA'): 37 | continue 38 | client.recordsets.delete(zone['id'], rrset['id']) 39 | 40 | # Make some A records 41 | www = client.recordsets.create( 42 | zone['id'], 43 | 'www.{}'.format(zone['name']), 44 | 'A', 45 | ['10.0.0.1']) 46 | 47 | values = { 48 | 'records': ['10.0.1.1', '10.0.0.2'] 49 | } 50 | 51 | client.recordsets.update(zone['id'], www['id'], values) 52 | 53 | cname = client.recordsets.create( 54 | zone['id'], 55 | 'my-site.{}'.format(zone['name']), 56 | 'CNAME', 57 | [www['name']]) 58 | 59 | # Now let's do some Mailserver examples 60 | 61 | # First create the A record 62 | mail1 = client.recordsets.create( 63 | zone['id'], 'mail1.' + zone['name'], 'A', ["10.0.0.11"]) 64 | 65 | mail2 = client.recordsets.create( 66 | zone['id'], 'mail2.' + zone['name'], 'A', ["10.0.0.12"]) 67 | 68 | # Create the MX records - it will be 1 recordset with multiple records pointing 69 | # to the A records we created above 70 | mx_rrset = client.recordsets.create( 71 | zone['id'], zone['name'], 'MX', 72 | ['0 ' + mail1['name'], '5 ' + mail2['name']]) 73 | 74 | print(zone['id']) 75 | -------------------------------------------------------------------------------- /doc/examples/zone_create_primary.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | from designateclient import exceptions 5 | from designateclient.v2 import client 6 | 7 | from keystoneauth1.identity import generic 8 | from keystoneauth1 import session as keystone_session 9 | 10 | 11 | logging.basicConfig(level='DEBUG') 12 | 13 | auth = generic.Password( 14 | auth_url=os.environ.get('OS_AUTH_URL'), 15 | username=os.environ.get('OS_USERNAME'), 16 | password=os.environ.get('OS_PASSWORD'), 17 | project_name=os.environ.get('OS_PROJECT_NAME'), 18 | project_domain_id='default', 19 | user_domain_id='default') 20 | 21 | session = keystone_session.Session(auth=auth) 22 | 23 | client = client.Client(session=session) 24 | 25 | 26 | try: 27 | zone = client.zones.create('i.io.', email='i@i.io') 28 | except exceptions.RemoteError: 29 | zone = {z['name']: z for z in client.zones.list()}['i.io.'] 30 | 31 | print(client.recordsets.list(zone['id'])) 32 | -------------------------------------------------------------------------------- /doc/examples/zone_create_secondary.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import uuid 4 | 5 | from keystoneauth1.identity import generic 6 | from keystoneauth1 import session as keystone_session 7 | 8 | from designateclient.v2 import client 9 | 10 | 11 | logging.basicConfig(level='DEBUG') 12 | 13 | auth = generic.Password( 14 | auth_url=os.environ.get('OS_AUTH_URL'), 15 | username=os.environ.get('OS_USERNAME'), 16 | password=os.environ.get('OS_PASSWORD'), 17 | project_name=os.environ.get('OS_PROJECT_NAME'), 18 | project_domain_id='default', 19 | user_domain_id='default') 20 | 21 | session = keystone_session.Session(auth=auth) 22 | 23 | client = client.Client(session=session) 24 | 25 | # Primary Zone 26 | primary = client.zones.create( 27 | f'primary-{str(uuid.uuid4())}.io.', 28 | 'PRIMARY', 29 | 'root@x.com') 30 | 31 | # Secondary Zone 32 | slave = client.zones.create( 33 | f'secondary-{str(uuid.uuid4())}.io.', 34 | 'SECONDARY', 35 | masters=["127.0.1.1"]) 36 | 37 | # Try updating Masters for the Secondary 38 | new_slave = client.zones.update( 39 | slave['id'], 40 | {"masters": ["10.0.0.1", "10.0.0.10"]} 41 | ) 42 | 43 | # List all Zones 44 | zones = client.zones.list() 45 | -------------------------------------------------------------------------------- /doc/examples/zone_list_nameservers.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import uuid 4 | 5 | from designateclient.v2 import client 6 | 7 | from keystoneauth1.identity import generic 8 | from keystoneauth1 import session as keystone_session 9 | 10 | 11 | logging.basicConfig(level='DEBUG') 12 | 13 | auth = generic.Password( 14 | auth_url=os.environ.get('OS_AUTH_URL'), 15 | username=os.environ.get('OS_USERNAME'), 16 | password=os.environ.get('OS_PASSWORD'), 17 | project_name=os.environ.get('OS_PROJECT_NAME'), 18 | project_domain_id='default', 19 | user_domain_id='default') 20 | 21 | session = keystone_session.Session(auth=auth) 22 | 23 | client = client.Client(session=session) 24 | 25 | zone = client.zones.create( 26 | f'primary-{str(uuid.uuid4())}.io.', 27 | 'PRIMARY', 28 | 'root@x.com') 29 | 30 | client.nameservers.list(zone['id']) 31 | -------------------------------------------------------------------------------- /doc/examples/zone_list_paging.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | from keystoneauth1.identity import generic 5 | from keystoneauth1 import session as keystone_session 6 | 7 | from designateclient.v2 import client 8 | 9 | logging.basicConfig(level='DEBUG') 10 | 11 | auth = generic.Password( 12 | auth_url=os.environ.get('OS_AUTH_URL'), 13 | username=os.environ.get('OS_USERNAME'), 14 | password=os.environ.get('OS_PASSWORD'), 15 | project_name=os.environ.get('OS_PROJECT_NAME'), 16 | project_domain_id='default', 17 | user_domain_id='default') 18 | 19 | session = keystone_session.Session(auth=auth, timeout=10) 20 | 21 | client = client.Client(session=session) 22 | 23 | 24 | pages = [] 25 | 26 | fetch = 1 27 | while fetch: 28 | kw = {'limit': 3} 29 | if pages: 30 | # marker is the latest page with the last item. 31 | kw['marker'] = pages[-1][-1]['id'] 32 | page = client.zones.list(**kw) 33 | if not page: 34 | break 35 | pages.append(page) 36 | 37 | for page in pages: 38 | print(page) 39 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx>=2.0.0,!=2.1.0 # BSD 2 | reno>=3.1.0 # Apache-2.0 3 | openstackdocstheme>=2.2.1 # Apache-2.0 4 | sphinxcontrib-apidoc>=0.2.0 # BSD 5 | -------------------------------------------------------------------------------- /doc/source/cli/index.rst: -------------------------------------------------------------------------------- 1 | Designate OpenStack Client Command Reference 2 | ============================================ 3 | 4 | List of released CLI commands available in OpenStack client. These commands 5 | can be referenced by doing ``openstack help ``. 6 | 7 | ======================== 8 | Managing the DNS Service 9 | ======================== 10 | 11 | .. autoprogram-cliff:: openstack.dns.v2 12 | :command: dns * 13 | 14 | =============== 15 | Pointer Records 16 | =============== 17 | 18 | .. autoprogram-cliff:: openstack.dns.v2 19 | :command: ptr * 20 | 21 | =========== 22 | Record Sets 23 | =========== 24 | 25 | .. autoprogram-cliff:: openstack.dns.v2 26 | :command: recordset * 27 | 28 | ================= 29 | Top Level Domains 30 | ================= 31 | 32 | .. autoprogram-cliff:: openstack.dns.v2 33 | :command: tld * 34 | 35 | ========================== 36 | Transaction Signature Keys 37 | ========================== 38 | 39 | .. autoprogram-cliff:: openstack.dns.v2 40 | :command: tsigkey * 41 | 42 | ============== 43 | Managing Zones 44 | ============== 45 | 46 | .. autoprogram-cliff:: openstack.dns.v2 47 | :command: zone * 48 | -------------------------------------------------------------------------------- /doc/source/conf.py: -------------------------------------------------------------------------------- 1 | # 2 | # designateclient documentation build configuration file 3 | 4 | # -- General configuration ----------------------------------------------------- 5 | 6 | # Add any Sphinx extension module names here, as strings. They can be extensions 7 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 8 | extensions = [ 9 | 'sphinx.ext.autodoc', 10 | 'sphinx.ext.viewcode', 11 | 'sphinxcontrib.apidoc', 12 | 'openstackdocstheme', 13 | 'cliff.sphinxext'] 14 | 15 | # openstackdocstheme options 16 | openstackdocs_repo_name = 'openstack/python-designateclient' 17 | openstackdocs_bug_project = 'python-designateclient' 18 | openstackdocs_bug_tag = '' 19 | html_theme = 'openstackdocs' 20 | 21 | apidoc_module_dir = '../../designateclient' 22 | apidoc_output_dir = 'reference/api' 23 | apidoc_excluded_paths = [ 'tests/*', 'functionaltests/*', 'hacking/*' ] 24 | apidoc_separate_modules = True 25 | 26 | autodoc_exclude_modules = [ 27 | 'designateclient.tests.*', 28 | 'designateclient.functionaltests.*', 29 | 'designateclient.hacking.*'] 30 | 31 | # Add any paths that contain templates here, relative to this directory. 32 | templates_path = ['_templates'] 33 | 34 | # The suffix of source filenames. 35 | source_suffix = '.rst' 36 | 37 | # The master toctree document. 38 | master_doc = 'index' 39 | 40 | # General information about the project. 41 | copyright = '2012, Managed I.T. 2013-2014, Hewlett-Packard Development Company, L.P.' 42 | 43 | # List of patterns, relative to source directory, that match files and 44 | # directories to ignore when looking for source files. 45 | exclude_patterns = [] 46 | 47 | # The name of the Pygments (syntax highlighting) style to use. 48 | pygments_style = 'native' 49 | 50 | # A list of ignored prefixes for module index sorting. 51 | modindex_common_prefix = ['designateclient'] 52 | 53 | 54 | # -- Options for HTML output --------------------------------------------------- 55 | 56 | # Output file base name for HTML help builder. 57 | htmlhelp_basename = 'designateclientdoc' 58 | -------------------------------------------------------------------------------- /doc/source/contributor/contributing.rst: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Code is hosted `on GitHub`_. Submit bugs to the Designate Client project on 5 | `Launchpad`_. Submit code to the openstack/python-designateclient project using 6 | `Gerrit`_. 7 | 8 | .. _on GitHub: https://github.com/openstack/python-designateclient 9 | .. _Launchpad: https://launchpad.net/python-designateclient 10 | .. _Gerrit: https://docs.openstack.org/infra/manual/developers.html#development-workflow 11 | 12 | Here's a quick summary: 13 | 14 | Install the git-review package to make life easier 15 | 16 | .. code-block:: shell-session 17 | 18 | pip install git-review 19 | 20 | Branch, work, & submit: 21 | 22 | .. code-block:: shell-session 23 | 24 | # cut a new branch, tracking master 25 | git checkout --track -b bug/id origin/master 26 | # work work work 27 | git add stuff 28 | git commit 29 | # rebase/squash to a single commit before submitting 30 | git rebase -i 31 | # submit 32 | git-review 33 | -------------------------------------------------------------------------------- /doc/source/contributor/functional-tests.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | Functional Tests 3 | ================ 4 | 5 | The functional tests invoke the client executable to see that it actually works 6 | with a running Designate. WARNING: these tests will create and delete zones, 7 | recordsets, and other resources in Designate. 8 | 9 | Installation 10 | ------------ 11 | 12 | .. code-block:: shell-session 13 | 14 | cd python-designateclient 15 | pip install python-openstackclient 16 | pip install -r requirements.txt -r test-requirements.txt 17 | pip install -e . 18 | 19 | Configuration 20 | ------------- 21 | 22 | The functional tests look for a variable ``TEMPEST_CONFIG`` which specifies a 23 | config file for the test. 24 | 25 | .. code-block:: shell-session 26 | 27 | export TEMPEST_CONFIG=tempest.conf 28 | 29 | The tests will use Keystone to grab the Designate endpoint to test against. 30 | They need at least three users (two regular users, and one admin) for all the 31 | tests to run. 32 | 33 | .. code-block:: shell-session 34 | 35 | [identity] 36 | uri = http://localhost:5000/v2.0 37 | uri_v3 = http://localhost:5000/v3 38 | auth_version = v2 39 | region = RegionOne 40 | 41 | username = demo 42 | tenant_name = demo 43 | password = password 44 | domain_name = Default 45 | 46 | alt_username = alt_demo 47 | alt_tenant_name = alt_demo 48 | alt_password = password 49 | alt_domain_name = Default 50 | 51 | admin_username = admin 52 | admin_tenant_name = admin 53 | admin_password = password 54 | admin_domain_name = Default 55 | 56 | [designateclient] 57 | # the directory containing the openstack executable 58 | directory=/root/python-designateclient/.venv/bin 59 | 60 | Running the tests 61 | ----------------- 62 | 63 | The functional tests are run with tox (installed with ``pip install tox``): 64 | 65 | .. code-block:: shell-session 66 | 67 | tox -e functional 68 | -------------------------------------------------------------------------------- /doc/source/contributor/index.rst: -------------------------------------------------------------------------------- 1 | ==================== 2 | Contributors Guide 3 | ==================== 4 | 5 | .. toctree:: 6 | 7 | contributing 8 | functional-tests 9 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | ====================== 2 | python-designateclient 3 | ====================== 4 | 5 | python-designateclient provides python bindings and command line tools for the 6 | Designate v2 APIs. 7 | 8 | The :ref:`Python API bindings ` are provided by the 9 | :program:`designateclient` module. 10 | 11 | The designate plugin can be used via the :program:`openstack` command line tool. 12 | More information can be 13 | found on the :ref:`designate command line tool page `. 14 | 15 | You'll need credentials for an OpenStack cloud that implements the Designate 16 | API in order to use the client. 17 | 18 | .. toctree:: 19 | :maxdepth: 1 20 | 21 | install/index 22 | user/index 23 | cli/index 24 | contributor/index 25 | reference/index 26 | 27 | .. rubric:: Indices and tables 28 | 29 | * :ref:`genindex` 30 | * :ref:`modindex` 31 | * :ref:`search` 32 | -------------------------------------------------------------------------------- /doc/source/install/index.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | Install the client from PyPI 6 | ---------------------------- 7 | The :program:`python-designateclient` package is published on `PyPI`_ and so can be installed using the pip tool, which will manage installing all 8 | python dependencies: 9 | 10 | .. code-block:: shell-session 11 | 12 | pip install python-designateclient 13 | 14 | *Warning: the packages on PyPI may lag behind the git repo in functionality.* 15 | 16 | Setup the client from source 17 | ---------------------------- 18 | If you want the latest version, straight from github: 19 | 20 | .. code-block:: shell-session 21 | 22 | git clone git@github.com:openstack/python-designateclient.git 23 | cd python-designateclient 24 | virtualenv .venv 25 | . .venv/bin/activate 26 | pip install -r requirements.txt -r test-requirements.txt 27 | python setup.py install 28 | 29 | Setup the client in development mode 30 | ------------------------------------ 31 | 32 | 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" 33 | step. You can find out more about `Development Mode`_ 34 | 35 | .. code-block:: shell-session 36 | 37 | git clone git@github.com:openstack/python-designateclient.git 38 | cd python-designateclient 39 | virtualenv .venv 40 | . .venv/bin/activate 41 | pip install -r requirements.txt -r test-requirements.txt 42 | python setup.py develop 43 | 44 | .. _Development Mode: https://setuptools.readthedocs.io/en/latest/setuptools.html#development-mode 45 | .. _PyPI: https://pypi.org/project/python-designateclient/ 46 | -------------------------------------------------------------------------------- /doc/source/reference/index.rst: -------------------------------------------------------------------------------- 1 | ======================================== 2 | python-designateclient Package Reference 3 | ======================================== 4 | 5 | .. toctree:: 6 | :maxdepth: 4 7 | 8 | api/modules 9 | -------------------------------------------------------------------------------- /doc/source/user/bindings.rst: -------------------------------------------------------------------------------- 1 | .. _bindings: 2 | 3 | =========================== 4 | Python Bindings - v2 5 | =========================== 6 | 7 | The python-designateclient package comes with python bindings 8 | the Designate API: v2. This can be used to interact with the Designate 9 | API from any python program. 10 | 11 | Introduction - Bindings v2 12 | ========================== 13 | 14 | To view examples of usage please checkout the *doc/examples* folder, basic usage is: 15 | 16 | .. code-block:: python 17 | 18 | #!/usr/bin/env python 19 | from designateclient.v2 import client 20 | from designateclient import shell 21 | 22 | from keystoneauth1.identity import generic 23 | from keystoneauth1 import session as keystone_session 24 | 25 | 26 | auth = generic.Password( 27 | auth_url=shell.env('OS_AUTH_URL'), 28 | username=shell.env('OS_USERNAME'), 29 | password=shell.env('OS_PASSWORD'), 30 | project_name=shell.env('OS_PROJECT_NAME'), 31 | project_domain_id='default', 32 | user_domain_id='default') 33 | 34 | session = keystone_session.Session(auth=auth) 35 | 36 | client = client.Client(session=session) 37 | 38 | zone = client.zones.create('i.io.', email='i@i.io') 39 | 40 | rs = client.recordsets.create(zone['id'], 'www', 'A', ['10.0.0.1']) 41 | -------------------------------------------------------------------------------- /doc/source/user/index.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | Using python-designateclient 3 | ============================== 4 | 5 | .. toctree:: 6 | 7 | bindings 8 | shell-v2 9 | -------------------------------------------------------------------------------- /python-designateclient.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "file_exclude_patterns": 6 | [ 7 | "*.pyc", 8 | "*.pyo", 9 | "*.exe", 10 | "*.dll", 11 | "*.obj", 12 | "*.o", 13 | "*.a", 14 | "*.lib", 15 | "*.so", 16 | "*.dylib", 17 | "*.ncb", 18 | "*.sdf", 19 | "*.suo", 20 | "*.pdb", 21 | "*.idb", 22 | ".DS_Store", 23 | "*.class", 24 | "*.psd", 25 | "*.db", 26 | ".vagrant", 27 | ".noseids" 28 | ], 29 | "folder_exclude_patterns": 30 | [ 31 | ".svn", 32 | ".git", 33 | ".hg", 34 | "CVS", 35 | "*.egg", 36 | "*.egg-info", 37 | ".tox", 38 | "venv" 39 | ], 40 | "path": "." 41 | } 42 | ], 43 | "settings": 44 | { 45 | "default_line_ending": "unix", 46 | "detect_indentation": false, 47 | "ensure_newline_at_eof_on_save": true, 48 | "rulers": 49 | [ 50 | 79 51 | ], 52 | "tab_size": 4, 53 | "translate_tabs_to_spaces": true, 54 | "trim_trailing_white_space_on_save": true 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /releasenotes/notes/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/python-designateclient/1505c27dc6a8b5b092b7fff87f7d9cd7d64da154/releasenotes/notes/.placeholder -------------------------------------------------------------------------------- /releasenotes/notes/Add-shared-zones-support-4be565f3d1c6356c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - Adds zone share commands to support sharing zones with additional projects. 4 | - Adds a ``--delete-shares`` option to zone delete to delete existing zone 5 | shares along with the zone. Without this option, you cannot delete a zone 6 | that has been shared with other projects. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/add-hard-delete-option-for-zone-delete-e16652c8e72fc023.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Added option``hard-delete`` for zone delete API. This will allow user to 5 | delete zone-files on the backend when zone is deleted. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/bug-1940544-9ed7805341dec1ba.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | fixes: 4 | - | 5 | Include SECONDARY zone type to 'zone list' command output. Previously only 6 | PRIMARY zones were shown -------------------------------------------------------------------------------- /releasenotes/notes/drop-py2-c4e50d006fa4446c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Python 2.7 support has been dropped. The last release of python-designateclient 5 | to support Python 2.7 is 3.1. The minimum version of Python now supported 6 | is Python 3.6. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/drop-python-3-6-and-3-7-7d815c897c330d9c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Python 3.6 & 3.7 support has been dropped. The minimum version of Python now 5 | supported is Python 3.8. -------------------------------------------------------------------------------- /releasenotes/notes/quota-commands-7ff037bddae95771.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - added openstack dns quota command + 4 | list + set + reset sub commands 5 | 6 | All sub commands can be scoped to a project using --project-id 7 | If the --project-id does not match the current project id 8 | it will set X-Auth-All-Projects:True 9 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-api-v1-4e507128b344082b.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Removed V1 API support from the client, as it was permanently 5 | removed in the Queens release of Designate. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-py38-355959306e686af7.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Support for Python 3.8 has been removed. Now the minimum python version 5 | supported is 3.9 . 6 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-records-argument-8eda058e3bf028ca.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | The following commands no longer support the ``--records`` argument. Use 5 | the ``--record`` argument instead. 6 | 7 | - ``openstack recordset create`` 8 | - ``openstack recordset set`` 9 | -------------------------------------------------------------------------------- /releasenotes/source/2023.1.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2023.1 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: unmaintained/2023.1 7 | -------------------------------------------------------------------------------- /releasenotes/source/2023.2.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2023.2 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2023.2 7 | -------------------------------------------------------------------------------- /releasenotes/source/2024.1.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2024.1 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2024.1 7 | -------------------------------------------------------------------------------- /releasenotes/source/2024.2.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2024.2 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2024.2 7 | -------------------------------------------------------------------------------- /releasenotes/source/2025.1.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2025.1 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2025.1 7 | -------------------------------------------------------------------------------- /releasenotes/source/_static/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/python-designateclient/1505c27dc6a8b5b092b7fff87f7d9cd7d64da154/releasenotes/source/_static/.placeholder -------------------------------------------------------------------------------- /releasenotes/source/_templates/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/python-designateclient/1505c27dc6a8b5b092b7fff87f7d9cd7d64da154/releasenotes/source/_templates/.placeholder -------------------------------------------------------------------------------- /releasenotes/source/index.rst: -------------------------------------------------------------------------------- 1 | ================================ 2 | Designate Client Release Notes 3 | ================================ 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | unreleased 9 | 2025.1 10 | 2024.2 11 | 2024.1 12 | 2023.2 13 | 2023.1 14 | zed 15 | yoga 16 | xena 17 | wallaby 18 | victoria 19 | ussuri 20 | train 21 | stein 22 | rocky 23 | queens 24 | pike 25 | ocata 26 | newton 27 | -------------------------------------------------------------------------------- /releasenotes/source/newton.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Newton Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: origin/stable/newton 7 | -------------------------------------------------------------------------------- /releasenotes/source/ocata.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Ocata Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: origin/stable/ocata 7 | -------------------------------------------------------------------------------- /releasenotes/source/pike.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Pike Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/pike 7 | -------------------------------------------------------------------------------- /releasenotes/source/queens.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Queens Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/queens 7 | -------------------------------------------------------------------------------- /releasenotes/source/rocky.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Rocky Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/rocky 7 | -------------------------------------------------------------------------------- /releasenotes/source/stein.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Stein Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/stein 7 | -------------------------------------------------------------------------------- /releasenotes/source/train.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | Train Series Release Notes 3 | ========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/train 7 | -------------------------------------------------------------------------------- /releasenotes/source/unreleased.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | Current Series Release Notes 3 | ============================== 4 | 5 | .. release-notes:: -------------------------------------------------------------------------------- /releasenotes/source/ussuri.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | Ussuri Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/ussuri 7 | -------------------------------------------------------------------------------- /releasenotes/source/victoria.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | Victoria Series Release Notes 3 | ============================= 4 | 5 | .. release-notes:: 6 | :branch: unmaintained/victoria 7 | -------------------------------------------------------------------------------- /releasenotes/source/wallaby.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | Wallaby Series Release Notes 3 | ============================ 4 | 5 | .. release-notes:: 6 | :branch: unmaintained/wallaby 7 | -------------------------------------------------------------------------------- /releasenotes/source/xena.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | Xena Series Release Notes 3 | ========================= 4 | 5 | .. release-notes:: 6 | :branch: unmaintained/xena 7 | -------------------------------------------------------------------------------- /releasenotes/source/yoga.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | Yoga Series Release Notes 3 | ========================= 4 | 5 | .. release-notes:: 6 | :branch: unmaintained/yoga 7 | -------------------------------------------------------------------------------- /releasenotes/source/zed.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | Zed Series Release Notes 3 | ======================== 4 | 5 | .. release-notes:: 6 | :branch: unmaintained/zed 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Requirements lower bounds listed here are our best effort to keep them up to 2 | # date but we do not test them so no guarantee of having them all correct. If 3 | # you find any incorrect lower bounds, let us know or propose a fix. 4 | 5 | cliff!=2.9.0,>=2.8.0 # Apache-2.0 6 | jsonschema>=3.2.0 # MIT 7 | osc-lib>=1.8.0 # Apache-2.0 8 | oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 9 | oslo.utils>=3.33.0 # Apache-2.0 10 | pbr!=2.1.0,>=2.0.0 # Apache-2.0 11 | keystoneauth1>=3.4.0 # Apache-2.0 12 | requests>=2.14.2 # Apache-2.0 13 | stevedore>=1.20.0 # Apache-2.0 14 | debtcollector>=1.2.0 # Apache-2.0 15 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = python-designateclient 3 | description = OpenStack DNS-as-a-Service - Client 4 | long_description = file: README.rst 5 | license = Apache License, Version 2.0 6 | author = OpenStack 7 | author_email = openstack-discuss@lists.openstack.org 8 | url = https://docs.openstack.org/python-designateclient/latest 9 | python_requires = >=3.9 10 | classifiers = 11 | Environment :: OpenStack 12 | Intended Audience :: Information Technology 13 | Intended Audience :: System Administrators 14 | License :: OSI Approved :: Apache Software License 15 | Operating System :: POSIX :: Linux 16 | Programming Language :: Python 17 | Programming Language :: Python :: Implementation :: CPython 18 | Programming Language :: Python :: 3 :: Only 19 | Programming Language :: Python :: 3 20 | Programming Language :: Python :: 3.9 21 | Programming Language :: Python :: 3.10 22 | Programming Language :: Python :: 3.11 23 | Programming Language :: Python :: 3.12 24 | Topic :: Internet :: Name Service (DNS) 25 | 26 | [files] 27 | packages = 28 | designateclient 29 | 30 | [entry_points] 31 | designateclient.versions = 32 | 2 = designateclient.v2.client:Client 33 | 34 | openstack.cli.extension = 35 | dns = designateclient.osc.plugin 36 | 37 | openstack.dns.v2 = 38 | zone_blacklist_create = designateclient.v2.cli.blacklists:CreateBlacklistCommand 39 | zone_blacklist_list = designateclient.v2.cli.blacklists:ListBlacklistsCommand 40 | zone_blacklist_show = designateclient.v2.cli.blacklists:ShowBlacklistCommand 41 | zone_blacklist_set = designateclient.v2.cli.blacklists:SetBlacklistCommand 42 | zone_blacklist_delete = designateclient.v2.cli.blacklists:DeleteBlacklistCommand 43 | 44 | tld_create = designateclient.v2.cli.tlds:CreateTLDCommand 45 | tld_list = designateclient.v2.cli.tlds:ListTLDsCommand 46 | tld_show = designateclient.v2.cli.tlds:ShowTLDCommand 47 | tld_set = designateclient.v2.cli.tlds:SetTLDCommand 48 | tld_delete = designateclient.v2.cli.tlds:DeleteTLDCommand 49 | 50 | zone_create = designateclient.v2.cli.zones:CreateZoneCommand 51 | zone_list = designateclient.v2.cli.zones:ListZonesCommand 52 | zone_show = designateclient.v2.cli.zones:ShowZoneCommand 53 | zone_set = designateclient.v2.cli.zones:SetZoneCommand 54 | zone_delete = designateclient.v2.cli.zones:DeleteZoneCommand 55 | 56 | zone_abandon = designateclient.v2.cli.zones:AbandonZoneCommand 57 | zone_axfr = designateclient.v2.cli.zones:AXFRZoneCommand 58 | zone_move = designateclient.v2.cli.zones:PoolMoveZoneCommand 59 | 60 | zone_export_create = designateclient.v2.cli.zones:ExportZoneCommand 61 | zone_export_list = designateclient.v2.cli.zones:ListZoneExportsCommand 62 | zone_export_show = designateclient.v2.cli.zones:ShowZoneExportCommand 63 | zone_export_delete = designateclient.v2.cli.zones:DeleteZoneExportCommand 64 | zone_export_showfile = designateclient.v2.cli.zones:ShowZoneExportFileCommand 65 | 66 | zone_import_create = designateclient.v2.cli.zones:ImportZoneCommand 67 | zone_import_list = designateclient.v2.cli.zones:ListZoneImportsCommand 68 | zone_import_show = designateclient.v2.cli.zones:ShowZoneImportCommand 69 | zone_import_delete = designateclient.v2.cli.zones:DeleteZoneImportCommand 70 | 71 | zone_transfer_request_create = designateclient.v2.cli.zones:CreateTransferRequestCommand 72 | zone_transfer_request_list = designateclient.v2.cli.zones:ListTransferRequestsCommand 73 | zone_transfer_request_show = designateclient.v2.cli.zones:ShowTransferRequestCommand 74 | zone_transfer_request_set = designateclient.v2.cli.zones:SetTransferRequestCommand 75 | zone_transfer_request_delete = designateclient.v2.cli.zones:DeleteTransferRequestCommand 76 | zone_transfer_accept_request = designateclient.v2.cli.zones:AcceptTransferRequestCommand 77 | zone_transfer_accept_list = designateclient.v2.cli.zones:ListTransferAcceptsCommand 78 | zone_transfer_accept_show = designateclient.v2.cli.zones:ShowTransferAcceptCommand 79 | 80 | zone_share_create = designateclient.v2.cli.zones:ShareZoneCommand 81 | zone_share_list = designateclient.v2.cli.zones:ListSharedZonesCommand 82 | zone_share_show = designateclient.v2.cli.zones:ShowSharedZoneCommand 83 | zone_share_delete = designateclient.v2.cli.zones:DeleteSharedZoneCommand 84 | 85 | recordset_create = designateclient.v2.cli.recordsets:CreateRecordSetCommand 86 | recordset_list = designateclient.v2.cli.recordsets:ListRecordSetsCommand 87 | recordset_show = designateclient.v2.cli.recordsets:ShowRecordSetCommand 88 | recordset_set = designateclient.v2.cli.recordsets:SetRecordSetCommand 89 | recordset_delete = designateclient.v2.cli.recordsets:DeleteRecordSetCommand 90 | 91 | ptr_record_list = designateclient.v2.cli.reverse:ListFloatingIPCommand 92 | ptr_record_show = designateclient.v2.cli.reverse:ShowFloatingIPCommand 93 | ptr_record_set = designateclient.v2.cli.reverse:SetFloatingIPCommand 94 | ptr_record_unset = designateclient.v2.cli.reverse:UnsetFloatingIPCommand 95 | 96 | dns_service_list = designateclient.v2.cli.service_statuses:ListServiceStatusesCommand 97 | dns_service_show = designateclient.v2.cli.service_statuses:ShowServiceStatusCommand 98 | 99 | dns_quota_list = designateclient.v2.cli.quotas:ListQuotasCommand 100 | dns_quota_set = designateclient.v2.cli.quotas:SetQuotasCommand 101 | dns_quota_reset = designateclient.v2.cli.quotas:ResetQuotasCommand 102 | 103 | tsigkey_create = designateclient.v2.cli.tsigkeys:CreateTSIGKeyCommand 104 | tsigkey_list = designateclient.v2.cli.tsigkeys:ListTSIGKeysCommand 105 | tsigkey_show = designateclient.v2.cli.tsigkeys:ShowTSIGKeyCommand 106 | tsigkey_set = designateclient.v2.cli.tsigkeys:SetTSIGKeyCommand 107 | tsigkey_delete = designateclient.v2.cli.tsigkeys:DeleteTSIGKeyCommand 108 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import setuptools 17 | 18 | setuptools.setup( 19 | setup_requires=['pbr>=2.0.0'], 20 | pbr=True) 21 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | # Hacking already pins down pep8, pyflakes and flake8 2 | hacking>=6.1.0,<6.2.0 # Apache-2.0 3 | coverage!=4.4,>=4.0 # Apache-2.0 4 | oslo.config>=5.2.0 # Apache-2.0 5 | oslotest>=3.2.0 # Apache-2.0 6 | python-subunit>=1.0.0 # Apache-2.0/BSD 7 | requests-mock>=1.2.0 # Apache-2.0 8 | stestr>=2.0.0 # Apache-2.0 9 | tempest>=25.0.0 # Apache-2.0 10 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py3,flake8 3 | minversion = 3.18.0 4 | skip_missing_interpreters = true 5 | # this allows tox to infer the base python from the environment name 6 | # and override any basepython configured in this file 7 | ignore_basepython_conflict = true 8 | 9 | [testenv] 10 | usedevelop = True 11 | install_command = pip install {opts} {packages} 12 | setenv = VIRTUAL_ENV={envdir} 13 | LANG=en_US.UTF-8 14 | LANGUAGE=en_US:en 15 | LC_ALL=C 16 | deps = 17 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 18 | -r{toxinidir}/requirements.txt 19 | -r{toxinidir}/test-requirements.txt 20 | 21 | allowlist_externals = find 22 | sh 23 | rm 24 | 25 | commands = 26 | find . -type f -name "*.pyc" -delete 27 | stestr run --slowest {posargs} 28 | passenv = 29 | http_proxy 30 | HTTP_PROXY 31 | https_proxy 32 | HTTPS_PROXY 33 | no_proxy 34 | NO_PROXY 35 | 36 | [testenv:docs] 37 | deps = 38 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 39 | -r{toxinidir}/doc/requirements.txt 40 | commands = sphinx-build -W -b html doc/source doc/build/html 41 | 42 | [testenv:flake8] 43 | commands = flake8 44 | 45 | [testenv:pep8] 46 | commands = flake8 47 | 48 | [testenv:pyflakes] 49 | commands = flake8 50 | 51 | [testenv:cover] 52 | setenv = 53 | PYTHON=coverage run --source designateclient --parallel-mode 54 | commands = 55 | stestr run {posargs} 56 | coverage combine 57 | coverage html -d cover 58 | coverage xml -o cover/coverage.xml 59 | 60 | [testenv:venv] 61 | commands = {posargs} 62 | 63 | [testenv:functional] 64 | usedevelop = False 65 | setenv = {[testenv]setenv} 66 | OS_TEST_PATH=designateclient/functionaltests/ 67 | passenv = OS_STDOUT_CAPTURE 68 | OS_STDERR_CAPTURE 69 | OS_LOG_CAPTURE 70 | OS_DEBUG 71 | TEMPEST_CONFIG 72 | 73 | [testenv:functional-py{39,310,311,312}] 74 | setenv = 75 | {[testenv:functional]setenv} 76 | 77 | [testenv:releasenotes] 78 | deps = 79 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 80 | -r{toxinidir}/doc/requirements.txt 81 | commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html 82 | 83 | [flake8] 84 | # ignored flake8 codes: 85 | # H302 import only modules 86 | # H404 multi line docstring should start with a summary 87 | # H405 multi line docstring summary not separated with an empty line 88 | # H904 Wrap long lines in parentheses instead of a backslash 89 | # W504 line break after binary operator 90 | # See designate for other ignored codes that may apply here 91 | 92 | ignore = H105,H302,H404,H405,W504,H904 93 | builtins = _ 94 | exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,tools 95 | 96 | [flake8:local-plugins] 97 | extension = 98 | D701 = checks:mutable_default_arguments 99 | D703 = checks:check_explicit_underscore_import 100 | D704 = checks:no_import_graduated_oslo_libraries 101 | D705 = checks:use_timeutils_utcnow 102 | D706 = checks:no_translate_debug_logs 103 | D707 = checks:check_no_basestring 104 | D708 = checks:check_python3_xrange 105 | D709 = checks:check_no_log_audit 106 | D710 = checks:check_no_log_warn 107 | D711 = checks:check_line_continuation_no_backslash 108 | paths = ./designateclient/hacking 109 | --------------------------------------------------------------------------------