diff options
Diffstat (limited to 'designate/manage/pool.py')
-rw-r--r-- | designate/manage/pool.py | 326 |
1 files changed, 176 insertions, 150 deletions
diff --git a/designate/manage/pool.py b/designate/manage/pool.py index 1c7344e5..c0553d60 100644 --- a/designate/manage/pool.py +++ b/designate/manage/pool.py @@ -13,13 +13,14 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -import sys from oslo_config import cfg from oslo_log import log as logging import oslo_messaging as messaging +import stevedore.exception import yaml +from designate.backend import base as backend_base from designate.central import rpcapi as central_rpcapi from designate import exceptions from designate.manage import base @@ -27,109 +28,76 @@ from designate import objects from designate.objects.adapters import DesignateAdapter from designate import policy from designate import rpc +from designate import utils LOG = logging.getLogger(__name__) - - CONF = cfg.CONF class PoolCommands(base.Commands): def __init__(self): super(PoolCommands, self).__init__() - self.output_msg = [''] + self.central_api = None + self.dry_run = False + self.skip_verify_drivers = False - # NOTE(jh): Cannot do this earlier because we are still missing the config - # at that point, see bug #1651576 - def _startup(self): + def _setup(self, dry_run=False, skip_verify_drivers=False): + self.dry_run = dry_run + self.skip_verify_drivers = skip_verify_drivers rpc.init(cfg.CONF) 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): - LOG.info('Updating zone masters for pool: %s', pool.id) - - def __get_masters_from_pool(pool): - masters = [] - for target in pool.targets: - for master in target.get('masters', []): - master = {'host': master['host'], 'port': master['port']} - found = False - for existing_master in masters: - if master == existing_master: - found = True - if not found: - masters.append(master) - return masters - - policy.init() - - self.context.all_tenants = True - zones = self.central_api.find_zones( - self.context, - criterion={'pool_id': pool.id}) - - for zone in zones: - zone.masters = objects.ZoneMasterList().from_list( - __get_masters_from_pool(pool) - ) - self.central_api.update_zone( - self.context, zone - ) - @base.args('--file', help='The path to the file the yaml output should be ' - 'written to', + 'written to', default='/etc/designate/pools.yaml') def generate_file(self, file): - self._startup() + self._setup() + try: pools = self.central_api.find_pools(self.context) + data = DesignateAdapter.render('YAML', pools) + self._write_config_to_file(file, data) + except messaging.exceptions.MessagingTimeout: LOG.critical( 'No response received from designate-central. ' 'Check it is running, and retry' ) - sys.exit(1) - with open(file, 'w') as stream: - yaml.dump( - DesignateAdapter.render('YAML', pools), - stream, - default_flow_style=False - ) + raise SystemExit(1) @base.args('--pool_id', help='ID of the pool to be examined', default=CONF['service:central'].default_pool_id) def show_config(self, pool_id): - self._startup() + self._setup() + + self.output_message.append('Pool Configuration:') + self.output_message.append('-------------------') + try: - pool = self.central_api.find_pool(self.context, {'id': pool_id}) + if not utils.is_uuid_like(pool_id): + self.output_message.append('Not a valid uuid: %s' % pool_id) + raise SystemExit(1) - print('Pool Configuration:') - print('-------------------') + pool = self.central_api.find_pool(self.context, {'id': pool_id}) - print(yaml.dump(DesignateAdapter.render('YAML', pool), - default_flow_style=False)) + self.output_message.append( + yaml.dump( + DesignateAdapter.render('YAML', pool), + default_flow_style=False + ) + ) + except exceptions.PoolNotFound: + self.output_message.append('Pool not found') + raise SystemExit(1) except messaging.exceptions.MessagingTimeout: LOG.critical( 'No response received from designate-central. ' 'Check it is running, and retry' ) - sys.exit(1) + raise SystemExit(1) + finally: + self._print_result() @base.args('--file', help='The path to the yaml file describing the pools', default='/etc/designate/pools.yaml') @@ -144,98 +112,156 @@ class PoolCommands(base.Commands): help='This will simulate what will happen when you run this command', action='store_true', default=False) - def update(self, file, delete, dry_run): - self._startup() - print('Updating Pools Configuration') - print('****************************') - - with open(file, 'r') as stream: - xpools = yaml.safe_load(stream) - - if dry_run: - self.output_msg.append('The following changes will occur:') - self.output_msg.append('*********************************') - - for xpool in xpools: - try: - if 'id' in xpool: - try: - pool = self.central_api.get_pool( - self.context, xpool['id'] - ) - except Exception as e: - LOG.critical( - 'Bad ID Supplied for pool. pool_id: ' - '%(pool)s message: %(res)s', - { - 'pool': xpool['id'], 'res': e - } - ) - continue - else: - pool = self.central_api.find_pool( - self.context, {'name': xpool['name']} - ) + @base.args( + '--skip-verify-drivers', + help='Don\'t verify the designate backend drivers', + action='store_true', + default=False) + def update(self, file, delete, dry_run=False, skip_verify_drivers=False): + self._setup(dry_run, skip_verify_drivers) - LOG.info('Updating existing pool: %s', pool) + try: + self.output_message.append('Updating Pools Configuration') + self.output_message.append('****************************') - # TODO(kiall): Move the below into the pool object + pools_data = self._load_config(file) - pool = DesignateAdapter.parse('YAML', xpool, pool) + if dry_run: + self.output_message.append('The following changes will occur:') + self.output_message.append('*********************************') - # TODO(graham): We should be doing a full validation, but right - # now there is quirks validating through nested objects. + for pool_data in pools_data: + self._create_or_update_pool(pool_data) - 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 delete: + pools = self.central_api.find_pools(self.context) + pools_in_db = {pool.name for pool in pools} + pools_in_yaml = {pool_data['name'] for pool_data in pools_data} + pools_to_delete = pools_in_db - pools_in_yaml + for pool_name in pools_to_delete: + self._delete_pool(pool_name) - if dry_run: - self.output_msg.append('Update Pool: %s' % pool) - else: - pool = self.central_api.update_pool(self.context, pool) - # Bug: Changes in the pool targets should trigger a - # zone masters update LP: #1879798. - self._update_zones(pool) + except exceptions.InvalidObject as e: + self.output_message.append(str(e)) + raise SystemExit(1) + except messaging.exceptions.MessagingTimeout: + LOG.critical( + 'No response received from designate-central. ' + 'Check it is running, and retry' + ) + raise SystemExit(1) + finally: + self._print_result() - except exceptions.PoolNotFound: - self._create_pool(xpool, dry_run) - except messaging.exceptions.MessagingTimeout: - LOG.critical( - 'No response received from designate-central. ' - 'Check it is running, and retry' - ) - sys.exit(1) + def _create_or_update_pool(self, pool_data): + try: + pool = self._get_pool(pool_data) + self._update_pool(pool_data, pool) - if delete: - pools = self.central_api.find_pools(self.context) - pools_in_db = {pool.name for pool in pools} - pools_in_yaml = {xpool['name'] for xpool in xpools} + except exceptions.PoolNotFound: + self._create_pool(pool_data) - pools_to_delete = pools_in_db - pools_in_yaml + def _get_pool(self, pool_data): + if 'id' in pool_data: + pool_id = pool_data['id'] + if not utils.is_uuid_like(pool_id): + self.output_message.append('Not a valid uuid: %s' % pool_id) + raise SystemExit(1) - for pool in pools_to_delete: - try: - p = self.central_api.find_pool( - self.context, - criterion={'name': pool}) + pool = self.central_api.get_pool( + self.context, pool_id + ) + else: + pool = self.central_api.find_pool( + self.context, {'name': pool_data['name']} + ) - if dry_run: - self.output_msg.append('Delete Pool: %s' % p) + return pool - else: - LOG.info('Deleting %s', p) - self.central_api.delete_pool(self.context, p.id) + def _create_pool(self, pool_data): + pool = DesignateAdapter.parse('YAML', pool_data, objects.Pool()) + self._validate_pool(pool) - except messaging.exceptions.MessagingTimeout: - LOG.critical( - 'No response received from designate-central. ' - 'Check it is running, and retry' - ) - sys.exit(1) + if self.dry_run: + self.output_message.append('Create Pool: %s' % pool) + else: + LOG.info('Creating new pool: %s', pool) + self.central_api.create_pool(self.context, pool) + + return pool + + def _update_pool(self, pool_data, pool): + pool = DesignateAdapter.parse('YAML', pool_data, pool) + self._validate_pool(pool) + + if self.dry_run: + self.output_message.append('Update Pool: %s' % pool) + else: + pool = self.central_api.update_pool(self.context, pool) + self._update_zones(pool) + + def _delete_pool(self, pool_name): + pool = self.central_api.find_pool( + self.context, criterion={'name': pool_name} + ) - for line in self.output_msg: - print(line) + if self.dry_run: + self.output_message.append('Delete Pool: %s' % pool_name) + else: + LOG.info('Deleting %s', pool_name) + self.central_api.delete_pool(self.context, pool.id) + + def _update_zones(self, pool): + LOG.info('Updating zone masters for pool: %s', pool.id) + + policy.init() + self.context.all_tenants = True + zones = self.central_api.find_zones( + self.context, criterion={'pool_id': pool.id} + ) + + for zone in zones: + zone.masters = objects.ZoneMasterList().from_list( + self._get_masters_from_pool(pool) + ) + self.central_api.update_zone(self.context, zone) + + def _validate_pool(self, pool): + for ns_record in pool.ns_records: + ns_record.validate() + + if not self.skip_verify_drivers: + for target in pool.targets: + try: + backend_base.Backend.get_driver(target.type) + except stevedore.exception.NoMatches: + self.output_message.append( + 'Unable to find designate backend driver type: ' + '%s' % target.type + ) + if not self.dry_run: + raise SystemExit(1) + + @staticmethod + def _get_masters_from_pool(pool): + masters = [] + for target in pool.targets: + for master in target.get('masters', []): + master = {'host': master['host'], 'port': master['port']} + found = False + for existing_master in masters: + if master == existing_master: + found = True + if not found: + masters.append(master) + return masters + + @staticmethod + def _load_config(filename): + with open(filename, 'r') as stream: + return yaml.safe_load(stream) + + @staticmethod + def _write_config_to_file(filename, data): + with open(filename, 'w') as stream: + yaml.dump(data, stream, default_flow_style=False) |