Clean up manage pools and add additional testing

Change-Id: I6bfe074ec9db9bd95021771b8ffe988f1f23e2be
This commit is contained in:
Erik Olof Gunnar Andersson 2022-08-21 04:03:54 -07:00
parent a03c4657c2
commit e65b32a6e6
4 changed files with 128 additions and 58 deletions

View File

@ -13,7 +13,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from designate.context import DesignateContext from designate import context
# Decorators for actions # Decorators for actions
@ -36,5 +36,6 @@ def name(name):
class Commands(object): class Commands(object):
def __init__(self): def __init__(self):
self.context = DesignateContext.get_admin_context( self.context = context.DesignateContext.get_admin_context(
request_id='designate-manage') request_id='designate-manage'
)

View File

@ -37,6 +37,7 @@ CONF = cfg.CONF
class PoolCommands(base.Commands): class PoolCommands(base.Commands):
def __init__(self): def __init__(self):
super(PoolCommands, self).__init__() super(PoolCommands, self).__init__()
self.output_msg = ['']
# NOTE(jh): Cannot do this earlier because we are still missing the config # NOTE(jh): Cannot do this earlier because we are still missing the config
# at that point, see bug #1651576 # at that point, see bug #1651576
@ -44,13 +45,28 @@ class PoolCommands(base.Commands):
rpc.init(cfg.CONF) rpc.init(cfg.CONF)
self.central_api = central_rpcapi.CentralAPI() self.central_api = central_rpcapi.CentralAPI()
def _create_pool(self, pool, dry_run):
pool = DesignateAdapter.parse('YAML', pool, objects.Pool())
for ns_record in pool.ns_records:
try:
ns_record.validate()
except exceptions.InvalidObject as e:
LOG.error(e.errors.to_list()[0]['message'])
sys.exit(1)
if dry_run:
self.output_msg.append('Create Pool: %s' % pool)
else:
LOG.info('Creating new pool: %s', pool)
self.central_api.create_pool(self.context, pool)
def _update_zones(self, pool): def _update_zones(self, pool):
LOG.info("Updating zone masters for pool: {}".format(pool.id)) LOG.info('Updating zone masters for pool: %s', pool.id)
def __get_masters_from_pool(pool): def __get_masters_from_pool(pool):
masters = [] masters = []
for target in pool.targets: for target in pool.targets:
for master in target.get("masters", []): for master in target.get('masters', []):
master = {'host': master['host'], 'port': master['port']} master = {'host': master['host'], 'port': master['port']}
found = False found = False
for existing_master in masters: for existing_master in masters:
@ -69,9 +85,11 @@ class PoolCommands(base.Commands):
for zone in zones: for zone in zones:
zone.masters = objects.ZoneMasterList().from_list( zone.masters = objects.ZoneMasterList().from_list(
__get_masters_from_pool(pool)) __get_masters_from_pool(pool)
self.central_api.update_zone(self.context, )
zone) self.central_api.update_zone(
self.context, zone
)
@base.args('--file', help='The path to the file the yaml output should be ' @base.args('--file', help='The path to the file the yaml output should be '
'written to', 'written to',
@ -81,8 +99,10 @@ class PoolCommands(base.Commands):
try: try:
pools = self.central_api.find_pools(self.context) pools = self.central_api.find_pools(self.context)
except messaging.exceptions.MessagingTimeout: except messaging.exceptions.MessagingTimeout:
LOG.critical("No response received from designate-central. " LOG.critical(
"Check it is running, and retry") 'No response received from designate-central. '
'Check it is running, and retry'
)
sys.exit(1) sys.exit(1)
with open(file, 'w') as stream: with open(file, 'w') as stream:
yaml.dump( yaml.dump(
@ -96,7 +116,7 @@ class PoolCommands(base.Commands):
def show_config(self, pool_id): def show_config(self, pool_id):
self._startup() self._startup()
try: try:
pool = self.central_api.find_pool(self.context, {"id": pool_id}) pool = self.central_api.find_pool(self.context, {'id': pool_id})
print('Pool Configuration:') print('Pool Configuration:')
print('-------------------') print('-------------------')
@ -105,8 +125,10 @@ class PoolCommands(base.Commands):
default_flow_style=False)) default_flow_style=False))
except messaging.exceptions.MessagingTimeout: except messaging.exceptions.MessagingTimeout:
LOG.critical("No response received from designate-central. " LOG.critical(
"Check it is running, and retry") 'No response received from designate-central. '
'Check it is running, and retry'
)
sys.exit(1) sys.exit(1)
@base.args('--file', help='The path to the yaml file describing the pools', @base.args('--file', help='The path to the yaml file describing the pools',
@ -115,40 +137,45 @@ class PoolCommands(base.Commands):
'--delete', '--delete',
help='Any Pools not listed in the config file will be deleted. ' help='Any Pools not listed in the config file will be deleted. '
' WARNING: This will delete any zones left in this pool', ' WARNING: This will delete any zones left in this pool',
action="store_true", action='store_true',
default=False) default=False)
@base.args( @base.args(
'--dry-run', '--dry-run',
help='This will simulate what will happen when you run this command', help='This will simulate what will happen when you run this command',
action="store_true", action='store_true',
default=False) default=False)
def update(self, file, delete, dry_run): def update(self, file, delete, dry_run):
self._startup() self._startup()
print('Updating Pools Configuration') print('Updating Pools Configuration')
print('****************************') print('****************************')
output_msg = ['']
with open(file, 'r') as stream: with open(file, 'r') as stream:
xpools = yaml.safe_load(stream) xpools = yaml.safe_load(stream)
if dry_run: if dry_run:
output_msg.append("The following changes will occur:") self.output_msg.append('The following changes will occur:')
output_msg.append("*********************************") self.output_msg.append('*********************************')
for xpool in xpools: for xpool in xpools:
try: try:
if 'id' in xpool: if 'id' in xpool:
try: try:
pool = self.central_api.get_pool( pool = self.central_api.get_pool(
self.context, xpool['id']) self.context, xpool['id']
)
except Exception as e: except Exception as e:
msg = ("Bad ID Supplied for pool. pool_id: " LOG.critical(
"%(pool)s message: %(res)s") 'Bad ID Supplied for pool. pool_id: '
LOG.critical(msg, {'pool': xpool['id'], 'res': e}) '%(pool)s message: %(res)s',
{
'pool': xpool['id'], 'res': e
}
)
continue continue
else: else:
pool = self.central_api.find_pool( pool = self.central_api.find_pool(
self.context, {"name": xpool['name']}) self.context, {'name': xpool['name']}
)
LOG.info('Updating existing pool: %s', pool) LOG.info('Updating existing pool: %s', pool)
@ -167,7 +194,7 @@ class PoolCommands(base.Commands):
sys.exit(1) sys.exit(1)
if dry_run: if dry_run:
output_msg.append("Update Pool: %s" % pool) self.output_msg.append('Update Pool: %s' % pool)
else: else:
pool = self.central_api.update_pool(self.context, pool) pool = self.central_api.update_pool(self.context, pool)
# Bug: Changes in the pool targets should trigger a # Bug: Changes in the pool targets should trigger a
@ -175,21 +202,12 @@ class PoolCommands(base.Commands):
self._update_zones(pool) self._update_zones(pool)
except exceptions.PoolNotFound: except exceptions.PoolNotFound:
pool = DesignateAdapter.parse('YAML', xpool, objects.Pool()) self._create_pool(xpool, dry_run)
for ns_record in pool.ns_records:
try:
ns_record.validate()
except exceptions.InvalidObject as e:
LOG.error(e.errors.to_list()[0]['message'])
sys.exit(1)
if dry_run:
output_msg.append("Create Pool: %s" % pool)
else:
LOG.info('Creating new pool: %s', pool)
self.central_api.create_pool(self.context, pool)
except messaging.exceptions.MessagingTimeout: except messaging.exceptions.MessagingTimeout:
LOG.critical("No response received from designate-central. " LOG.critical(
"Check it is running, and retry") 'No response received from designate-central. '
'Check it is running, and retry'
)
sys.exit(1) sys.exit(1)
if delete: if delete:
@ -206,7 +224,7 @@ class PoolCommands(base.Commands):
criterion={'name': pool}) criterion={'name': pool})
if dry_run: if dry_run:
output_msg.append("Delete Pool: %s" % p) self.output_msg.append('Delete Pool: %s' % p)
else: else:
LOG.info('Deleting %s', p) LOG.info('Deleting %s', p)
@ -214,9 +232,10 @@ class PoolCommands(base.Commands):
except messaging.exceptions.MessagingTimeout: except messaging.exceptions.MessagingTimeout:
LOG.critical( LOG.critical(
"No response received from designate-central. " 'No response received from designate-central. '
"Check it is running, and retry") 'Check it is running, and retry'
)
sys.exit(1) sys.exit(1)
for line in output_msg: for line in self.output_msg:
print(line) print(line)

