Пример #1
0
def is_in_dnsaas(ip):
    if not settings.ENABLE_DNSAAS_INTEGRATION:
        return False
    dnsaas_client = DNSaaS()
    url = dnsaas_client.build_url('records',
                                  get_params=[('ip', ip), ('type', 'A')])
    return len(dnsaas_client.get_api_result(url)) > 0
Пример #2
0
def clean_dns(cls, instances, **kwargs):
    """
    Clean DNS entries for each instance if DNSaaS integration is enabled.
    """
    if not settings.ENABLE_DNSAAS_INTEGRATION:
        raise DNSaaSIntegrationNotEnabledError()
    dnsaas = DNSaaS()
    # TODO: transaction?
    for instance in instances:
        ips = list(
            instance.ipaddresses.exclude(is_management=True).values_list(
                'address', flat=True))
        if not ips:
            logger.info('No IPs for %s - skipping cleaning DNS entries',
                        instance)
            continue
        records = dnsaas.get_dns_records(ips)
        if len(records) > settings.DEPLOYMENT_MAX_DNS_ENTRIES_TO_CLEAN:
            raise Exception(
                'Cannot clean {} entries for {} - clean it manually'.format(
                    len(records), instance))
        for record in records:
            logger.warning('Deleting %s (%s / %s / %s) DNS record',
                           record['pk'], record['type'], record['name'],
                           record['content'])
            if dnsaas.delete_dns_record(record['pk']):
                raise Exception()  # TODO
Пример #3
0
class TestGetDnsRecords(TestCase):
    @patch.object(DNSaaS, '_get_oauth_token')
    def setUp(self, mocked):
        mocked.return_value = 'token'
        self.dnsaas = DNSaaS()

    @patch.object(DNSaaS, 'get_api_result')
    def test_return_empty_when_api_returns_empty(self, mocked):
        mocked.return_value = []
        found_dns = self.dnsaas.get_dns_records(['192.168.0.1'])
        self.assertEqual(found_dns, [])

    def test_return_empty_when_no_ipaddress(self):
        found_dns = self.dnsaas.get_dns_records([])
        self.assertEqual(found_dns, [])

    @patch.object(DNSaaS, 'get_api_result')
    def test_return_dns_records_when_api_returns_records(self, mocked):
        data = {
            'content': '127.0.0.3',
            'name': '1.test.pl',
            'type': 'A',
            'id': 1
        }
        mocked.return_value = [data]
        found_dns = self.dnsaas.get_dns_records(['192.168.0.1'])
        self.assertEqual(len(found_dns), 1)
        self.assertEqual(found_dns[0]['content'], data['content'])
        self.assertEqual(found_dns[0]['name'], data['name'])
        self.assertEqual(found_dns[0]['type'], RecordType.a)

    @override_settings(DNSAAS_URL='http://dnsaas.com/')
    def test_build_url(self):
        self.assertEqual(self.dnsaas.build_url('domains'),
                         'http://dnsaas.com/api/domains/')

    @override_settings(DNSAAS_URL='http://dnsaas.com/')
    def test_build_url_with_version(self):
        self.assertEqual(self.dnsaas.build_url('domains'),
                         'http://dnsaas.com/api/domains/')

    @override_settings(DNSAAS_URL='http://dnsaas.com/')
    def test_build_url_with_id(self):
        self.assertEqual(self.dnsaas.build_url('domains', id=1),
                         'http://dnsaas.com/api/domains/1/')

    @override_settings(DNSAAS_URL='http://dnsaas.com/')
    def test_build_url_with_get_params(self):
        self.assertEqual(
            self.dnsaas.build_url('domains', get_params=[('name', 'ralph')]),
            'http://dnsaas.com/api/domains/?name=ralph')

    @override_settings(DNSAAS_URL='http://dnsaas.com/')
    def test_build_url_with_id_and_get_params(self):
        self.assertEqual(
            self.dnsaas.build_url('domains',
                                  id=1,
                                  get_params=[('name', 'ralph')]),
            'http://dnsaas.com/api/domains/1/?name=ralph')
