예제 #1
0
파일: zone.py 프로젝트: openstack/designate
    def _get_zones(self):
        criterion = {
            'shard': "BETWEEN %s,%s" % (self.begin_shard, self.end_shard),
            'status': 'ERROR'
        }
        error_zones = self.storage.find_zones(self.context, criterion)

        # Include things that have been hanging out in PENDING
        # status for longer than they should
        # Generate the current serial, will provide a UTC Unix TS.
        current = utils.increment_serial()
        stale_criterion = {
            'shard': "BETWEEN %s,%s" % (self.begin_shard, self.end_shard),
            'status': 'PENDING',
            'serial': "<%s" % (current - self.max_prop_time)
        }

        stale_zones = self.storage.find_zones(self.context, stale_criterion)
        if stale_zones:
            LOG.warning('Found %(len)d zones PENDING for more than %(sec)d '
                        'seconds', {
                            'len': len(stale_zones),
                            'sec': self.max_prop_time
                        })
            error_zones.extend(stale_zones)

        return error_zones
예제 #2
0
    def periodic_sync(self):
        """
        :return: None
        """
        LOG.debug("Calling periodic_sync.")

        context = DesignateContext.get_admin_context(all_tenants=True)

        criterion = {
            'pool_id': cfg.CONF['service:pool_manager'].pool_id,
            'status': '%s%s' % ('!', ERROR_STATUS)
        }

        periodic_sync_seconds = \
            cfg.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:
                self.update_domain(context, domain)
        except Exception:
            LOG.exception(
                _LE('An unhandled exception in periodic sync '
                    'occurred.  This should never happen!'))
예제 #3
0
    def _get_zones(self):
        criterion = {
            'shard': "BETWEEN %s,%s" % (self.begin_shard, self.end_shard),
            'status': 'ERROR'
        }
        error_zones = self.storage.find_zones(self.context, criterion)

        # Include things that have been hanging out in PENDING
        # status for longer than they should
        # Generate the current serial, will provide a UTC Unix TS.
        current = utils.increment_serial()
        stale_criterion = {
            'shard': "BETWEEN %s,%s" % (self.begin_shard, self.end_shard),
            'status': 'PENDING',
            'serial': "<%s" % (current - self.max_prop_time)
        }

        stale_zones = self.storage.find_zones(self.context, stale_criterion)
        if stale_zones:
            LOG.warning(
                'Found %(len)d zones PENDING for more than %(sec)d '
                'seconds', {
                    'len': len(stale_zones),
                    'sec': self.max_prop_time
                })
            error_zones.extend(stale_zones)

        return error_zones
예제 #4
0
    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.'))
예제 #5
0
    def create_domain(self, context, values):
        # TODO(kiall): Refactor this method into *MUCH* smaller chunks.

        # Default to creating in the current users tenant
        if 'tenant_id' not in values:
            values['tenant_id'] = context.tenant

        target = {
            'tenant_id': values['tenant_id'],
            'domain_name': values['name']
        }

        policy.check('create_domain', context, target)

        # Ensure the tenant has enough quota to continue
        self._enforce_domain_quota(context, values['tenant_id'])

        # Ensure the domain name is valid
        self._is_valid_domain_name(context, values['name'])

        # Ensure TTL is above the minimum
        ttl = values.get('ttl', None)
        if ttl is not None:
            self._is_valid_ttl(context, ttl)

        # Handle sub-domains appropriately
        parent_domain = self._is_subdomain(context, values['name'])

        if parent_domain:
            if parent_domain['tenant_id'] == values['tenant_id']:
                # Record the Parent Domain ID
                values['parent_domain_id'] = parent_domain['id']
            else:
                raise exceptions.Forbidden('Unable to create subdomain in '
                                           'another tenants domain')

        # TODO(kiall): Handle super-domains properly

        # NOTE(kiall): Fetch the servers before creating the domain, this way
        #              we can prevent domain creation if no servers are
        #              configured.
        servers = self.storage_api.find_servers(context)

        if len(servers) == 0:
            LOG.critical('No servers configured. Please create at least one '
                         'server')
            raise exceptions.NoServersConfigured()

        # Set the serial number
        values['serial'] = utils.increment_serial()

        with self.storage_api.create_domain(context, values) as domain:
            with wrap_backend_call():
                self.backend.create_domain(context, domain)

        self.notifier.info(context, 'dns.domain.create', domain)

        return domain