View File

@ -59,7 +59,6 @@ class PoolAttributeListYAMLAdapter(base.YAMLAdapter):
@classmethod @classmethod
def parse_list(cls, values, output_object, *args, **kwargs): def parse_list(cls, values, output_object, *args, **kwargs):
for key, value in values.items(): for key, value in values.items():
# Add the object to the list # Add the object to the list
output_object.append( output_object.append(

View File

@ -13,6 +13,7 @@ from unittest import mock
from oslo_log import log as logging from oslo_log import log as logging
from designate import context
from designate.manage.pool import PoolCommands from designate.manage.pool import PoolCommands
from designate import objects from designate import objects
from designate.tests import fixtures from designate.tests import fixtures
@ -27,6 +28,10 @@ class UpdatePoolTestCase(DesignateManageTestCase):
self.stdlog = fixtures.StandardLogging() self.stdlog = fixtures.StandardLogging()
self.useFixture(self.stdlog) self.useFixture(self.stdlog)
self.context = context.DesignateContext.get_admin_context(
request_id='designate-manage'
)
def hydrate_pool_targets(self, target_masters): def hydrate_pool_targets(self, target_masters):
pool_targets = objects.PoolTargetList() pool_targets = objects.PoolTargetList()
masters = objects.PoolTargetMasterList() masters = objects.PoolTargetMasterList()
@ -49,19 +54,21 @@ class UpdatePoolTestCase(DesignateManageTestCase):
# Ensure the correct NS Records are in place # Ensure the correct NS Records are in place
pool = self.central_service.get_pool( pool = self.central_service.get_pool(
self.admin_context, zone.pool_id) self.admin_context, zone.pool_id
)
pool.targets = self.hydrate_pool_targets([objects.PoolTargetMaster( pool.targets = self.hydrate_pool_targets([objects.PoolTargetMaster(
pool_target_id=pool.id, pool_target_id=pool.id,
host="127.0.0.1", host='192.0.2.2',
port="53")]) port='53')]
)
command = PoolCommands() command = PoolCommands()
command.context = self.admin_context command.context = self.context
command.central_api = self.central_service command.central_api = self.central_service
with mock.patch.object(self.central_service, with mock.patch.object(
"update_zone") as mock_update_zone: self.central_service, 'update_zone') as mock_update_zone:
command._update_zones(pool) command._update_zones(pool)
mock_update_zone.assert_called_once() mock_update_zone.assert_called_once()
@ -77,25 +84,69 @@ class UpdatePoolTestCase(DesignateManageTestCase):
# Ensure the correct NS Records are in place # Ensure the correct NS Records are in place
pool = self.central_service.get_pool( pool = self.central_service.get_pool(
self.admin_context, zone.pool_id) self.admin_context, zone.pool_id
)
targets1 = self.hydrate_pool_targets([ targets1 = self.hydrate_pool_targets([
objects.PoolTargetMaster( objects.PoolTargetMaster(
pool_target_id=pool.id, pool_target_id=pool.id,
host="127.0.0.1", host='192.0.2.3',
port="53") port='53')
]) ])
targets2 = self.hydrate_pool_targets([ targets2 = self.hydrate_pool_targets([
objects.PoolTargetMaster( objects.PoolTargetMaster(
pool_target_id=pool.id, pool_target_id=pool.id,
host="127.0.0.1", host='192.0.2.4',
port="53") port='53')
]) ])
pool.targets = objects.PoolTargetList() pool.targets = objects.PoolTargetList()
pool.targets.extend(targets1.objects + targets2.objects) pool.targets.extend(targets1.objects + targets2.objects)
command = PoolCommands() command = PoolCommands()
command.context = self.admin_context command.context = self.context
command.central_api = self.central_service command.central_api = self.central_service
command._update_zones(pool) command._update_zones(pool)
def test_create_new_pool(self):
pool = {
'name': 'new_pool',
'description': 'New PowerDNS Pool',
'attributes': {},
'ns_records': [
{'hostname': 'ns1-1.example.org.', 'priority': 1},
{'hostname': 'ns1-2.example.org.', 'priority': 2}
],
'nameservers': [
{'host': '192.0.2.2', 'port': 53}
],
'targets': [
{
'type': 'powerdns',
'description': 'PowerDNS Database Cluster',
'masters': [
{'host': '192.0.2.1', 'port': 5354}
],
'options': {
'host': '192.0.2.2', 'port': 53,
'connection': 'connection'
}
}
],
'also_notifies': [
{'host': '192.0.2.4', 'port': 53}
]
}
command = PoolCommands()
command.context = self.context
command.central_api = self.central_service
command._create_pool(pool, dry_run=False)
pool = self.central_service.find_pool(
self.admin_context, {'name': 'new_pool'}
)
self.assertEqual('new_pool', pool.name)
self.assertEqual('New PowerDNS Pool', pool.description)