diff --git a/designateclient/functionaltests/client.py b/designateclient/functionaltests/client.py index 01db686..2c959ba 100644 --- a/designateclient/functionaltests/client.py +++ b/designateclient/functionaltests/client.py @@ -25,6 +25,28 @@ from designateclient.functionaltests.models import ListModel LOG = logging.getLogger(__name__) +def build_option_string(options): + """Format a string of option flags (--key 'value'). + + This will quote the values, in case spaces are included. + Any values that are None are excluded entirely. + + Usage: + build_option_string({ + "--email": "me@example.com", + "--name": "example.com." + "--ttl": None, + + }) + + Returns: + "--email 'me@example.com' --name 'example.com.' + """ + return " ".join("{0} '{1}'".format(flag, value) + for flag, value in options.items() + if value is not None) + + class ZoneCommands(object): """This is a mixin that provides zone commands to DesignateCLI""" @@ -38,16 +60,94 @@ class ZoneCommands(object): def zone_delete(self, id, *args, **kwargs): return self.parsed_cmd('zone delete %s' % id, *args, **kwargs) - def zone_create(self, name, email, *args, **kwargs): - cmd = 'zone create %s --email %s' % (name, email) + def zone_create(self, name, email=None, ttl=None, description=None, + type=None, masters=None, *args, **kwargs): + options_str = build_option_string({ + "--email": email, + "--ttl": ttl, + "--description": description, + "--masters": masters, + "--type": type, + }) + cmd = 'zone create {0} {1}'.format(name, options_str) + return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) + + def zone_set(self, id, email=None, ttl=None, description=None, + type=None, masters=None, *args, **kwargs): + options_str = build_option_string({ + "--email": email, + "--ttl": ttl, + "--description": description, + "--masters": masters, + "--type": type, + }) + cmd = 'zone set {0} {1}'.format(id, options_str) return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) -class DesignateCLI(base.CLIClient, ZoneCommands): +class ZoneTransferCommands(object): + """A mixin for DesignateCLI to add zone transfer commands""" + + def zone_transfer_request_list(self, *args, **kwargs): + cmd = 'zone transfer request list' + return self.parsed_cmd(cmd, ListModel, *args, **kwargs) + + def zone_transfer_request_create(self, zone_id, target_project_id=None, + description=None, *args, **kwargs): + options_str = build_option_string({ + "--target-project-id": target_project_id, + "--description": description, + }) + cmd = 'zone transfer request create {0} {1}'.format( + zone_id, options_str) + return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) + + def zone_transfer_request_show(self, id, *args, **kwargs): + cmd = 'zone transfer request show {0}'.format(id) + return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) + + def zone_transfer_request_set(self, id, description=None, *args, **kwargs): + options_str = build_option_string({"--description": description}) + cmd = 'zone transfer request set {0} {1}'.format(options_str, id) + return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) + + def zone_transfer_request_delete(self, id, *args, **kwargs): + cmd = 'zone transfer request delete {0}'.format(id) + return self.parsed_cmd(cmd, *args, **kwargs) + + def zone_transfer_accept_request(self, id, key, *args, **kwargs): + options_str = build_option_string({ + "--transfer-id": id, + "--key": key, + }) + cmd = 'zone transfer accept request {0}'.format(options_str) + return self.parsed_cmd(cmd, *args, **kwargs) + + def zone_transfer_accept_show(self, id, *args, **kwargs): + cmd = 'zone transfer accept show {0}'.format(id) + return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs) + + +class DesignateCLI(base.CLIClient, ZoneCommands, ZoneTransferCommands): + + # instantiate this once to minimize requests to keystone + _CLIENTS = None + + def __init__(self, *args, **kwargs): + super(DesignateCLI, self).__init__(*args, **kwargs) + # grab the project id. this is used for zone transfer requests + resp = FieldValueModel(self.keystone('token-get')) + self.project_id = resp.tenant_id @classmethod def get_clients(cls): - return { + if not cls._CLIENTS: + cls._init_clients() + return cls._CLIENTS + + @classmethod + def _init_clients(cls): + cls._CLIENTS = { 'default': DesignateCLI( cli_dir=cfg.CONF.designateclient.directory, username=cfg.CONF.identity.username, diff --git a/designateclient/functionaltests/v2/fixtures.py b/designateclient/functionaltests/v2/fixtures.py new file mode 100644 index 0000000..7bb096a --- /dev/null +++ b/designateclient/functionaltests/v2/fixtures.py @@ -0,0 +1,80 @@ +""" +Copyright 2015 Rackspace + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +from __future__ import absolute_import + +import fixtures +from tempest_lib.exceptions import CommandFailed + +from designateclient.functionaltests.client import DesignateCLI + + +class BaseFixture(fixtures.Fixture): + + def __init__(self, user='default', *args, **kwargs): + """args/kwargs are forwarded to a create method on DesignateCLI""" + super(BaseFixture, self).__init__() + self.args = args + self.kwargs = kwargs + self.client = DesignateCLI.as_user(user) + + +class ZoneFixture(BaseFixture): + """See DesignateCLI.zone_create for __init__ args""" + + def _setUp(self): + super(ZoneFixture, self)._setUp() + self.zone = self.client.zone_create(*self.args, **self.kwargs) + self.addCleanup(self.cleanup_zone, self.client, self.zone.id) + + @classmethod + def cleanup_zone(cls, client, zone_id): + try: + client.zone_delete(zone_id) + except CommandFailed: + pass + + +class TransferRequestFixture(BaseFixture): + """See DesignateCLI.zone_transfer_request_create for __init__ args""" + + def __init__(self, zone, user='default', target_user='alt', *args, + **kwargs): + super(TransferRequestFixture, self).__init__(user, *args, **kwargs) + self.zone = zone + self.target_client = DesignateCLI.as_user(target_user) + + # the client has a bug such that it requires --target-project-id. + # when this bug is fixed, please remove this + self.kwargs['target_project_id'] = self.target_client.project_id + + def _setUp(self): + super(TransferRequestFixture, self)._setUp() + self.transfer_request = self.client.zone_transfer_request_create( + zone_id=self.zone.id, + *self.args, **self.kwargs + ) + self.addCleanup(self.cleanup_transfer_request, self.client, + self.transfer_request.id) + self.addCleanup(ZoneFixture.cleanup_zone, self.client, self.zone.id) + self.addCleanup(ZoneFixture.cleanup_zone, self.target_client, + self.zone.id) + + @classmethod + def cleanup_transfer_request(cls, client, transfer_request_id): + try: + client.zone_transfer_request_delete(transfer_request_id) + except CommandFailed: + pass diff --git a/designateclient/functionaltests/v2/test_zone.py b/designateclient/functionaltests/v2/test_zone.py index 0d30a22..6ce8fd8 100644 --- a/designateclient/functionaltests/v2/test_zone.py +++ b/designateclient/functionaltests/v2/test_zone.py @@ -17,31 +17,108 @@ from tempest_lib.exceptions import CommandFailed from designateclient.functionaltests.base import BaseDesignateTest from designateclient.functionaltests.datagen import random_zone_name +from designateclient.functionaltests.v2.fixtures import ZoneFixture class TestZone(BaseDesignateTest): def setUp(self): super(TestZone, self).setUp() - self.zone_name = random_zone_name() - zone = self.clients.zone_create(name=self.zone_name, - email='test@example.com') - self.zone_id = zone.id + self.fixture = self.useFixture(ZoneFixture( + name=random_zone_name(), + email='test@example.com', + )) + self.zone = self.fixture.zone def test_zone_list(self): zones = self.clients.zone_list() self.assertGreater(len(zones), 0) def test_zone_create_and_show(self): - zone = self.clients.zone_show(self.zone_id) - self.assertEqual(zone.name, self.zone_name) - self.assertEqual(zone.id, self.zone_id) + zone = self.clients.zone_show(self.zone.id) + self.assertTrue(hasattr(zone, 'action')) + self.assertEqual(zone.created_at, self.zone.created_at) + self.assertEqual(zone.description, self.zone.description) + self.assertEqual(zone.email, self.zone.email) + self.assertEqual(zone.id, self.zone.id) + self.assertEqual(zone.masters, self.zone.masters) + self.assertEqual(zone.name, self.zone.name) + self.assertEqual(zone.pool_id, self.zone.pool_id) + self.assertEqual(zone.project_id, self.zone.project_id) + self.assertEqual(zone.serial, self.zone.serial) + self.assertTrue(hasattr(zone, 'status')) + self.assertEqual(zone.transferred_at, self.zone.transferred_at) + self.assertEqual(zone.ttl, self.zone.ttl) + self.assertEqual(zone.type, self.zone.type) + self.assertEqual(zone.updated_at, self.zone.updated_at) + self.assertEqual(zone.version, self.zone.version) def test_zone_delete(self): - self.clients.zone_delete(self.zone_id) - self.assertRaises(CommandFailed, self.clients.zone_show, self.zone_id) + self.clients.zone_delete(self.zone.id) + self.assertRaises(CommandFailed, self.clients.zone_show, self.zone.id) - def tearDown(self): - if hasattr(self, 'zone_id'): - self.clients.zone_delete(self.zone_id, fail_ok=True) - super(TestZone, self).tearDown() + def test_zone_set(self): + ttl = int(self.zone.ttl) + 123 + email = 'updated{0}'.format(self.zone.email) + description = 'new description' + + zone = self.clients.zone_set(self.zone.id, ttl=ttl, email=email, + description=description) + self.assertEqual(int(zone.ttl), ttl) + self.assertEqual(zone.email, email) + self.assertEqual(zone.description, description) + + def test_invalid_option_on_zone_create(self): + cmd = 'zone create %s --invalid "not a valid option"'.format( + random_zone_name()) + self.assertRaises(CommandFailed, self.clients.openstack, cmd) + + def test_invalid_zone_command(self): + cmd = 'zone hopefullynotacommand' + self.assertRaises(CommandFailed, self.clients.openstack, cmd) + + +class TestsPassingZoneFlags(BaseDesignateTest): + + def test_zone_create_primary_with_all_args(self): + zone_name = random_zone_name() + fixture = self.useFixture(ZoneFixture( + name=zone_name, + email='primary@example.com', + description='A primary zone', + ttl=2345, + type='PRIMARY', + )) + zone = fixture.zone + self.assertEqual(zone.name, zone_name) + self.assertEqual(zone.email, 'primary@example.com') + self.assertEqual(zone.description, 'A primary zone') + self.assertEqual(zone.ttl, '2345') + self.assertEqual(zone.type, 'PRIMARY') + + def test_zone_create_secondary_with_all_args(self): + zone_name = random_zone_name() + fixture = self.useFixture(ZoneFixture( + name=zone_name, + description='A secondary zone', + type='SECONDARY', + masters='127.0.0.1', + )) + zone = fixture.zone + self.assertEqual(zone.name, zone_name) + self.assertEqual(zone.description, 'A secondary zone') + self.assertEqual(zone.type, 'SECONDARY') + self.assertEqual(zone.masters, '127.0.0.1') + + def test_zone_set_secondary_masters(self): + fixture = self.useFixture(ZoneFixture( + name=random_zone_name(), + description='A secondary zone', + type='SECONDARY', + masters='127.0.0.1', + )) + zone = fixture.zone + self.assertEqual(zone.masters, '127.0.0.1') + + zone = self.clients.zone_set(zone.id, masters='127.0.0.2') + self.assertEqual(zone.masters, '127.0.0.2') diff --git a/designateclient/functionaltests/v2/test_zone_transfer.py b/designateclient/functionaltests/v2/test_zone_transfer.py new file mode 100644 index 0000000..4b97498 --- /dev/null +++ b/designateclient/functionaltests/v2/test_zone_transfer.py @@ -0,0 +1,116 @@ +""" +Copyright 2015 Rackspace + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import unittest + +from tempest_lib.exceptions import CommandFailed + +from designateclient.functionaltests.base import BaseDesignateTest +from designateclient.functionaltests.client import DesignateCLI +from designateclient.functionaltests.datagen import random_zone_name +from designateclient.functionaltests.v2.fixtures import TransferRequestFixture +from designateclient.functionaltests.v2.fixtures import ZoneFixture + + +class TestZoneTransferRequest(BaseDesignateTest): + + def setUp(self): + super(TestZoneTransferRequest, self).setUp() + fixture = self.useFixture(ZoneFixture( + name=random_zone_name(), + email='test@example.com', + )) + self.zone = fixture.zone + + def test_list_zone_transfer_request(self): + self.useFixture(TransferRequestFixture(self.zone)) + xfrs = self.clients.zone_transfer_request_list() + self.assertGreater(len(xfrs), 0) + + def test_create_and_show_zone_transfer_request(self): + transfer_request = self.useFixture(TransferRequestFixture( + zone=self.zone, + user='default', + target_user='alt', + )).transfer_request + + fetched_xfr = self.clients.zone_transfer_request_show( + transfer_request.id) + + self.assertEqual(fetched_xfr.created_at, transfer_request.created_at) + self.assertEqual(fetched_xfr.description, transfer_request.description) + self.assertEqual(fetched_xfr.id, transfer_request.id) + self.assertEqual(fetched_xfr.key, transfer_request.key) + self.assertEqual(fetched_xfr.links, transfer_request.links) + self.assertEqual(fetched_xfr.target_project_id, + transfer_request.target_project_id) + self.assertEqual(fetched_xfr.updated_at, transfer_request.updated_at) + self.assertEqual(fetched_xfr.status, transfer_request.status) + self.assertEqual(fetched_xfr.zone_id, self.zone.id) + self.assertEqual(fetched_xfr.zone_name, self.zone.name) + + def test_delete_zone_transfer_request(self): + transfer_request = self.useFixture(TransferRequestFixture( + zone=self.zone, + user='default', + target_user='alt', + )).transfer_request + + self.clients.zone_transfer_request_delete(transfer_request.id) + self.assertRaises(CommandFailed, + self.clients.zone_transfer_request_show, + transfer_request.id) + + @unittest.skip("Fails because `zone transfer request set` returns nothing") + def test_set_zone_transfer_request(self): + transfer_request = self.useFixture(TransferRequestFixture( + zone=self.zone, + description="old description", + )).transfer_request + + self.assertEqual(transfer_request.description, "old description") + + updated_xfr = self.clients.zone_transfer_request_set( + transfer_request.id, + description="updated description") + self.assertEqual(updated_xfr.description, "updated description") + + +class TestZoneTransferAccept(BaseDesignateTest): + + def setUp(self): + super(TestZoneTransferAccept, self).setUp() + fixture = self.useFixture(ZoneFixture( + name=random_zone_name(), + email='test@example.com', + )) + self.zone = fixture.zone + + self.target_client = DesignateCLI.as_user('alt') + fixture = self.useFixture(TransferRequestFixture( + zone=self.zone, + user='default', + target_user='alt', + target_project_id=self.target_client.project_id, + )) + self.transfer_request = fixture.transfer_request + + def test_zone_transfer_accept_request(self): + self.target_client.zone_transfer_accept_request( + id=self.transfer_request.id, + key=self.transfer_request.key, + ) + self.target_client.zone_show(self.zone.id) + self.assertRaises(CommandFailed, self.clients.zone_show, self.zone.id)