Пример #4
0
def create_dns_entries(cls, instances, **kwargs):
    if not settings.ENABLE_DNSAAS_INTEGRATION:
        raise DNSaaSIntegrationNotEnabledError()
    dnsaas = DNSaaS()
    # TODO: transaction?
    for instance in instances:
        # TODO: use dedicated param instead of history_kwargs
        ip = kwargs['history_kwargs'][instance.pk]['ip']
        dnsaas.create_dns_record(record={
            'name': instance.hostname,
            'type': RecordType.a.id,
            'content': ip,
            'ptr': True,
        },
                                 service=instance.service)
Пример #5
0
    def test_user_get_info_when_dnsaas_user_has_no_perm(self):
        class RequestStub():
            status_code = 202

        request = RequestStub()
        dns = DNSaaS()

        result = dns._response2result(request)

        self.assertEqual(
            result,
            {
                'non_field_errors':
                [_("Your request couldn't be handled, try later.")]
            },
        )
Пример #6
0
def delete_dns_record(instance, *args, **kwargs):
    if not _should_send_dnsaas_request(instance):
        return
    DNSaaS().send_ipaddress_data({
        'address': instance.address,
        'hostname': instance.hostname,
        'action': 'delete'
    })
Пример #7
0
def update_dns_record(instance, created, *args, **kwargs):
    if not _should_send_dnsaas_request(instance):
        return
    keys = ['address', 'hostname']
    data_to_send = {
        'old': {key: instance._previous_state[key]
                for key in keys},
        'new': {key: instance.__dict__[key]
                for key in keys},
        'service_uid': _get_connected_service_uid(instance)
    }
    data_to_send['action'] = 'add' if created else 'update'
    if data_to_send['old']['hostname'] is not None:
        DNSaaS().send_ipaddress_data(data_to_send)
Пример #8
0
def update_dns_record(instance, created, *args, **kwargs):
    if not _should_send_dnsaas_request(instance):
        return
    keys = ['address', 'hostname']
    old = {key: instance._previous_state[key] for key in keys}
    new = {key: instance.__dict__[key] for key in keys}
    if old != new and old['hostname'] is not None:
        data_to_send = {
            'old': old,
            'new': new,
            'service_uid': _get_connected_service_uid(instance),
            'action': 'add' if created else 'update'
        }
        DNSaaS().send_ipaddress_data(data_to_send)
Пример #9
0
class TestGetDnsRecords(TestCase):
    def setUp(self):
        self.dnsaas = DNSaaS()

    @patch.object(DNSaaS, 'get_api_result')
    def test_return_empty_when_api_returns_empty(self, mocked):
        mocked.return_value = []
        found_dns = self.dnsaas.get_dns_records(['192.168.0.1'])
        self.assertEqual(found_dns, [])

    @patch.object(DNSaaS, 'get_api_result')
    def test_return_dns_records_when_api_returns_records(self, mocked):
        data = {
            'content': '127.0.0.3',
            'name': '1.test.pl',
            'type': 'A',
            'id': 1
        }
        mocked.return_value = [data]
        found_dns = self.dnsaas.get_dns_records(['192.168.0.1'])
        self.assertEqual(len(found_dns), 1)
        self.assertEqual(found_dns[0]['content'], data['content'])
        self.assertEqual(found_dns[0]['name'], data['name'])
        self.assertEqual(found_dns[0]['type'], RecordType.a)
Пример #10
0
 def __init__(self, *args, **kwargs):
     if not settings.ENABLE_DNSAAS_INTEGRATION:
         raise DNSaaSIntegrationNotEnabledError()
     self.dnsaas = DNSaaS()
     return super().__init__(*args, **kwargs)