예제 #6
0
    def create_domain(self, context, values):
        # TODO(kiall): Refactor this method into *MUCH* smaller chunks.
        values['tenant_id'] = context.tenant_id

        target = {
            'tenant_id': values['tenant_id'],
            'domain_name': values['name']
        }

        policy.check('create_domain', context, target)

        # Ensure the tenant has enough quota to continue
        quota_criterion = {'tenant_id': values['tenant_id']}
        domain_count = self.storage_api.count_domains(
            context, criterion=quota_criterion)
        self.quota.limit_check(context, values['tenant_id'],
                               domains=domain_count)

        # Ensure the domain name is valid
        self._is_valid_domain_name(context, values['name'])

        # Handle sub-domains appropriately
        parent_domain = self._is_subdomain(context, values['name'])

        if parent_domain:
            if parent_domain['tenant_id'] == values['tenant_id']:
                # Record the Parent Domain ID
                values['parent_domain_id'] = parent_domain['id']
            else:
                raise exceptions.Forbidden('Unable to create subdomain in '
                                           'another tenants domain')

        # TODO(kiall): Handle super-domains properly

        # NOTE(kiall): Fetch the servers before creating the domain, this way
        #              we can prevent domain creation if no servers are
        #              configured.
        servers = self.storage_api.find_servers(context)

        if len(servers) == 0:
            LOG.critical('No servers configured. Please create at least one '
                         'server')
            raise exceptions.NoServersConfigured()

        # Set the serial number
        values['serial'] = utils.increment_serial()

        with self.storage_api.create_domain(context, values) as domain:
            with wrap_backend_call():
                self.backend.create_domain(context, domain)

        utils.notify(context, 'central', 'domain.create', domain)

        return domain
예제 #7
0
    def _increment_domain_serial(self, context, domain_id):
        domain = self.storage_api.get_domain(context, domain_id)

        # Increment the serial number
        values = {'serial': utils.increment_serial(domain['serial'])}

        with self.storage_api.update_domain(context, domain_id,
                                            values) as domain:
            with wrap_backend_call():
                self.backend.update_domain(context, domain)

        return domain
예제 #8
0
    def _increment_domain_serial(self, context, domain_id):
        domain = self.storage_api.get_domain(context, domain_id)

        # Increment the serial number
        values = {'serial': utils.increment_serial(domain['serial'])}

        with self.storage_api.update_domain(
                context, domain_id, values) as domain:
            with wrap_backend_call():
                self.backend.update_domain(context, domain)

        return domain
예제 #9
0
    def create_domain(self, context, values):
        # TODO(kiall): Refactor this method into *MUCH* smaller chunks.
        values['tenant_id'] = context.tenant_id

        target = {
            'tenant_id': values['tenant_id'],
            'domain_name': values['name']
        }

        policy.check('create_domain', context, target)

        # Ensure the tenant has enough quota to continue
        self._enforce_domain_quota(context, values['tenant_id'])

        # Ensure the domain name is valid
        self._is_valid_domain_name(context, values['name'])

        # Handle sub-domains appropriately
        parent_domain = self._is_subdomain(context, values['name'])

        if parent_domain:
            if parent_domain['tenant_id'] == values['tenant_id']:
                # Record the Parent Domain ID
                values['parent_domain_id'] = parent_domain['id']
            else:
                raise exceptions.Forbidden('Unable to create subdomain in '
                                           'another tenants domain')

        # TODO(kiall): Handle super-domains properly

        # NOTE(kiall): Fetch the servers before creating the domain, this way
        #              we can prevent domain creation if no servers are
        #              configured.
        servers = self.storage_api.find_servers(context)

        if len(servers) == 0:
            LOG.critical('No servers configured. Please create at least one '
                         'server')
            raise exceptions.NoServersConfigured()

        # Set the serial number
        values['serial'] = utils.increment_serial()

        with self.storage_api.create_domain(context, values) as domain:
            with wrap_backend_call():
                self.backend.create_domain(context, domain)

        utils.notify(context, 'central', 'domain.create', domain)

        return domain
