python-designateclient
Advanced tools
| """ | ||
| Copyright 2020 Cloudification GmbH. All rights reserved. | ||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| """ | ||
| from designateclient.functionaltests.base import BaseDesignateTest | ||
| from designateclient.functionaltests.client import DesignateCLI | ||
| from designateclient.functionaltests.datagen import random_zone_name | ||
| from designateclient.functionaltests.v2.fixtures import SharedZoneFixture | ||
| from designateclient.functionaltests.v2.fixtures import ZoneFixture | ||
| class TestSharedZone(BaseDesignateTest): | ||
| def setUp(self): | ||
| super(TestSharedZone, self).setUp() | ||
| self.ensure_tld_exists('com') | ||
| fixture = self.useFixture(ZoneFixture( | ||
| name=random_zone_name(), | ||
| email='test@example.com', | ||
| )) | ||
| self.zone = fixture.zone | ||
| self.target_client = DesignateCLI.as_user('alt') | ||
| def test_list_shared_zones(self): | ||
| shared_zone = self.useFixture(SharedZoneFixture( | ||
| zone_id=self.zone.id, | ||
| target_tenant_id=self.target_client.project_id | ||
| )).zone_share | ||
| shared_zones = self.clients.shared_zone_list(self.zone.id) | ||
| self.assertGreater(len(shared_zones), 0) | ||
| self.assertTrue(self._is_entity_in_list(shared_zone, shared_zones)) | ||
| def test_share_and_show_shared_zone(self): | ||
| shared_zone = self.useFixture(SharedZoneFixture( | ||
| zone_id=self.zone.id, | ||
| target_tenant_id=self.target_client.project_id | ||
| )).zone_share | ||
| fetched_shared_zone = self.clients.shared_zone_show(self.zone.id, | ||
| shared_zone.id) | ||
| self.assertEqual( | ||
| shared_zone.created_at, fetched_shared_zone.created_at) | ||
| self.assertEqual(shared_zone.id, fetched_shared_zone.id) | ||
| self.assertEqual( | ||
| shared_zone.project_id, fetched_shared_zone.project_id) | ||
| self.assertEqual(shared_zone.zone_id, fetched_shared_zone.zone_id) | ||
| def test_unshare_zone(self): | ||
| shared_zone = self.useFixture(SharedZoneFixture( | ||
| zone_id=self.zone.id, | ||
| target_tenant_id=self.target_client.project_id | ||
| )).zone_share | ||
| shared_zones = self.clients.shared_zone_list(self.zone.id) | ||
| self.assertTrue(self._is_entity_in_list(shared_zone, shared_zones)) | ||
| self.clients.unshare_zone(self.zone.id, shared_zone.id) | ||
| shared_zones = self.clients.shared_zone_list(self.zone.id) | ||
| self.assertFalse(self._is_entity_in_list(shared_zone, shared_zones)) |
| --- | ||
| upgrade: | ||
| - | | ||
| Added option``hard-delete`` for zone delete API. This will allow user to | ||
| delete zone-files on the backend when zone is deleted. |
| --- | ||
| features: | ||
| - Adds zone share commands to support sharing zones with additional projects. | ||
| - Adds a ``--delete-shares`` option to zone delete to delete existing zone | ||
| shares along with the zone. Without this option, you cannot delete a zone | ||
| that has been shared with other projects. |
+2
-0
@@ -41,2 +41,3 @@ 98k <18552437190@163.com> | ||
| Hervé Beraud <hberaud@redhat.com> | ||
| Igor Malinovskiy <u.glide@gmail.com> | ||
| James Li <yueli.m@gmail.com> | ||
@@ -54,2 +55,3 @@ Janonymous <janonymous.codevulture@gmail.com> | ||
| Lakshmi N Sampath <lakshmi.sampath@hp.com> | ||
| Manish Honap <mmhonap@gmail.com> | ||
| Marcus Furlong <furlongm@gmail.com> | ||
@@ -56,0 +58,0 @@ Michael Chapman <woppin@gmail.com> |
@@ -351,5 +351,24 @@ """ | ||
| class SharedZoneCommands(object): | ||
| def shared_zone_show(self, zone_id, shared_zone_id, *args, **kwargs): | ||
| cmd = 'zone share show {0} {1}'.format(zone_id, shared_zone_id) | ||
| return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) | ||
| def shared_zone_list(self, zone_id, *args, **kwargs): | ||
| cmd = 'zone share list {0}'.format(zone_id) | ||
| return self.parsed_cmd(cmd, ListModel, *args, **kwargs) | ||
| def share_zone(self, zone_id, target_project_id, *args, **kwargs): | ||
| cmd = 'zone share create {0} {1}'.format(zone_id, target_project_id) | ||
| return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) | ||
| def unshare_zone(self, zone_id, shared_zone_id, *args, **kwargs): | ||
| cmd = 'zone share delete {0} {1}'.format(zone_id, shared_zone_id) | ||
| return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) | ||
| class DesignateCLI(base.CLIClient, ZoneCommands, ZoneTransferCommands, | ||
| ZoneExportCommands, ZoneImportCommands, RecordsetCommands, | ||
| TLDCommands, BlacklistCommands): | ||
| TLDCommands, BlacklistCommands, SharedZoneCommands): | ||
@@ -356,0 +375,0 @@ # instantiate this once to minimize requests to keystone |
@@ -231,1 +231,23 @@ """ | ||
| pass | ||
| class SharedZoneFixture(BaseFixture): | ||
| """See DesignateCLI.recordset_create for __init__ args""" | ||
| def __init__(self, zone, *args, **kwargs): | ||
| super(SharedZoneFixture, self).__init__(*args, **kwargs) | ||
| self.zone = zone | ||
| def _setUp(self): | ||
| super(SharedZoneFixture, self)._setUp() | ||
| self.zone_share = self.client.zone_share(zone_id=self.zone.id, | ||
| *self.args, **self.kwargs) | ||
| self.addCleanup(self.cleanup_shared_zone, self.client, self.zone.id, | ||
| self.zone_share.id) | ||
| @classmethod | ||
| def cleanup_shared_zone(cls, client, zone_id, shared_zone_id): | ||
| try: | ||
| client.unshare_zone(zone_id, shared_zone_id) | ||
| except CommandFailed: | ||
| pass |
@@ -119,4 +119,16 @@ # Copyright 2015 Hewlett-Packard Development Company, L.P. | ||
| self.client.zones.delete(ref["id"]) | ||
| self.assertRequestBodyIs(None) | ||
| self.assertRequestHeaderEqual('X-Designate-Delete-Shares', None) | ||
| def test_delete_with_delete_shares(self): | ||
| ref = self.new_ref() | ||
| self.stub_entity("DELETE", id=ref["id"]) | ||
| self.client.zones.delete(ref["id"], delete_shares=True) | ||
| self.assertRequestBodyIs(None) | ||
| self.assertRequestHeaderEqual('X-Designate-Delete-Shares', 'true') | ||
| def test_task_abandon(self): | ||
@@ -384,1 +396,71 @@ ref = self.new_ref() | ||
| self.assertRequestBodyIs(None) | ||
| class TestZoneShared(v2.APIV2TestCase, v2.CrudMixin): | ||
| def setUp(self): | ||
| super(TestZoneShared, self).setUp() | ||
| self.zone_id = str(uuid.uuid4()) | ||
| self.target_project_id = str(uuid.uuid4()) | ||
| self.project_id = str(uuid.uuid4()) | ||
| self.created_at = time.strftime("%c") | ||
| self.updated_at = time.strftime("%c") | ||
| def new_ref(self, **kwargs): | ||
| ref = super(TestZoneShared, self).new_ref(**kwargs) | ||
| ref.setdefault("zone_id", self.zone_id) | ||
| ref.setdefault("target_project_id", self.target_project_id) | ||
| ref.setdefault("project_id", self.project_id) | ||
| ref.setdefault("created_at", self.created_at) | ||
| ref.setdefault("updated_at", self.updated_at) | ||
| return ref | ||
| def test_share_a_zone(self): | ||
| json_body = {"target_project_id": self.target_project_id} | ||
| expected = self.new_ref() | ||
| self.stub_entity('POST', parts=['zones', self.zone_id, 'shares'], | ||
| entity=expected, json=json_body) | ||
| response = self.client.zone_share.create(self.zone_id, | ||
| self.target_project_id) | ||
| self.assertRequestBodyIs(json=json_body) | ||
| self.assertEqual(expected, response) | ||
| def test_get_zone_share(self): | ||
| expected = self.new_ref() | ||
| parts = ["zones", self.zone_id, "shares"] | ||
| self.stub_entity("GET", parts=parts, entity=expected, | ||
| id=expected["id"]) | ||
| response = self.client.zone_share.get(self.zone_id, expected["id"]) | ||
| self.assertRequestBodyIs(None) | ||
| self.assertEqual(expected, response) | ||
| def test_list_zone_shares(self): | ||
| items = [ | ||
| self.new_ref(), | ||
| self.new_ref() | ||
| ] | ||
| parts = ["zones", self.zone_id, "shares"] | ||
| self.stub_entity('GET', parts=parts, entity={"shared_zones": items}) | ||
| listed = self.client.zone_share.list(self.zone_id) | ||
| self.assertList(items, listed) | ||
| self.assertQueryStringIs("") | ||
| def test_delete_zone_share(self): | ||
| ref = self.new_ref() | ||
| parts = ["zones", self.zone_id, "shares", ref["id"]] | ||
| self.stub_url('DELETE', parts=parts) | ||
| response = self.client.zone_share.delete(self.zone_id, ref["id"]) | ||
| self.assertRequestBodyIs(None) | ||
| self.assertEqual('', response) |
@@ -36,2 +36,12 @@ # Copyright 2016 Hewlett Packard Enterprise Development Company LP | ||
| def add_hard_delete_option(parser): | ||
| parser.add_argument( | ||
| '--hard-delete', | ||
| default=False, | ||
| action='store_true', | ||
| help='Delete zone along-with backend zone resources (i.e. files). ' | ||
| 'Default: False' | ||
| ) | ||
| def add_sudo_project_id_option(parser): | ||
@@ -62,2 +72,6 @@ parser.add_argument( | ||
| def set_hard_delete(client, value): | ||
| client.session.hard_delete = value | ||
| def set_all_common_headers(client, parsed_args): | ||
@@ -77,1 +91,6 @@ | ||
| set_sudo_project_id(client, parsed_args.sudo_project_id) | ||
| if hasattr(parsed_args, 'hard_delete') and \ | ||
| parsed_args.hard_delete is not None and \ | ||
| isinstance(parsed_args.hard_delete, bool): | ||
| set_hard_delete(client, parsed_args.hard_delete) |
@@ -244,3 +244,8 @@ # Copyright 2014 Hewlett-Packard Development Company, L.P. | ||
| parser.add_argument('--delete-shares', default=False, | ||
| action='store_true', | ||
| help='Delete existing zone shares. Default: False') | ||
| common.add_all_common_options(parser) | ||
| common.add_hard_delete_option(parser) | ||
@@ -253,3 +258,9 @@ return parser | ||
| data = client.zones.delete(parsed_args.id) | ||
| delete_shares = False | ||
| if (hasattr(parsed_args, 'delete_shares') and | ||
| parsed_args.delete_shares is not None and | ||
| isinstance(parsed_args.delete_shares, bool)): | ||
| delete_shares = parsed_args.delete_shares | ||
| data = client.zones.delete(parsed_args.id, delete_shares=delete_shares) | ||
| LOG.info('Zone %s was deleted', parsed_args.id) | ||
@@ -728,1 +739,122 @@ | ||
| LOG.info('Zone Import %s was deleted', parsed_args.zone_import_id) | ||
| class ShareZoneCommand(command.ShowOne): | ||
| """Share a Zone""" | ||
| def get_parser(self, prog_name): | ||
| parser = super(ShareZoneCommand, self).get_parser( | ||
| prog_name) | ||
| common.add_all_common_options(parser) | ||
| parser.add_argument('zone', help='The zone name or ID to share.') | ||
| parser.add_argument('target_project_id', | ||
| help='Target project ID to share the zone with.') | ||
| return parser | ||
| def take_action(self, parsed_args): | ||
| client = self.app.client_manager.dns | ||
| common.set_all_common_headers(client, parsed_args) | ||
| data = client.zone_share.create( | ||
| parsed_args.zone, | ||
| parsed_args.target_project_id | ||
| ) | ||
| LOG.info('Zone %s was shared', data['id']) | ||
| data.pop('links', None) | ||
| return self.dict2columns(data) | ||
| class ListSharedZonesCommand(command.Lister): | ||
| """List Zone Shares""" | ||
| columns = [ | ||
| 'id', | ||
| 'zone_id', | ||
| 'target_project_id', | ||
| ] | ||
| def get_parser(self, prog_name): | ||
| parser = super(ListSharedZonesCommand, self).get_parser( | ||
| prog_name) | ||
| common.add_all_common_options(parser) | ||
| parser.add_argument('zone', help='The zone name or ID to share.') | ||
| parser.add_argument('--target-project-id', | ||
| help='The target project ID to filter on.', | ||
| required=False) | ||
| return parser | ||
| def take_action(self, parsed_args): | ||
| client = self.app.client_manager.dns | ||
| common.set_all_common_headers(client, parsed_args) | ||
| criterion = {} | ||
| if parsed_args.target_project_id is not None: | ||
| criterion['target_project_id'] = parsed_args.target_project_id | ||
| data = get_all(client.zone_share.list, criterion=criterion, | ||
| args=[parsed_args.zone]) | ||
| cols = list(self.columns) | ||
| if client.session.all_projects: | ||
| cols.insert(1, 'project_id') | ||
| return cols, (utils.get_item_properties(s, cols) for s in data) | ||
| class ShowSharedZoneCommand(command.ShowOne): | ||
| """Show Zone Share Details""" | ||
| def get_parser(self, prog_name): | ||
| parser = super(ShowSharedZoneCommand, self).get_parser(prog_name) | ||
| parser.add_argument('zone', help='The zone name or ID to share.') | ||
| parser.add_argument('shared_zone_id', | ||
| help='The zone share ID to show.') | ||
| common.add_all_common_options(parser) | ||
| return parser | ||
| def take_action(self, parsed_args): | ||
| client = self.app.client_manager.dns | ||
| common.set_all_common_headers(client, parsed_args) | ||
| data = client.zone_share.get(parsed_args.zone, | ||
| parsed_args.shared_zone_id) | ||
| data.pop('links', None) | ||
| return self.dict2columns(data) | ||
| class DeleteSharedZoneCommand(command.Command): | ||
| """Delete a Zone Share""" | ||
| def get_parser(self, prog_name): | ||
| parser = super(DeleteSharedZoneCommand, self).get_parser( | ||
| prog_name) | ||
| parser.add_argument('zone', help='The zone name or ID to share.') | ||
| parser.add_argument('shared_zone_id', | ||
| help='The zone share ID to delete.') | ||
| common.add_all_common_options(parser) | ||
| return parser | ||
| def take_action(self, parsed_args): | ||
| client = self.app.client_manager.dns | ||
| common.set_all_common_headers(client, parsed_args) | ||
| client.zone_share.delete(parsed_args.zone, parsed_args.shared_zone_id) | ||
| LOG.info('Shared Zone %s was deleted', parsed_args.shared_zone_id) |
@@ -32,2 +32,3 @@ # Copyright 2015 Hewlett-Packard Development Company, L.P. | ||
| from designateclient.v2.zones import ZoneImportsController | ||
| from designateclient.v2.zones import ZoneShareController | ||
| from designateclient.v2.zones import ZoneTransfersController | ||
@@ -53,2 +54,3 @@ from designateclient import version | ||
| self.edit_managed = kwargs.pop('edit_managed', False) | ||
| self.hard_delete = kwargs.pop('hard_delete', False) | ||
| self.sudo_project_id = kwargs.pop('sudo_project_id', None) | ||
@@ -77,2 +79,8 @@ super(self.__class__, self).__init__(*args, **kwargs) | ||
| if self.hard_delete: | ||
| kwargs['headers'].setdefault( | ||
| 'X-Designate-Hard-Delete', | ||
| str(self.hard_delete) | ||
| ) | ||
| if self.sudo_project_id is not None: | ||
@@ -119,3 +127,3 @@ kwargs['headers'].setdefault( | ||
| endpoint_override=None, all_projects=False, | ||
| edit_managed=False, sudo_project_id=None): | ||
| edit_managed=False, hard_delete=False, sudo_project_id=None): | ||
| if session is None: | ||
@@ -136,2 +144,3 @@ raise ValueError("A session instance is required") | ||
| edit_managed=edit_managed, | ||
| hard_delete=hard_delete, | ||
| sudo_project_id=sudo_project_id | ||
@@ -151,4 +160,5 @@ ) | ||
| self.zone_imports = ZoneImportsController(self) | ||
| self.zone_share = ZoneShareController(self) | ||
| self.pools = PoolController(self) | ||
| self.quotas = QuotasController(self) | ||
| self.tsigkeys = TSIGKeysController(self) |
@@ -65,3 +65,3 @@ # Copyright 2015 Hewlett-Packard Development Company, L.P. | ||
| def delete(self, zone): | ||
| def delete(self, zone, delete_shares=False): | ||
| zone = v2_utils.resolve_by_name(self.list, zone) | ||
@@ -71,4 +71,10 @@ | ||
| return self._delete(url) | ||
| if delete_shares: | ||
| headers = {'X-Designate-Delete-Shares': 'true'} | ||
| _resp, body = self.client.session.delete(url, headers=headers) | ||
| else: | ||
| _resp, body = self.client.session.delete(url) | ||
| return body | ||
| def abandon(self, zone): | ||
@@ -171,1 +177,27 @@ zone = v2_utils.resolve_by_name(self.list, zone) | ||
| return self._delete('/zones/tasks/imports/%s' % zone_import_id) | ||
| class ZoneShareController(V2Controller): | ||
| def create(self, zone, target_project_id): | ||
| zone_id = v2_utils.resolve_by_name(self.client.zones.list, zone) | ||
| data = {"target_project_id": target_project_id} | ||
| return self._post(f'/zones/{zone_id}/shares', data=data) | ||
| def list(self, zone, criterion=None, marker=None, limit=None): | ||
| zone_id = v2_utils.resolve_by_name(self.client.zones.list, zone) | ||
| url = self.build_url(f'/zones/{zone_id}/shares', | ||
| criterion, marker, limit) | ||
| return self._get(url, response_key='shared_zones') | ||
| def delete(self, zone, shared_zone_id): | ||
| zone_id = v2_utils.resolve_by_name(self.client.zones.list, zone) | ||
| return self._delete(f'/zones/{zone_id}/shares/{shared_zone_id}') | ||
| def get(self, zone, shared_zone_id): | ||
| zone_id = v2_utils.resolve_by_name(self.client.zones.list, zone) | ||
| return self._get(f'/zones/{zone_id}/shares/{shared_zone_id}') |
+1
-1
| Metadata-Version: 1.2 | ||
| Name: python-designateclient | ||
| Version: 5.1.0 | ||
| Version: 5.2.0 | ||
| Summary: OpenStack DNS-as-a-Service - Client | ||
@@ -5,0 +5,0 @@ Home-page: https://docs.openstack.org/python-designateclient/latest |
@@ -52,2 +52,6 @@ [designateclient.versions] | ||
| zone_set = designateclient.v2.cli.zones:SetZoneCommand | ||
| zone_share_create = designateclient.v2.cli.zones:ShareZoneCommand | ||
| zone_share_delete = designateclient.v2.cli.zones:DeleteSharedZoneCommand | ||
| zone_share_list = designateclient.v2.cli.zones:ListSharedZonesCommand | ||
| zone_share_show = designateclient.v2.cli.zones:ShowSharedZoneCommand | ||
| zone_show = designateclient.v2.cli.zones:ShowZoneCommand | ||
@@ -54,0 +58,0 @@ zone_transfer_accept_list = designateclient.v2.cli.zones:ListTransferAcceptsCommand |
@@ -1,1 +0,1 @@ | ||
| {"git_version": "6a8ef57", "is_release": true} | ||
| {"git_version": "bc39d23", "is_release": true} |
| Metadata-Version: 1.2 | ||
| Name: python-designateclient | ||
| Version: 5.1.0 | ||
| Version: 5.2.0 | ||
| Summary: OpenStack DNS-as-a-Service - Client | ||
@@ -5,0 +5,0 @@ Home-page: https://docs.openstack.org/python-designateclient/latest |
@@ -29,2 +29,3 @@ .stestr.conf | ||
| designateclient/functionaltests/v2/test_recordsets.py | ||
| designateclient/functionaltests/v2/test_shared_zone.py | ||
| designateclient/functionaltests/v2/test_tlds.py | ||
@@ -119,2 +120,4 @@ designateclient/functionaltests/v2/test_tsigkeys.py | ||
| releasenotes/notes/.placeholder | ||
| releasenotes/notes/Add-shared-zones-support-4be565f3d1c6356c.yaml | ||
| releasenotes/notes/add-hard-delete-option-for-zone-delete-e16652c8e72fc023.yaml | ||
| releasenotes/notes/bug-1940544-9ed7805341dec1ba.yaml | ||
@@ -121,0 +124,0 @@ releasenotes/notes/drop-py2-c4e50d006fa4446c.yaml |
+5
-0
@@ -75,2 +75,7 @@ [metadata] | ||
| zone_share_create = designateclient.v2.cli.zones:ShareZoneCommand | ||
| zone_share_list = designateclient.v2.cli.zones:ListSharedZonesCommand | ||
| zone_share_show = designateclient.v2.cli.zones:ShowSharedZoneCommand | ||
| zone_share_delete = designateclient.v2.cli.zones:DeleteSharedZoneCommand | ||
| recordset_create = designateclient.v2.cli.recordsets:CreateRecordSetCommand | ||
@@ -77,0 +82,0 @@ recordset_list = designateclient.v2.cli.recordsets:ListRecordSetsCommand |
+8
-4
| [tox] | ||
| envlist = py3,flake8 | ||
| minversion = 3.1.0 | ||
| skipsdist = True | ||
| skip_missing_interpreters = true | ||
@@ -11,3 +10,2 @@ # this allows tox to infer the base python from the environment name | ||
| [testenv] | ||
| basepython = python3 | ||
| usedevelop = True | ||
@@ -24,3 +22,3 @@ install_command = pip install {opts} {packages} | ||
| whitelist_externals = find | ||
| allowlist_externals = find | ||
| sh | ||
@@ -32,3 +30,9 @@ rm | ||
| stestr run --slowest {posargs} | ||
| passenv = http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY | ||
| passenv = | ||
| http_proxy | ||
| HTTP_PROXY | ||
| https_proxy | ||
| HTTPS_PROXY | ||
| no_proxy | ||
| NO_PROXY | ||
@@ -35,0 +39,0 @@ [testenv:docs] |
Sorry, the diff of this file is not supported yet
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
362296
4.32%144
2.13%6167
4.7%