Пример #11
0
class DNSView(RalphDetailView):
    icon = 'chain-broken'
    name = 'dns_edit'
    label = 'DNS'
    url_name = 'dns_edit'
    template_name = 'dns/dns_edit.html'

    def __init__(self, *args, **kwargs):
        if not settings.ENABLE_DNSAAS_INTEGRATION:
            raise DNSaaSIntegrationNotEnabledError()
        self.dnsaas = DNSaaS()
        return super().__init__(*args, **kwargs)

    def get_forms(self):
        forms = []
        ipaddresses = self.object.ipaddress_set.all().values_list('address',
                                                                  flat=True)
        if not ipaddresses:
            # If ipaddresses is empty return empty form list because we can not
            # identify the records do not have any IP address
            return forms

        initial = self.dnsaas.get_dns_records(ipaddresses)
        for item in initial:
            forms.append(DNSRecordForm(item))

        if initial and initial[0]['type'] == RecordType.a.id:
            # from API "A" record is always first
            empty_form = DNSRecordForm(initial={'name': initial[0]['name']})
        else:
            empty_form = DNSRecordForm()

        forms.append(empty_form)
        return forms

    def get(self, request, *args, **kwargs):
        if 'forms' not in kwargs:
            kwargs['forms'] = self.get_forms()
        return super().get(request, *kwargs, **kwargs)

    def post(self, request, *args, **kwargs):
        forms = self.get_forms()
        posted_form = DNSRecordForm(request.POST)
        # Find form which request's data belongs to
        for i, form in enumerate(forms):
            if (str(form.data.get('pk',
                                  '')) == str(posted_form.data.get('pk', ''))):
                forms[i] = posted_form
                break

        if posted_form.is_valid():
            if posted_form.data.get('delete'):
                errors = self.dnsaas.delete_dns_record(form.data['pk'])
            elif posted_form.cleaned_data.get('pk'):
                errors = self.dnsaas.update_dns_record(
                    posted_form.cleaned_data)
            else:
                errors = self.dnsaas.create_dns_record(
                    posted_form.cleaned_data)

            if errors:
                for field_name, field_errors in errors.items():
                    for field_error in field_errors:
                        if field_name == 'non_field_errors':
                            field_name = None
                        posted_form.add_error(field_name, field_error)
            else:
                return HttpResponseRedirect('.')

        kwargs['forms'] = forms
        return self.get(request, *args, **kwargs)