예제 #10
0
    def _fetch_healthy_zones(self, context):
        """Fetch all zones not in error
        :return: :class:`ZoneList` zones
        """
        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)
        return zones
예제 #11
0
    def _get_failed_zones(self, context, action):
        """
        Fetch zones that are in ERROR status or have been PENDING for a long
        time. Used by periodic recovery.
        After a certain time changes either should have successfully
        propagated or gone to an ERROR state.
        However, random failures and undiscovered bugs leave zones hanging out
        in PENDING state forever. By treating those "stale" zones as failed,
        periodic recovery will attempt to restore them.
        :return: :class:`ZoneList` zones
        """
        criterion = {
            'pool_id': CONF['service:pool_manager'].pool_id,
            'action': action,
            'status': ERROR_STATUS
        }
        error_zones = self.central_api.find_zones(context, criterion)

        # Include things that have been hanging out in PENDING
        # status for longer than they should
        # Generate the current serial, will provide a UTC Unix TS.
        current = utils.increment_serial()
        stale_criterion = {
            'pool_id': CONF['service:pool_manager'].pool_id,
            'action': action,
            'status': PENDING_STATUS,
            'serial': "<%s" % (current - self.max_prop_time)
        }
        LOG.debug(
            'Including zones with action %(action)s and %(status)s '
            'older than %(seconds)ds' % {
                'action': action,
                'status': PENDING_STATUS,
                'seconds': self.max_prop_time
            })

        stale_zones = self.central_api.find_zones(context, stale_criterion)
        if stale_zones:
            LOG.warning(
                _LW('Found %(len)d zones PENDING for more than %(sec)d '
                    'seconds'), {
                        'len': len(stale_zones),
                        'sec': self.max_prop_time
                    })
            error_zones.extend(stale_zones)

        return error_zones
예제 #12
0
    def _get_failed_zones(self, context, action):
        """
        Fetch zones that are in ERROR status or have been PENDING for a long
        time. Used by periodic recovery.
        After a certain time changes either should have successfully
        propagated or gone to an ERROR state.
        However, random failures and undiscovered bugs leave zones hanging out
        in PENDING state forever. By treating those "stale" zones as failed,
        periodic recovery will attempt to restore them.
        :return: :class:`ZoneList` zones
        """
        criterion = {
            'pool_id': CONF['service:pool_manager'].pool_id,
            'action': action,
            'status': ERROR_STATUS
        }
        error_zones = self.central_api.find_zones(context, criterion)

        # Include things that have been hanging out in PENDING
        # status for longer than they should
        # Generate the current serial, will provide a UTC Unix TS.
        current = utils.increment_serial()
        stale_criterion = {
            'pool_id': CONF['service:pool_manager'].pool_id,
            'action': action,
            'status': PENDING_STATUS,
            'serial': "<%s" % (current - self.max_prop_time)
        }
        LOG.debug('Including zones with action %(action)s and %(status)s '
                  'older than %(seconds)ds' % {'action': action,
                                               'status': PENDING_STATUS,
                                               'seconds': self.max_prop_time})

        stale_zones = self.central_api.find_zones(context, stale_criterion)
        if stale_zones:
            LOG.warning(
                'Found %(len)d zones PENDING for more than %(sec)d seconds',
                {
                    'len': len(stale_zones),
                    'sec': self.max_prop_time
                })
            error_zones.extend(stale_zones)

        return error_zones
