def process_notification(self, context, event_type, payload): # Do something with the notification.. e.g: zone_id = cfg.CONF[self.name].zone_id zone_name = cfg.CONF[self.name].zone_name record_name = '%s.%s' % (payload['instance_id'], zone_name) context = DesignateContext().elevated() context.all_tenants = True # context.edit_managed_records = True for fixed_ip in payload['fixed_ips']: recordset_values = { 'zone_id': zone_id, 'name': record_name, 'type': 'A' if fixed_ip['version'] == 4 else 'AAAA' } record_values = { 'data': fixed_ip['address'], } recordset = self._find_or_create_recordset(context, **recordset_values) self.central_api.create_record(context, zone_id, recordset['id'], Record(**record_values))
def _delete(self, zone_id, resource_id=None, resource_type='instance', criterion=None): """ Handle a generic delete of a fixed ip within a zone :param zone_id: The ID of the designate zone. :param resource_id: The managed resource ID :param resource_type: The managed resource type :param criterion: Criterion to search and destroy records """ criterion = criterion or {} context = DesignateContext().elevated() context.all_tenants = True context.edit_managed_records = True criterion.update({ 'zone_id': zone_id, 'managed': True, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_id': resource_id, 'managed_resource_type': resource_type }) records = self.central_api.find_records(context, criterion) for record in records: LOG.debug('Deleting record %s', record['id']) self.central_api.delete_record(context, zone_id, record['recordset_id'], record['id'])
def process_request(self, request): headers = request.headers try: if headers['X-Identity-Status'] is 'Invalid': #TODO(graham) fix the return to use non-flask resources return flask.Response(status=401) except KeyError: #If the key is valid, Keystone does not include this header at all pass roles = headers.get('X-Roles').split(',') context = DesignateContext(auth_token=headers.get('X-Auth-Token'), user=headers.get('X-User-ID'), tenant=headers.get('X-Tenant-ID'), roles=roles) # Store the context where oslo-log exepcts to find it. local.store.context = context # Attempt to sudo, if requested. sudo_tenant_id = headers.get('X-Designate-Sudo-Tenant-ID', None) if sudo_tenant_id and (uuidutils.is_uuid_like(sudo_tenant_id) or sudo_tenant_id.isdigit()): context.sudo(sudo_tenant_id) # Attach the context to the request environment request.environ['context'] = context
def _create(self, addresses, extra, zone_id, managed=True, resource_type=None, resource_id=None): """ Create a a record from addresses :param addresses: Address objects like {'version': 4, 'ip': '10.0.0.1'} :param extra: Extra data to use when formatting the record :param managed: Is it a managed resource :param resource_type: The managed resource type :param resource_id: The managed resource ID """ if not managed: LOG.warning( _LW( "Deprecation notice: Unmanaged designate-sink records are " "being deprecated please update the call " "to remove managed=False" ) ) LOG.debug("Using Zone ID: %s", zone_id) zone = self.get_zone(zone_id) LOG.debug("Domain: %r", zone) data = extra.copy() LOG.debug("Event data: %s", data) data["zone"] = zone["name"] context = DesignateContext().elevated() context.all_tenants = True context.edit_managed_records = True for addr in addresses: event_data = data.copy() event_data.update(self._get_ip_data(addr)) for fmt in cfg.CONF[self.name].get("format"): recordset_values = { "zone_id": zone["id"], "name": fmt % event_data, "type": "A" if addr["version"] == 4 else "AAAA", } recordset = self._find_or_create_recordset(context, **recordset_values) record_values = {"data": addr["address"]} if managed: record_values.update( { "managed": managed, "managed_plugin_name": self.get_plugin_name(), "managed_plugin_type": self.get_plugin_type(), "managed_resource_type": resource_type, "managed_resource_id": resource_id, } ) LOG.debug("Creating record in %s / %s with values %r", zone["id"], recordset["id"], record_values) self.central_api.create_record(context, zone["id"], recordset["id"], Record(**record_values))
def _create(self, addresses, extra, zone_id, managed=True, resource_type=None, resource_id=None): """ Create a a record from addresses :param addresses: Address objects like {'version': 4, 'ip': '10.0.0.1'} :param extra: Extra data to use when formatting the record :param managed: Is it a managed resource :param resource_type: The managed resource type :param resource_id: The managed resource ID """ if not managed: LOG.warning(_LW( 'Deprecation notice: Unmanaged designate-sink records are ' 'being deprecated please update the call ' 'to remove managed=False')) LOG.debug('Using Zone ID: %s' % zone_id) zone = self.get_zone(zone_id) LOG.debug('Zone: %r' % zone) data = extra.copy() LOG.debug('Event data: %s' % data) data['zone'] = zone['name'] context = DesignateContext().elevated() context.all_tenants = True context.edit_managed_records = True for addr in addresses: event_data = data.copy() event_data.update(self._get_ip_data(addr)) for fmt in cfg.CONF[self.name].get('format'): recordset_values = { 'zone_id': zone['id'], 'name': fmt % event_data, 'type': 'A' if addr['version'] == 4 else 'AAAA'} recordset = self._find_or_create_recordset( context, **recordset_values) record_values = { 'data': addr['address']} if managed: record_values.update({ 'managed': managed, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': resource_type, 'managed_resource_id': resource_id}) LOG.debug('Creating record in %s / %s with values %r' % (zone['id'], recordset['id'], record_values)) self.central_api.create_record(context, zone['id'], recordset['id'], Record(**record_values))
def _create(self, addresses, extra, zone_id, resource_type=None, resource_id=None): """ Create a record from addresses :param addresses: Address objects like {'version': 4, 'ip': '10.0.0.1'} :param extra: Extra data to use when formatting the record :param zone_id: The ID of the designate zone. :param resource_type: The managed resource type :param resource_id: The managed resource ID """ LOG.debug('Using Zone ID: %s', zone_id) zone = self.get_zone(zone_id) LOG.debug('Domain: %r', zone) data = extra.copy() LOG.debug('Event data: %s', data) data['zone'] = zone['name'] context = DesignateContext().elevated() context.all_tenants = True context.edit_managed_records = True for addr in addresses: event_data = data.copy() event_data.update(self._get_ip_data(addr)) if addr['version'] == 4: format = self._get_formatv4() else: format = self._get_formatv6() for fmt in format: recordset_values = { 'zone_id': zone['id'], 'name': fmt % event_data, 'type': 'A' if addr['version'] == 4 else 'AAAA'} recordset = self._find_or_create_recordset( context, **recordset_values) record_values = { 'data': addr['address'], 'managed': True, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': resource_type, 'managed_resource_id': resource_id } LOG.debug('Creating record in %s / %s with values %r', zone['id'], recordset['id'], record_values) self.central_api.create_record(context, zone['id'], recordset['id'], Record(**record_values))
def periodic_sync(self): """ :return: None """ # NOTE(kiall): Only run this periodic task on the pool leader if self._pool_election.is_leader: context = DesignateContext.get_admin_context(all_tenants=True) LOG.debug("Starting Periodic Synchronization") criterion = { 'pool_id': CONF['service:pool_manager'].pool_id, 'status': '!%s' % ERROR_STATUS } periodic_sync_seconds = \ CONF['service:pool_manager'].periodic_sync_seconds if periodic_sync_seconds is not None: # Generate the current serial, will provide a UTC Unix TS. current = utils.increment_serial() criterion['serial'] = ">%s" % (current - periodic_sync_seconds) zones = self.central_api.find_zones(context, criterion) try: for zone in zones: # TODO(kiall): If the zone was created within the last # periodic_sync_seconds, attempt to recreate # to fill in targets which may have failed. self.update_zone(context, zone) except Exception: LOG.exception(_LE('An unhandled exception in periodic ' 'synchronization occurred.'))
def _delete(self, managed=True, resource_id=None, resource_type='instance', criterion={}): """ Handle a generic delete of a fixed ip within a domain :param criterion: Criterion to search and destroy records """ context = DesignateContext.get_admin_context() if managed: criterion.update({ 'managed': managed, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_id': resource_id, 'managed_resource_type': resource_type }) records = central_api.find_records(context, cfg.CONF[self.name].domain_id, criterion) for record in records: LOG.debug('Deleting record %s' % record['id']) central_api.delete_record(context, cfg.CONF[self.name].domain_id, record['id'])
def periodic_recovery(self): """ :return: None """ # TODO(kiall): Replace this inter-process-lock with a distributed # lock, likely using the tooz library - see bug 1445127. with lockutils.lock('periodic_recovery', external=True, delay=30): context = DesignateContext.get_admin_context(all_tenants=True) LOG.debug("Starting Periodic Recovery") try: # Handle Deletion Failures domains = self._get_failed_domains(context, DELETE_ACTION) for domain in domains: self.delete_domain(context, domain) # Handle Creation Failures domains = self._get_failed_domains(context, CREATE_ACTION) for domain in domains: self.create_domain(context, domain) # Handle Update Failures domains = self._get_failed_domains(context, UPDATE_ACTION) for domain in domains: self.update_domain(context, domain) except Exception: LOG.exception(_LE('An unhandled exception in periodic ' 'recovery occurred'))
def load_pool(self, pool_id): # Build the Pool (and related) Object from Config context = DesignateContext.get_admin_context() pool = None has_targets = False while not has_targets: try: pool = self.central_api.get_pool(context, pool_id) if len(pool.targets) > 0: has_targets = True else: LOG.error("No targets for %s found.", pool) time.sleep(5) # Pool data may not have migrated to the DB yet except exceptions.PoolNotFound: LOG.error("Pool ID %s not found.", pool_id) time.sleep(5) # designate-central service may not have started yet except messaging.exceptions.MessagingTimeout: time.sleep(0.2) return self._setup_target_backends(pool)
def start(self): # Build the Pool (and related) Object from Config context = DesignateContext.get_admin_context() pool_id = CONF['service:pool_manager'].pool_id has_targets = False # TODO(kiall): This block of code should be replaced with a cleaner, # limited version. e.g. should retry for X minutes, and # backoff rather than fixed retry intervals. while not has_targets: try: self.pool = self.central_api.get_pool(context, pool_id) if len(self.pool.targets) > 0: has_targets = True else: LOG.error(_LE("No targets for %s found."), self.pool) time.sleep(5) # Pool data may not have migrated to the DB yet except exceptions.PoolNotFound: LOG.error(_LE("Pool ID %s not found."), pool_id) time.sleep(5) # designate-central service may not have started yet except messaging.exceptions.MessagingTimeout: time.sleep(0.2) # designate-central failed in an unknown way, don't allow another # failing / not started service to cause pool-manager to crash. except Exception: LOG.exception(_LE("An unknown exception occurred while " "fetching pool details")) time.sleep(5) # Create the necessary Backend instances for each target self._setup_target_backends() for target in self.pool.targets: self.target_backends[target.id].start() super(Service, self).start() # Setup a Leader Election, use for ensuring certain tasks are executed # on exactly one pool-manager instance at a time] self._pool_election = coordination.LeaderElection( self._coordinator, '%s:%s' % (self.service_name, self.pool.id)) self._pool_election.start() if CONF['service:pool_manager'].enable_recovery_timer: interval = CONF['service:pool_manager'].periodic_recovery_interval LOG.info(_LI('Starting periodic recovery timer every' ' %(interval)s s') % {'interval': interval}) self.tg.add_timer(interval, self.periodic_recovery, interval) if CONF['service:pool_manager'].enable_sync_timer: interval = CONF['service:pool_manager'].periodic_sync_interval LOG.info(_LI('Starting periodic synchronization timer every' ' %(interval)s s') % {'interval': interval}) self.tg.add_timer(interval, self.periodic_sync, interval)
def periodic_recovery(self): """ :return: None """ # NOTE(kiall): Only run this periodic task on the pool leader if self._pool_election.is_leader: context = DesignateContext.get_admin_context(all_tenants=True) LOG.debug("Starting Periodic Recovery") try: # Handle Deletion Failures zones = self._get_failed_zones(context, DELETE_ACTION) for zone in zones: self.delete_zone(context, zone) # Handle Creation Failures zones = self._get_failed_zones(context, CREATE_ACTION) for zone in zones: self.create_zone(context, zone) # Handle Update Failures zones = self._get_failed_zones(context, UPDATE_ACTION) for zone in zones: self.update_zone(context, zone) except Exception: LOG.exception(_LE('An unhandled exception in periodic ' 'recovery occurred'))
def __init__(self, target): super(Backend, self).__init__() self.target = target self.options = target.options self.masters = target.masters # TODO(kiall): Context's should never be shared accross requests. self.admin_context = DesignateContext.get_admin_context() self.admin_context.all_tenants = True
def test_invalid_event_type(self): context = DesignateContext.get_admin_context(all_tenants=True) if not hasattr(self, 'plugin'): raise NotImplementedError event_type = 'invalid' self.assertNotIn(event_type, self.plugin.get_event_types()) with testtools.ExpectedException(ValueError): self.plugin.process_notification(context, event_type, 'payload')
def __init__(self): # Get a storage connection storage_driver = cfg.CONF['service:mdns'].storage_driver self.storage = storage.get_storage(storage_driver) self.admin_context = DesignateContext.get_admin_context( all_tenants=True) # Fake request is used to send a response when we cannot decipher the # request. self._fake_request = dns.message.make_query('unknown', dns.rdatatype.A)
def _delete(self, zone_id, managed=True, resource_id=None, resource_type='instance', criterion=None): """ Handle a generic delete of a fixed ip within a zone :param zone_id: The ID of the designate zone. :param managed: Is it a managed resource :param resource_id: The managed resource ID :param resource_type: The managed resource type :param criterion: Criterion to search and destroy records """ if not managed: LOG.warning( 'Deprecation notice: Unmanaged designate-sink records are ' 'being deprecated please update the call ' 'to remove managed=False') criterion = criterion or {} context = DesignateContext().elevated() context.all_tenants = True context.edit_managed_records = True criterion.update({'zone_id': zone_id}) if managed: criterion.update({ 'managed': managed, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_id': resource_id, 'managed_resource_type': resource_type }) records = self.central_api.find_records(context, criterion) for record in records: LOG.debug('Deleting record %s', record['id']) self.central_api.delete_record(context, zone_id, record['recordset_id'], record['id'])
def _delete(self, zone_id, managed=True, resource_id=None, resource_type="instance", criterion=None): """ Handle a generic delete of a fixed ip within a zone :param criterion: Criterion to search and destroy records """ if not managed: LOG.warning( _LW( "Deprecation notice: Unmanaged designate-sink records are " "being deprecated please update the call " "to remove managed=False" ) ) criterion = criterion or {} context = DesignateContext().elevated() context.all_tenants = True context.edit_managed_records = True criterion.update({"zone_id": zone_id}) if managed: criterion.update( { "managed": managed, "managed_plugin_name": self.get_plugin_name(), "managed_plugin_type": self.get_plugin_type(), "managed_resource_id": resource_id, "managed_resource_type": resource_type, } ) records = self.central_api.find_records(context, criterion) for record in records: LOG.debug("Deleting record %s", record["id"]) self.central_api.delete_record(context, zone_id, record["recordset_id"], record["id"])
def process_request(self, request): headers = request.headers roles = headers.get('X-Roles').split(',') context = DesignateContext(auth_token=headers.get('X-Auth-Token'), user=headers.get('X-User-ID'), tenant=headers.get('X-Tenant-ID'), roles=roles) # Store the context where oslo-log exepcts to find it. local.store.context = context # Attempt to sudo, if requested. sudo_tenant_id = headers.get('X-Designate-Sudo-Tenant-ID', None) if sudo_tenant_id and (uuidutils.is_uuid_like(sudo_tenant_id) or sudo_tenant_id.isdigit()): context.sudo(sudo_tenant_id) # Attach the context to the request environment request.environ['context'] = context
def start(self): # Build the Pool (and related) Object from Config context = DesignateContext.get_admin_context() pool_id = CONF['service:pool_manager'].pool_id has_targets = False while not has_targets: try: self.pool = self.central_api.get_pool(context, pool_id) if len(self.pool.targets) > 0: has_targets = True else: LOG.error(_LE("No targets for %s found."), self.pool) time.sleep(5) # Pool data may not have migrated to the DB yet except exceptions.PoolNotFound: LOG.error(_LE("Pool ID %s not found."), pool_id) time.sleep(5) # designate-central service may not have started yet except messaging.exceptions.MessagingTimeout: time.sleep(0.2) # Create the necessary Backend instances for each target self._setup_target_backends() for target in self.pool.targets: self.target_backends[target.id].start() super(Service, self).start() # Setup a Leader Election, use for ensuring certain tasks are executed # on exactly one pool-manager instance at a time] self._pool_election = coordination.LeaderElection( self._coordinator, '%s:%s' % (self.service_name, self.pool.id)) self._pool_election.start() if CONF['service:pool_manager'].enable_recovery_timer: interval = CONF['service:pool_manager'].periodic_recovery_interval LOG.info(_LI('Starting periodic recovery timer every' ' %(interval)s s') % {'interval': interval}) self.tg.add_timer(interval, self.periodic_recovery, interval) if CONF['service:pool_manager'].enable_sync_timer: interval = CONF['service:pool_manager'].periodic_sync_interval LOG.info(_LI('Starting periodic synchronization timer every' ' %(interval)s s') % {'interval': interval}) self.tg.add_timer(interval, self.periodic_sync, interval)
def _create(self, addresses, extra, managed=True, resource_type=None, resource_id=None): """ Create a a record from addresses :param addresses: Address objects like {'version': 4, 'ip': '10.0.0.1'} :param extra: Extra data to use when formatting the record :param managed: Is it a managed resource :param resource_type: The managed resource type :param resource_id: The managed resource ID """ LOG.debug('Using DomainID: %s' % cfg.CONF[self.name].domain_id) domain = self.get_domain(cfg.CONF[self.name].domain_id) LOG.debug('Domain: %r' % domain) data = extra.copy() LOG.debug('Event data: %s' % data) data['domain'] = domain['name'] context = DesignateContext.get_admin_context(all_tenants=True) for addr in addresses: event_data = data.copy() event_data.update(get_ip_data(addr)) recordset_values = { 'domain_id': domain['id'], 'name': self._get_format() % event_data, 'type': 'A' if addr['version'] == 4 else 'AAAA'} recordset = self._find_or_create_recordset( context, **recordset_values) record_values = { 'data': addr['address']} if managed: record_values.update({ 'managed': managed, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': resource_type, 'managed_resource_id': resource_id}) LOG.debug('Creating record in %s / %s with values %r' % (domain['id'], recordset['id'], record_values)) self.central_api.create_record(context, domain['id'], recordset['id'], Record(**record_values))
def process_request(self, request): headers = request.headers try: if headers["X-Identity-Status"] is "Invalid": # TODO(graham) fix the return to use non-flask resources return flask.Response(status=401) except KeyError: # If the key is valid, Keystone does not include this header at all pass if headers.get("X-Service-Catalog"): catalog = json.loads(headers.get("X-Service-Catalog")) else: catalog = None roles = headers.get("X-Roles").split(",") context = DesignateContext( auth_token=headers.get("X-Auth-Token"), user=headers.get("X-User-ID"), tenant=headers.get("X-Tenant-ID"), roles=roles, service_catalog=catalog, ) # Store the context where oslo-log exepcts to find it. local.store.context = context # Attempt to sudo, if requested. sudo_tenant_id = headers.get("X-Designate-Sudo-Tenant-ID", None) if sudo_tenant_id and (uuidutils.is_uuid_like(sudo_tenant_id) or sudo_tenant_id.isdigit()): context.sudo(sudo_tenant_id) # Attach the context to the request environment request.environ["context"] = context
def __init__(self, target): super(Backend, self).__init__() self.target = target self.options = target.options self.masters = target.masters self.host = self.options.get('host', '127.0.0.1') self.port = int(self.options.get('port', 53)) # TODO(kiall): Context's should never be shared across requests. self.admin_context = DesignateContext.get_admin_context() self.admin_context.all_tenants = True # Options for sending NOTIFYs self.timeout = CONF['service:pool_manager'].poll_timeout self.retry_interval = CONF['service:pool_manager'].poll_retry_interval self.max_retries = CONF['service:pool_manager'].poll_max_retries self.delay = CONF['service:pool_manager'].poll_delay
def main(): args = parse_args() logging.setup('cirrus_floatingip') LOG.logger.setLevel('DEBUG') load_config(args.config_file) kc = keystone_c.Client( username=args.username, password=args.password, tenant_name=args.tenantname, auth_url=args.authurl, endpoint_type=args.endpoint_type, region_name=args.regionname) policy.init() rpc.init(cfg.CONF) context = DesignateContext.get_admin_context(tenant=kc.auth_tenant_id) handler = cirrus.CirrusFloatingIPHandler() args.func(kc, handler, context, args)
def _create_reverse_record(self, context, managed, host_fqdn, interface): LOG.info('Create reverse record for interface: %s and address: %s', interface['label'], interface['address']) host_reverse_fqdn = self._get_reverse_fqdn(interface['address'], interface['version']) LOG.info('Create reverse record: %s', host_reverse_fqdn) reverse_domains = self._get_reverse_domains(host_reverse_fqdn) admin_context = DesignateContext.get_admin_context(all_tenants=True) for reverse_domain in reverse_domains: LOG.info('Create reverse record for domain: %s', reverse_domain.name) try: recordset = self.central_api.create_recordset(admin_context, reverse_domain.id, RecordSet(name=host_reverse_fqdn, type='PTR')) except exceptions.DuplicateRecordSet: LOG.warn('The reverse record: %s was already registered', host_reverse_fqdn) else: record_values = dict(managed, data=host_fqdn) LOG.debug('Creating reverse record in %s / %s with values %r', reverse_domain.id, recordset['id'], record_values) self.central_api.create_record(admin_context, reverse_domain.id, recordset['id'], Record(**record_values))
def _create(self, addresses, extra, managed=True, resource_type=None, resource_id=None): """ Create a a record from addresses :param addresses: Address objects like {'version': 4, 'ip': '10.0.0.1'} :param extra: Extra data to use when formatting the record :param managed: Is it a managed resource :param resource_type: The managed resource type :param resource_id: The managed resource ID """ LOG.debug('Using DomainID: %s' % cfg.CONF[self.name].domain_id) domain = self.get_domain(cfg.CONF[self.name].domain_id) LOG.debug('Domain: %r' % domain) data = extra.copy() data['domain'] = domain['name'] context = DesignateContext.get_admin_context() for addr in addresses: record_data = data.copy() record_data.update(get_ip_data(addr)) record_name = self._get_format() % record_data record_values = { 'type': 'A' if addr['version'] == 4 else 'AAAA', 'name': record_name, 'data': addr['address']} if managed: record_values.update({ 'managed': managed, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': resource_type, 'managed_resource_id': resource_id}) central_api.create_record(context, domain['id'], record_values)
def periodic_sync(self): """ :return: None """ # TODO(kiall): Replace this inter-process-lock with a distributed # lock, likely using the tooz library - see bug 1445127. with lockutils.lock('periodic_sync', external=True, delay=30): context = DesignateContext.get_admin_context(all_tenants=True) LOG.debug("Starting Periodic Synchronization") criterion = { 'pool_id': CONF['service:pool_manager'].pool_id, 'status': '!%s' % ERROR_STATUS } periodic_sync_seconds = \ CONF['service:pool_manager'].periodic_sync_seconds if periodic_sync_seconds is not None: # Generate the current serial, will provide a UTC Unix TS. current = utils.increment_serial() criterion['serial'] = ">%s" % (current - periodic_sync_seconds) domains = self.central_api.find_domains(context, criterion) try: for domain in domains: # TODO(kiall): If the domain was created within the last # periodic_sync_seconds, attempt to recreate # to fill in targets which may have failed. self.update_domain(context, domain) except Exception: LOG.exception(_LE('An unhandled exception in periodic ' 'synchronization occurred.'))
def _delete(self, extra, managed=True, resource_id=None, resource_type='instance', criterion={}): """ Handle a generic delete of a fixed ip within a zone :param criterion: Criterion to search and destroy records """ zone = self.get_zone(cfg.CONF[self.name].domain_id) data = extra.copy() LOG.debug('Event data: %s' % data) data['zone'] = zone['name'] data['project_name'] = data['tenant_id'] event_data = data.copy() fqdn = cfg.CONF[self.name].fqdn_format % event_data fqdn = fqdn.rstrip('.') keystone = keystone_client.Client( session=self._get_keystone_session(), interface='public', connect_retries=5) region_recs = keystone.regions.list() regions = [region.id for region in region_recs] if len(regions) > 1: # We need to make sure this VM doesn't exist in another region. If it does # then we don't want to purge anything because we'll break that one. for region in regions: if region == cfg.CONF[self.name].region: continue nova = novaclient.Client('2', session=self._get_keystone_session(data['tenant_id']), region_name=region) servers = nova.servers.list() servernames = [server.name for server in servers] if event_data['hostname'] in servernames: LOG.warning("Skipping cleanup of %s because it is also present in region %s" % (fqdn, region)) return # Clean puppet keys for deleted instance if cfg.CONF[self.name].puppet_master_host: LOG.debug('Cleaning puppet key %s' % fqdn) self._run_remote_command(cfg.CONF[self.name].puppet_master_host, cfg.CONF[self.name].certmanager_user, 'sudo puppet cert clean %s' % pipes.quote(fqdn)) # Clean up the puppet config for this instance, if there is one self._delete_puppet_config(data['tenant_id'], fqdn) self._delete_puppet_git_config(data['tenant_id'], fqdn) # For good measure, look around for things associated with the old domain as well if cfg.CONF[self.name].legacy_domain_id: legacy_zone = self.get_zone(cfg.CONF[self.name].legacy_domain_id) legacy_data = data.copy() legacy_data['zone'] = legacy_zone['name'] legacy_fqdn = cfg.CONF[self.name].fqdn_format % legacy_data legacy_fqdn = legacy_fqdn.rstrip('.') if cfg.CONF[self.name].puppet_master_host: LOG.debug('Cleaning puppet key %s' % legacy_fqdn) self._run_remote_command(cfg.CONF[self.name].puppet_master_host, cfg.CONF[self.name].certmanager_user, 'sudo puppet cert clean %s' % pipes.quote(legacy_fqdn)) # Clean up the puppet config for this instance, if there is one self._delete_puppet_config(legacy_data['tenant_id'], legacy_fqdn) self._delete_puppet_git_config(legacy_data['tenant_id'], legacy_fqdn) # Finally, delete any proxy records pointing to this instance. # # For that, we need the IP which we can dig out of the old DNS record. crit = criterion.copy() # Make sure we only look at forward records crit['zone_id'] = cfg.CONF[self.name].domain_id if managed: crit.update({ 'managed': managed, 'managed_resource_id': resource_id, 'managed_resource_type': resource_type }) context = DesignateContext().elevated() context.all_tenants = True context.edit_managed_records = True records = central_api.find_records(context, crit) # We only care about the IP, and that's the same in both records. ip = records[0].data LOG.debug("Cleaning up proxy records for IP %s" % ip) try: self._delete_proxies_for_ip(data['project_name'], ip) except requests.exceptions.ConnectionError: LOG.warning("Caught exception when deleting proxy records", exc_info=True)
def process_notification(self, context, event_type, payload): LOG.debug('NeutronFloatingHandler: Event type received: %s', event_type) LOG.debug('NeutronFloatingHandler: Event body received: %s', payload) zone_id = cfg.CONF[self.name].zone_id zone = self.get_zone(zone_id) # Get a list all all zones owned by the zone tenant owner. # This is so we can find the reverse DNS zone. elevated_context = DesignateContext.get_admin_context( all_tenants=True, edit_managed_records=True) criterion = { "tenant_id": cfg.CONF[self.name].zone_owner_tenant_id, } zones = self.central_api.find_zones(elevated_context, criterion) if event_type.startswith('floatingip.delete'): self._delete(zone_id=zone_id, resource_id=payload['floatingip_id'], resource_type='instance') elif event_type.startswith('floatingip.update'): floatingip = payload['floatingip']['floating_ip_address'] # Calculate Reverse Address v4address = ipaddress.ip_address(floatingip) reverse_address = v4address.reverse_pointer + '.' reverse_network = '.'.join(reverse_address.split('.')[1:]) reverse_id = None # Loop through all zones and see if one matches the reverse zone reverse_id = None for i in zones: if i.name == reverse_network: reverse_id = i.id if payload['floatingip']['fixed_ip_address']: # Create a nova client username = cfg.CONF[self.name].admin_user password = cfg.CONF[self.name].admin_password tenant_name = cfg.CONF[self.name].admin_tenant_name auth_url = cfg.CONF[self.name].auth_url auth = v3.Password(username=username, password=password, project_name=tenant_name, project_domain_name='default', user_domain_name='default', auth_url=auth_url) sess = session.Session(auth=auth) nvc = nova_c.Client(2.1, session=sess) # Search for an instance with the matching fixed ip search_opts = { 'ip': payload['floatingip']['fixed_ip_address'], 'status': 'ACTIVE', 'all_tenants': True, 'tenant_id': payload['floatingip']['tenant_id'], } instances = nvc.servers.list(detailed=True, search_opts=search_opts) if len(instances) == 1: instance = instances[0] # Get the ec2 id of the instance and build the hostname from it ec2id = getattr(instance, 'OS-EXT-SRV-ATTR:instance_name') ec2id = ec2id.split('-', 1)[1].lstrip('0') hostname = '%s.%s' % (ec2id, zone['name']) # create a recordset record_type = 'A' recordset_values = { 'zone_id': zone_id, 'name': hostname, 'type': record_type } recordset = self._find_or_create_recordset( elevated_context, **recordset_values) record_values = { 'data': floatingip, 'managed': True, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': 'instance', 'managed_resource_id': payload['floatingip']['id'], 'managed_extra': 'instance:%s' % (getattr(instance, 'id')), } LOG.debug('Creating record in %s / %s with values %r' % (zone_id, recordset['id'], record_values)) self.central_api.create_record(elevated_context, zone_id, recordset['id'], Record(**record_values)) # create a reverse recordset record_type = 'PTR' if reverse_id == None: LOG.debug('UNABLE TO DETERMINE REVERSE ZONE: %s', payload['floatingip']) else: recordset_values = { 'zone_id': reverse_id, 'name': reverse_address, 'type': record_type } recordset = self._find_or_create_recordset( elevated_context, **recordset_values) record_values = { 'data': hostname, 'managed': True, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': 'instance', 'managed_resource_id': payload['floatingip']['id'], 'managed_extra': 'instance:%s' % (getattr(instance, 'id')), } LOG.debug('Creating record in %s / %s with values %r' % (reverse_id, recordset['id'], record_values)) self.central_api.create_record(elevated_context, reverse_id, recordset['id'], Record(**record_values)) else: LOG.debug('Deleting records for %s / %s' % (zone_id, payload['floatingip']['id'])) self._delete(zone_id=zone_id, resource_id=payload['floatingip']['id'], resource_type='instance') if reverse_id == None: LOG.debug('UNABLE TO DETERMINE REVERSE ZONE: %s', payload['floatingip']) else: LOG.debug('Deleting records for %s / %s' % (reverse_id, payload['floatingip']['id'])) self._delete(zone_id=reverse_id, resource_id=payload['floatingip']['id'], resource_type='instance')
def _get_admin_context_all_tenants(self): return DesignateContext.get_admin_context(all_tenants=True)
def _create(self, addresses, extra, managed=True, resource_type=None, resource_id=None): """ Create a a record from addresses :param addresses: Address objects like {'version': 4, 'ip': '10.0.0.1'} :param extra: Extra data to use when formatting the record :param managed: Is it a managed resource :param resource_type: The managed resource type :param resource_id: The managed resource ID """ if not managed: LOG.warning( _LW('Deprecation notice: Unmanaged designate-sink records are ' 'being deprecated please update the call ' 'to remove managed=False')) LOG.debug('Using DomainID: %s' % cfg.CONF[self.name].domain_id) domain = self.get_domain(cfg.CONF[self.name].domain_id) LOG.debug('Domain: %r' % domain) data = extra.copy() LOG.debug('Event data: %s' % data) data['domain'] = domain['name'] context = DesignateContext.get_admin_context(all_tenants=True) for addr in addresses: event_data = data.copy() event_data.update(get_ip_data(addr)) recordset_values = { 'domain_id': domain['id'], 'name': self._get_format() % event_data, 'type': 'A' if addr['version'] == 4 else 'AAAA' } recordset = self._find_or_create_recordset(context, **recordset_values) record_values = {'data': addr['address']} if managed: record_values.update({ 'managed': managed, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': resource_type, 'managed_resource_id': resource_id }) LOG.debug('Creating record in %s / %s with values %r' % (domain['id'], recordset['id'], record_values)) self.central_api.create_record(context, domain['id'], recordset['id'], Record(**record_values))
def process_notification(self, context, event_type, payload): # Take notification and create a record LOG.debug('FloatingV4Handler: Event type received: %s', event_type) zone = self.get_zone(cfg.CONF[self.name].zone_id) domain_id = zone['id'] # Gather extra information (need to compare payload['floating_ip'] and figure out which zone it fits in. # The domains are owned by admin so we need admin context? elevated_context = DesignateContext.get_admin_context(all_tenants=True, edit_managed_records=True) criterion = { "tenant_id": cfg.CONF[self.name].admin_tenant_id, } zones = self.central_api.find_zones(elevated_context, criterion) # Calculate Reverse Address v4address = ipaddress.ip_address(payload['floating_ip']) reverse_address = v4address.reverse_pointer + '.' reverse_network = '.'.join(reverse_address.split('.')[1:]) reverse_id = None for i in zones: if i.name == reverse_network: reverse_id = i.id if event_type == 'network.floating_ip.associate': LOG.debug('FloatingV4Handler: Creating A record for %s on %s', payload['floating_ip'], payload['instance_id']) # Get ec2id for hostname (must be admin kc = keystone_c.Client(username=cfg.CONF['keystone_authtoken'].admin_user, password=cfg.CONF['keystone_authtoken'].admin_password, tenant_name=cfg.CONF['keystone_authtoken'].admin_tenant_name, auth_url = cfg.CONF['handler:nova_fixed_v6'].auth_uri) nova_endpoint = kc.service_catalog.url_for(service_type='compute', endpoint_type='internalURL') nvc = nova_c.Client(auth_token=kc.auth_token, tenant_id=kc.auth_tenant_id, bypass_url=nova_endpoint) server_info = nvc.servers.get(payload['instance_id']) # Determine the hostname ec2id = getattr(server_info, 'OS-EXT-SRV-ATTR:instance_name') ec2id = ec2id.split('-', 1)[1].lstrip('0') hostname = '%s.%s' % (ec2id, zone['name']) record_type = 'A' recordset_values = { 'zone_id': domain_id, 'name': hostname, 'type': record_type } recordset = self._find_or_create_recordset(elevated_context, **recordset_values) record_values = { 'data': payload['floating_ip'], 'managed': True, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': 'instance', 'managed_resource_id': payload['instance_id'] } LOG.debug('Creating record in %s / %s with values %r' % (domain_id, recordset['id'], record_values)) self.central_api.create_record(elevated_context, domain_id, recordset['id'], Record(**record_values)) # Reverse Record record_type = 'PTR' if reverse_id == None: LOG.debug('UNABLE TO DETERMINE REVERSE ZONE: %s', payload['floating_ip']) else: recordset_values = { 'zone_id': reverse_id, 'name': reverse_address, 'type': record_type } recordset = self._find_or_create_recordset(elevated_context, **recordset_values) record_values = { 'data': hostname, 'managed': True, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': 'instance', 'managed_resource_id': payload['instance_id'] } LOG.debug('Creating record in %s / %s with values %r' % (reverse_id, recordset['id'], record_values)) self.central_api.create_record(elevated_context, reverse_id, recordset['id'], Record(**record_values)) elif event_type == 'network.floating_ip.disassociate': LOG.debug('FloatingV4Handler: Deleting A record for %s on %s', payload['floating_ip'], payload['instance_id']) self._delete(zone_id=domain_id, resource_id=payload['instance_id'], resource_type='instance') if reverse_id == None: LOG.debug('UNABLE TO DETERMINE REVERSE ZONE: %s', payload['floating_ip']) else: self._delete(zone_id=reverse_id, resource_id=payload['instance_id'], resource_type='instance')
def get_admin_context(self): return DesignateContext.get_admin_context()
def start(self): # Build the Pool (and related) Object from Config context = DesignateContext.get_admin_context() pool_id = CONF['service:pool_manager'].pool_id has_targets = False # TODO(kiall): This block of code should be replaced with a cleaner, # limited version. e.g. should retry for X minutes, and # backoff rather than fixed retry intervals. while not has_targets: try: self.pool = self.central_api.get_pool(context, pool_id) if len(self.pool.targets) > 0: has_targets = True else: LOG.error(_LE("No targets for %s found."), self.pool) time.sleep(5) # Pool data may not have migrated to the DB yet except exceptions.PoolNotFound: LOG.error(_LE("Pool ID %s not found."), pool_id) time.sleep(5) # designate-central service may not have started yet except messaging.exceptions.MessagingTimeout: time.sleep(0.2) # designate-central failed in an unknown way, don't allow another # failing / not started service to cause pool-manager to crash. except Exception: LOG.exception( _LE("An unknown exception occurred while " "fetching pool details")) time.sleep(5) # Create the necessary Backend instances for each target self._setup_target_backends() for target in self.pool.targets: self.target_backends[target.id].start() super(Service, self).start() # Setup a Leader Election, use for ensuring certain tasks are executed # on exactly one pool-manager instance at a time] self._pool_election = coordination.LeaderElection( self._coordinator, '%s:%s' % (self.service_name, self.pool.id)) self._pool_election.start() if CONF['service:pool_manager'].enable_recovery_timer: interval = CONF['service:pool_manager'].periodic_recovery_interval LOG.info( _LI('Starting periodic recovery timer every' ' %(interval)s s') % {'interval': interval}) self.tg.add_timer(interval, self.periodic_recovery, interval) if CONF['service:pool_manager'].enable_sync_timer: interval = CONF['service:pool_manager'].periodic_sync_interval LOG.info( _LI('Starting periodic synchronization timer every' ' %(interval)s s') % {'interval': interval}) self.tg.add_timer(interval, self.periodic_sync, interval)
def _create(self, addresses, extra, zone_id, managed=True, resource_type=None, resource_id=None): """ Create a a record from addresses :param addresses: Address objects like {'version': 4, 'ip': '10.0.0.1'} :param extra: Extra data to use when formatting the record :param zone_id: The ID of the designate zone. :param managed: Is it a managed resource :param resource_type: The managed resource type :param resource_id: The managed resource ID """ if not managed: LOG.warning( _LW('Deprecation notice: Unmanaged designate-sink records are ' 'being deprecated please update the call ' 'to remove managed=False')) LOG.debug('Using Zone ID: %s', zone_id) zone = self.get_zone(zone_id) LOG.debug('Domain: %r', zone) data = extra.copy() LOG.debug('Event data: %s', data) data['zone'] = zone['name'] context = DesignateContext().elevated() context.all_tenants = True context.edit_managed_records = True for addr in addresses: event_data = data.copy() event_data.update(self._get_ip_data(addr)) if addr['version'] == 4: format = self._get_formatv4() else: format = self._get_formatv6() for fmt in format: recordset_values = { 'zone_id': zone['id'], 'name': fmt % event_data, 'type': 'A' if addr['version'] == 4 else 'AAAA' } recordset = self._find_or_create_recordset( context, **recordset_values) record_values = {'data': addr['address']} if managed: record_values.update({ 'managed': managed, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': resource_type, 'managed_resource_id': resource_id }) LOG.debug('Creating record in %s / %s with values %r', zone['id'], recordset['id'], record_values) self.central_api.create_record(context, zone['id'], recordset['id'], Record(**record_values))
def get_zone(self, zone_id): """ Return the zone for this context """ context = DesignateContext.get_admin_context(all_tenants=True) return self.central_api.get_zone(context, zone_id)
def get_domain(self, domain_id): """ Return the domain for this context """ context = DesignateContext.get_admin_context(all_tenants=True) return self.central_api.get_domain(context, domain_id)
def __init__(self): self.context = DesignateContext.get_admin_context( request_id='designate-manage') policy.init()
def get_admin_context(self): return DesignateContext.get_admin_context( project_id=utils.generate_uuid(), user_id=utils.generate_uuid())
def process_notification(self, context, event_type, payload): LOG.debug('NovaFixedV6Handler: Event type received %s', event_type) LOG.debug('NovaFixedV6Handler: Event body received %s', payload) zone = self.get_zone(cfg.CONF[self.name].zone_id) reverse_zone = self.get_zone(cfg.CONF[self.name].reverse_zone_id) domain_id = zone['id'] reverse_domain_id = reverse_zone['id'] if event_type == 'compute.instance.create.end': # Need admin context to get the ec2id. Otherwise using the normal context would have worked. username = cfg.CONF[self.name].admin_user password = cfg.CONF[self.name].admin_password tenant_name = cfg.CONF[self.name].admin_tenant_name auth_url = cfg.CONF[self.name].auth_url auth = v3.Password(username=username, password=password, project_name=tenant_name, project_domain_name='default', user_domain_name='default', auth_url=auth_url) sess = session.Session(auth=auth) nvc = nova_c.Client(2.1, session=sess) instance = nvc.servers.get(payload['instance_id']) # Determine the hostname ec2id = getattr(instance, 'OS-EXT-SRV-ATTR:instance_name') ec2id = ec2id.split('-', 1)[1].lstrip('0') hostname = '%s.%s' % (ec2id, zone['name']) LOG.debug('NovaFixedV6Handler creating AAAA record (%s) for - %s', hostname, payload['instance_id']) # Become Designate Admin to manage records context = DesignateContext.get_admin_context(all_tenants=True) # 1 recordset of an A and AAAA record for fixed_ip in payload['fixed_ips']: # Don't create an A record for the private address. if fixed_ip['version'] == 4: continue record_type = 'AAAA' recordset_values = { 'zone_id': domain_id, 'name': hostname, 'type': record_type } recordset = self._find_or_create_recordset( context, **recordset_values) record_values = { 'data': fixed_ip['address'], 'managed': True, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': 'instance', 'managed_resource_id': payload['instance_id'] } LOG.debug('Creating record in %s / %s with values %r' % (domain_id, recordset['id'], record_values)) self.central_api.create_record(context, domain_id, recordset['id'], Record(**record_values)) # Create PTR record_type = 'PTR' # Calculate reverse address v6address = ipaddress.ip_address(fixed_ip['address']) reverse_address = v6address.reverse_pointer + '.' recordset_values = { 'zone_id': reverse_domain_id, 'name': reverse_address, 'type': record_type } reverse_recordset = self._find_or_create_recordset( context, **recordset_values) record_values = { 'data': hostname, 'managed': True, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': 'instance', 'managed_resource_id': payload['instance_id'] } LOG.debug( 'NovaFixedV6Handler Creating record in %s / %s with values %r' % (reverse_domain_id, reverse_recordset['id'], record_values)) self.central_api.create_record(context, reverse_domain_id, reverse_recordset['id'], Record(**record_values)) nvc.servers.set_meta_item(instance, 'dns', hostname[:-1]) elif event_type == 'compute.instance.delete.start': # Nova Delete Event does not include fixed_ips. Hence why we had the instance ID in the records. LOG.debug('NovaFixedV6Handler delete A and AAAA record for - %s', payload['instance_id']) self._delete(zone_id=domain_id, resource_id=payload['instance_id'], resource_type='instance') self._delete(zone_id=reverse_domain_id, resource_id=payload['instance_id'], resource_type='instance') # search for and delete floating IPs elevated_context = DesignateContext.get_admin_context( all_tenants=True, edit_managed_records=True) criterion = { 'managed': True, 'managed_plugin_name': 'neutron_floating', 'managed_resource_type': 'instance', 'managed_extra': 'instance:%s' % (payload['instance_id']), } records = self.central_api.find_records(elevated_context, criterion) LOG.debug('Found %d floating ip records to delete for %s' % (len(records), payload['instance_id'])) for record in records: zones = self.central_api.find_zones(elevated_context) for zone in zones: try: recordset = self.central_api.get_recordset( elevated_context, zone['id'], record['recordset_id']) LOG.debug( 'Deleting record %s from %s / %s' % (record['id'], zone['id'], record['recordset_id'])) #self.central_api.delete_record(elevated_context, zone['id'], record['recordset_id'], record['id']) self.central_api.delete_recordset( elevated_context, zone['id'], record['recordset_id']) except: pass
def _create(self, addresses, extra, managed=True, resource_type=None, resource_id=None): """ Create a record from addresses :param addresses: Address objects like {'version': 4, 'ip': '10.0.0.1'} :param extra: Extra data to use when formatting the record :param managed: Is it a managed resource :param resource_type: The managed resource type :param resource_id: The managed resource ID """ LOG.debug('Using DomainID: %s' % cfg.CONF[self.name].domain_id) zone = self.get_zone(cfg.CONF[self.name].domain_id) legacy_zone = self.get_zone(cfg.CONF[self.name].legacy_domain_id) LOG.debug('Domain: %r' % zone) data = extra.copy() LOG.debug('Event data: %s' % data) data['zone'] = zone['name'] context = DesignateContext.get_admin_context(all_tenants=True) # We have a hack elsewhere in keystone to ensure that tenant id == tenant name. # So... we can safely use the id in the fqdn. data['project_name'] = data['tenant_id'] for addr in addresses: event_data = data.copy() event_data.update(self._get_ip_data(addr)) if addr['version'] == 4: reverse_format = cfg.CONF[self.name].get('reverse_format') reverse_zone_id = cfg.CONF[self.name].get('reverse_domain_id') if reverse_format and reverse_zone_id: reverse_zone = self.get_zone(reverse_zone_id) LOG.debug('Reverse zone: %r' % reverse_zone) ip_digits = addr['address'].split('.') ip_digits.reverse() name = "%s.in-addr.arpa." % '.'.join(ip_digits) recordset_values = { 'zone_id': reverse_zone['id'], 'name': name, 'type': 'PTR', } recordset = self._find_or_create_recordset( context, **recordset_values) record_values = {'data': reverse_format % event_data} if managed: record_values.update({ 'managed': managed, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': resource_type, 'managed_resource_id': resource_id }) LOG.warn( 'Creating reverse record in %s / %s with values %r', reverse_zone['id'], recordset['id'], record_values) central_api.create_record(context, reverse_zone['id'], recordset['id'], Record(**record_values)) for fmt in cfg.CONF[self.name].get('format'): self._create_record(context, fmt, zone, event_data, addr, managed, resource_type, resource_id) event_data['zone'] = legacy_zone['name'] for fmt in cfg.CONF[self.name].get('legacy_format'): self._create_record(context, fmt, legacy_zone, event_data, addr, managed, resource_type, resource_id)
def process_notification(self, context, event_type, payload): """Process floating IP notifications from Neutron""" LOG.info('%s received notification - %s' % (self.get_canonical_name(), event_type)) # We need a context that will allow us to manipulate records that are # flagged as managed, so we can't use the context that was provided # with the notification. elevated_context = DesignateContext(tenant=context['tenant']).elevated() elevated_context.all_tenants = True elevated_context.edit_managed_records = True # Create an object from the original context so we can use it with the # RPC API calls. We want this limited to the single tenant so we can # use it to find their domains. orig_context = DesignateContext(tenant=context['tenant']).elevated() # When an instance is deleted, we never get a floating IP update event, # we just get notified that the underlying port was deleted. In that # case look for it under the other key. if event_type.startswith('port.delete'): self._disassociate_port_id(context=elevated_context, port_id=payload['port_id']) if event_type.startswith('floatingip.'): # A floating IP can only be associated with a single instance at a # time, so the first thing we always do is remove any existing # association when we get an update. This is always safe whether # or not we're deleting it or reassigning it. if 'floatingip' in payload: # floatingip.update.end floating_ip = payload['floatingip']['floating_ip_address'] floating_ip_id = payload['floatingip']['id'] elif 'floatingip_id' in payload: # floatingip.delete.end floating_ip = None floating_ip_id = payload['floatingip_id'] self._disassociate_floating_ip(context=elevated_context, floating_ip_id=floating_ip_id, ) # If it turns out that the event is an update and it has a fixed ip in # the update, then we create the new record. if event_type.startswith('floatingip.update'): if payload['floatingip']['fixed_ip_address']: domain = self._pick_tenant_domain(orig_context, default_regex=cfg.CONF[self.name].default_regex, require_default_regex=cfg.CONF[self.name].require_default_regex, ) if domain is None: LOG.info('No domains found for tenant %s(%s), ignoring Floating IP update for %s' % (context['tenant_name'], context['tenant_id'], floating_ip)) else: LOG.debug('Using domain %s(%s) for tenant %s(%s)' % (domain.name, domain.id, context['tenant_name'], context['tenant_id'])) kc = keystone_c.Client(token=context['auth_token'], tenant_id=context['tenant_id'], region_name=cfg.CONF[self.name].region_name, auth_url=cfg.CONF[self.name].keystone_auth_uri) port_id = payload['floatingip']['port_id'] instance_info = self._get_instance_info(kc, port_id) extra = payload.copy() extra.update({'instance_name': instance_info['name'], 'instance_short_name': instance_info['name'].partition('.')[0], 'domain': domain.name}) self._associate_floating_ip(context=elevated_context, domain_id=domain.id, extra=extra, floating_ip_id=floating_ip_id, floating_ip=floating_ip, port_id=port_id)
def get_admin_context(self): return DesignateContext.get_admin_context(tenant=utils.generate_uuid(), user=utils.generate_uuid())
def __init__(self, central_service): super(Backend, self).__init__() self.central_service = central_service self.admin_context = DesignateContext.get_admin_context() self.admin_context.all_tenants = True
def get_context(self, **kwargs): return DesignateContext(**kwargs)
def get_domain(self, domain_id): """ Return the domain for this context """ context = DesignateContext.get_admin_context() return central_api.get_domain(context, domain_id)
def __init__(self, central_service): super(Backend, self).__init__() self.central_service = central_service self.admin_context = DesignateContext.get_admin_context()
def _delete(self, extra, managed=True, resource_id=None, resource_type='instance', criterion={}): """ Handle a generic delete of a fixed ip within a domain :param criterion: Criterion to search and destroy records """ domain = self.get_domain(cfg.CONF[self.name].domain_id) data = extra.copy() LOG.debug('Event data: %s' % data) data['domain'] = domain['name'] data['project_name'] = data['tenant_id'] event_data = data.copy() fqdn = cfg.CONF[self.name].fqdn_format % event_data fqdn = fqdn.rstrip('.').encode('utf8') # Clean salt and puppet keys for deleted instance if cfg.CONF[self.name].puppet_master_host: LOG.debug('Cleaning puppet key %s' % fqdn) self._run_remote_command( cfg.CONF[self.name].puppet_master_host, cfg.CONF[self.name].certmanager_user, 'sudo puppet cert clean %s' % pipes.quote(fqdn)) if cfg.CONF[self.name].salt_master_host: LOG.debug('Cleaning salt key %s' % fqdn) self._run_remote_command( cfg.CONF[self.name].salt_master_host, cfg.CONF[self.name].certmanager_user, 'sudo salt-key -y -d %s' % pipes.quote(fqdn)) # Clean up the puppet config for this instance, if there is one self._delete_puppet_config(data['tenant_id'], fqdn) # Finally, delete any proxy records pointing to this instance. # # For that, we need the IP which we can dig out of the old DNS record. crit = criterion.copy() # Make sure we only look at forward records crit['domain_id'] = cfg.CONF[self.name].domain_id if managed: crit.update({ 'managed': managed, 'managed_resource_id': resource_id, 'managed_resource_type': resource_type }) context = DesignateContext().elevated() context.all_tenants = True context.edit_managed_records = True records = central_api.find_records(context, crit) # We only care about the IP, and that's the same in both records. ip = records[0].data LOG.debug("Cleaning up proxy records for IP %s" % ip) self._delete_proxies_for_ip(data['project_name'], ip)
def _delete(self, managed=True, resource_id=None, resource_type='instance', criterion={}): """ Handle a generic delete of a fixed ip within a zone :param criterion: Criterion to search and destroy records """ context = DesignateContext().elevated() context.all_tenants = True context.edit_managed_records = True forward_crit = criterion.copy() forward_crit['zone_id'] = cfg.CONF[self.name].domain_id if managed: forward_crit.update({ 'managed': managed, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_id': resource_id, 'managed_resource_type': resource_type }) records = central_api.find_records(context, forward_crit) for record in records: LOG.warn('Deleting forward record %s in recordset %s' % (record['id'], record['recordset_id'])) central_api.delete_record(context, cfg.CONF[self.name].domain_id, record['recordset_id'], record['id']) legacy_zone_id = cfg.CONF[self.name].get('legacy_domain_id') if legacy_zone_id: legacy_crit = criterion.copy() legacy_crit['zone_id'] = legacy_zone_id if managed: legacy_crit.update({ 'managed': managed, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_id': resource_id, 'managed_resource_type': resource_type }) records = central_api.find_records(context, legacy_crit) for record in records: LOG.warn('Deleting legacy record %s in recordset %s' % (record['id'], record['recordset_id'])) central_api.delete_record(context, legacy_zone_id, record['recordset_id'], record['id']) reverse_zone_id = cfg.CONF[self.name].get('reverse_domain_id') if reverse_zone_id: reverse_crit = criterion.copy() reverse_crit.update({'zone_id': reverse_zone_id}) if managed: reverse_crit.update({ 'managed': managed, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_id': resource_id, 'managed_resource_type': resource_type }) records = central_api.find_records(context, reverse_crit) for record in records: LOG.warn('Deleting reverse record %s in recordset %s' % (record['id'], record['recordset_id'])) central_api.delete_record(context, reverse_zone_id, record['recordset_id'], record['id'])
def _create(self, addresses, extra, managed=True, resource_type=None, resource_id=None): """ Create a a record from addresses :param addresses: Address objects like {'version': 4, 'ip': '10.0.0.1'} :param extra: Extra data to use when formatting the record :param managed: Is it a managed resource :param resource_type: The managed resource type :param resource_id: The managed resource ID """ LOG.debug('Using DomainID: %s' % cfg.CONF[self.name].domain_id) domain = self.get_domain(cfg.CONF[self.name].domain_id) LOG.debug('Domain: %r' % domain) data = extra.copy() LOG.debug('Event data: %s' % data) data['domain'] = domain['name'] context = DesignateContext.get_admin_context(all_tenants=True) # Extra magic! The event record contains a tenant id but not a tenant name. So # if our formats include project_name then we need to ask keystone for the name. need_project_name = False for fmt in cfg.CONF[self.name].get('format'): if 'project_name' in fmt: need_project_name = True break if 'project_name' in cfg.CONF[self.name].get('reverse_format'): need_project_name = True if need_project_name: project_name = self._resolve_project_name(data['tenant_id']) data['project_name'] = project_name for addr in addresses: event_data = data.copy() event_data.update(self._get_ip_data(addr)) if addr['version'] == 4: reverse_format = cfg.CONF[self.name].get('reverse_format') reverse_domain_id = cfg.CONF[self.name].get( 'reverse_domain_id') if reverse_format and reverse_domain_id: reverse_domain = self.get_domain(reverse_domain_id) LOG.debug('Reverse domain: %r' % reverse_domain) ip_digits = addr['address'].split('.') ip_digits.reverse() name = "%s.in-addr.arpa." % '.'.join(ip_digits) recordset_values = { 'domain_id': reverse_domain['id'], 'name': name, 'type': 'PTR', } recordset = self._find_or_create_recordset( context, **recordset_values) record_values = {'data': reverse_format % event_data} if managed: record_values.update({ 'managed': managed, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': resource_type, 'managed_resource_id': resource_id }) LOG.debug('Creating record in %s / %s with values %r', reverse_domain['id'], recordset['id'], record_values) central_api.create_record(context, reverse_domain['id'], recordset['id'], Record(**record_values)) for fmt in cfg.CONF[self.name].get('format'): recordset_values = { 'domain_id': domain['id'], 'name': fmt % event_data, 'type': 'A' if addr['version'] == 4 else 'AAAA' } recordset = self._find_or_create_recordset( context, **recordset_values) record_values = {'data': addr['address']} if managed: record_values.update({ 'managed': managed, 'managed_plugin_name': self.get_plugin_name(), 'managed_plugin_type': self.get_plugin_type(), 'managed_resource_type': resource_type, 'managed_resource_id': resource_id }) LOG.debug('Creating record in %s / %s with values %r', domain['id'], recordset['id'], record_values) central_api.create_record(context, domain['id'], recordset['id'], Record(**record_values))
def get_admin_context(self): return DesignateContext.get_admin_context( tenant=utils.generate_uuid(), user=utils.generate_uuid())
def run(self, parsed_args): self.context = DesignateContext.get_admin_context( request_id="designate-manage") return super(Command, self).run(parsed_args)