Пример #12
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.dns = DNSaaS()
Пример #13
0
class Command(BaseCommand):
    help = 'Compare DNS records in DNSaaS with state of IP-hostname in Ralph'

    # TODO (mkurek): add possibility to exclude some domains

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.dns = DNSaaS()

    def _fetch_dns_records(self):
        """
        Fetch DNS Records (A and PTR) from DNSaaS.

        Returns two nested dicts, where key on the first level is record type,
        and key-values on the second level are name-content and content-name
        respectively.

        Example output:
        (
            # regular order: name -> [content]
            {
                'A': {
                    'myserver.mydc.net': ['1.2.3.4', '5.6.7.8'],
                    'myserver2.mydc.net': ['10.20.30.40']
                },
                'PTR': {
                    '4.3.2.1.in-addr.arpa': ['myserver.mydc.net'],
                    '8.7.5.6.in-addr.arpa': ['myserver.mydc.net'],
                    '40.30.20.10.in-addr.arpa': ['myserver2.mydc.net']
                }
            },
            # reversed order: content -> [name]
            {
                'A': {
                    '1.2.3.4': ['myserver.mydc.net'],
                    '5.6.7.8': ['myserver.mydc.net'],
                    '10.20.30.40': ['myserver2.mydc.net']
                },
                'PTR': {
                    'myserver.mydc.net': [
                        '4.3.2.1.in-addr.arpa', '8.7.5.6.in-addr.arpa'
                    ],
                    'myserver2.mydc.net': ['40.30.20.10.in-addr.arpa']
                }
            }
        )
        """
        url = self.dns.build_url('records',
                                 get_params=[
                                     ('limit', 1000),
                                     ('offset', 0),
                                 ] + [('type', t) for t in {'A', 'PTR'}])
        api_results = self.dns.get_api_result(url)
        records_by_types = defaultdict(lambda: defaultdict(list))
        records_by_types_rev = defaultdict(lambda: defaultdict(list))
        for record in api_results:
            records_by_types[record['type']][record['name']].append(
                record['content'])
            records_by_types_rev[record['type']][record['content']].append(
                record['name'])
        return records_by_types, records_by_types_rev

    def _get_ips(self):
        """
        Return dict with IP-hostname from Ralph.
        """
        ips = IPAddress.objects.filter(
            ethernet__base_object__cloudhost__isnull=True,
            hostname__isnull=False).values_list('address', 'hostname')
        return dict(ips)

    def get_missing_a_records_in_dnsaas(self, ips, dns, dns_rev):
        """
        Return pairs of (ip, hostname) which are present in Ralph, but not in
        DNSaaS
        """
        for ip, hostname in ips.items():
            if ip not in dns_rev['A']:
                yield (ip, hostname)

    def get_wrong_a_records_in_dnsaas(self, ips, dns, dns_rev):
        """
        Return triplets of (ip, ralph_hostname, dns_hostname) for Records which
        are both in Ralph and DNSaaS, but are inconsistent
        """
        for ip, hostname in ips.items():
            if ip in dns_rev['A']:
                dns_hostnames = dns_rev['A'][ip]
                if hostname not in dns_hostnames or len(dns_hostnames) != 1:
                    yield (ip, hostname, dns_hostnames)

    def get_missing_a_records_in_ralph(self, ips, dns, dns_rev):
        """
        Return pairs of (ip, list of hostnames) for Records (ips) which are
        present in DNSaaS, but they are not in Ralph.
        """
        for ip, hostnames in dns_rev['A'].items():
            if ip not in ips:
                yield (ip, hostnames)

    def check_ralph_ptrs(self, ips, dns, dns_rev):
        """
        Return pairs of (ip, hostname) which has not properly configured PTR
        records.
        """
        for ip, hostname in ips.items():
            if ip in dns_rev['A'] and hostname in dns_rev['A'][ip]:
                ptr = get_ptr(ip)
                if ptr not in dns['PTR'] or hostname not in dns['PTR'][ptr]:
                    yield (ip, hostname, dns['PTR'].get(ptr))

    def get_zombie_ptrs(self, ips, dns, dns_rev):
        """
        Return pairs of (ip, hostname) which has not properly configured PTR
        records.
        """
        for hostname, ptrs in dns_rev['PTR'].items():
            for ptr in ptrs:
                ip = '.'.join(ptr.split('.')[3::-1])
                if hostname not in dns['A'] or ip not in dns['A'][hostname]:
                    yield ptr, hostname

    def get_duplicated_ptrs(self, ips, dns, dns_rev):
        """
        Return pairs of (ip, hostname) which has not properly configured PTR
        records.
        """
        for ptr, hostnames in dns['PTR'].items():
            if len(hostnames) > 1:
                yield ptr, hostnames

    def handle(self, **options):
        dns, dns_rev = self._fetch_dns_records()
        ips = self._get_ips()
        for func, headers, description in [
            (self.get_missing_a_records_in_dnsaas, ['IP', 'hostname'],
             'A records missing in DNSaaS'),
            (self.get_missing_a_records_in_ralph, ['IP', 'hostname'],
             'A records missing in Ralph'),
            (self.get_wrong_a_records_in_dnsaas,
             ['IP', 'ralph hostname',
              'dnsaas hostnames'], 'Inconsistent A records'),
            (self.check_ralph_ptrs, ['IP', 'ralph hostname', 'PTR content'],
             'Missing or wrong PTR records'),
            (self.get_zombie_ptrs, ['PTR', 'hostname (content)'],
             'Zombie PTR records'),
            (self.get_duplicated_ptrs, ['PTR', 'hostnames'],
             'Duplicated PTR records'),
        ]:
            result = func(ips, dns, dns_rev)
            self.stdout.write(
                TEMPLATE.format(
                    description=description,
                    headers='\t'.join(headers),
                    content='\n'.join(
                        ['\t'.join(map(str, line)) for line in result]),
                ))
Пример #14
0
 def setUp(self):
     self.dnsaas = DNSaaS()
Пример #15
0
 def setUp(self, mocked):
     mocked.return_value = 'token'
     self.dnsaas = DNSaaS()
Пример #16
0
 def handle(self, *args, **options):
     dnsaas_client = DNSaaS()
     domains = self.get_domains(dnsaas_client)
     self.update_from_domains(domains)
     self.get_records(dnsaas_client)