예제 #13
0
    def _fetch_healthy_zones(self, context):
        """Fetch all zones not in error
        :return: :class:`ZoneList` zones
        """
        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)
        return zones
예제 #14
0
    def update_domain(self, context, domain_id, values, increment_serial=True):
        # TODO(kiall): Refactor this method into *MUCH* smaller chunks.
        domain = self.storage_api.get_domain(context, domain_id)

        target = {
            'domain_id': domain_id,
            'domain_name': domain['name'],
            'tenant_id': domain['tenant_id']
        }

        policy.check('update_domain', context, target)

        if 'tenant_id' in values:
            # NOTE(kiall): Ensure the user is allowed to delete a domain from
            #              the original tenant.
            policy.check('delete_domain', context, target)

            # NOTE(kiall): Ensure the user is allowed to create a domain in
            #              the new tenant.
            target = {'domain_id': domain_id, 'tenant_id': values['tenant_id']}
            policy.check('create_domain', context, target)

        if 'name' in values and values['name'] != domain['name']:
            raise exceptions.BadRequest('Renaming a domain is not allowed')

        # Ensure TTL is above the minimum
        ttl = values.get('ttl', None)
        if ttl is not None:
            self._is_valid_ttl(context, ttl)

        if increment_serial:
            # Increment the serial number
            values['serial'] = utils.increment_serial(domain['serial'])

        with self.storage_api.update_domain(
                context, domain_id, values) as domain:
            with wrap_backend_call():
                self.backend.update_domain(context, domain)

        self.notifier.info(context, 'dns.domain.update', domain)

        return domain
예제 #15
0
    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.'))
예제 #16
0
    def update_domain(self, context, domain_id, values, increment_serial=True):
        # TODO(kiall): Refactor this method into *MUCH* smaller chunks.
        domain = self.storage_api.get_domain(context, domain_id)

        target = {
            'domain_id': domain_id,
            'domain_name': domain['name'],
            'tenant_id': domain['tenant_id']
        }

        policy.check('update_domain', context, target)

        if 'tenant_id' in values:
            # NOTE(kiall): Ensure the user is allowed to delete a domain from
            #              the original tenant.
            policy.check('delete_domain', context, target)

            # NOTE(kiall): Ensure the user is allowed to create a domain in
            #              the new tenant.
            target = {'domain_id': domain_id, 'tenant_id': values['tenant_id']}
            policy.check('create_domain', context, target)

        if 'name' in values and values['name'] != domain['name']:
            raise exceptions.BadRequest('Renaming a domain is not allowed')

        if increment_serial:
            # Increment the serial number
            values['serial'] = utils.increment_serial(domain['serial'])

        with self.storage_api.update_domain(context, domain_id,
                                            values) as domain:
            with wrap_backend_call():
                self.backend.update_domain(context, domain)

        utils.notify(context, 'central', 'domain.update', domain)

        return domain
예제 #17
0
    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.'))
예제 #18
0
    def test_increment_serial_higher_than_ts(self, mock_utcnow_ts):
        mock_utcnow_ts.return_value = 1561698354

        ret_serial = utils.increment_serial(serial=1561698354 * 2)

        self.assertEqual(1561698354 * 2 + 1, ret_serial)
예제 #19
0
 def test_increment_serial(self):
     ret_serial = utils.increment_serial(serial=20)
     self.assertTrue(ret_serial > 20)
예제 #20
0
 def test_increment_serial(self):
     ret_serial = utils.increment_serial(serial=20)
     self.assertGreater(ret_serial, 20)
예제 #21
0
 def test_increment_serial(self):
     ret_serial = utils.increment_serial(serial=20)
     self.assertGreater(ret_serial, 20)
예제 #22
0
 def test_increment_serial(self):
     ret_serial = utils.increment_serial(serial=20)
     self.assertTrue(ret_serial > 20)