Esempio n. 1
0
    def find_dns_owners(self, dns_owner_id, only_type=True):
        """Return information about entries using this dns_owner.  If
        only_type=True, returns a list of owner_type.  Otherwise
        returns a list of (owner_type, owner_id) tuples"""

        ret = []
        arecord = ARecord.ARecord(self._db)
        for row in arecord.list_ext(dns_owner_id=dns_owner_id):
            ret.append((dns.A_RECORD, row['a_record_id']))
        aaaarecord = AAAARecord.AAAARecord(self._db)
        for row in aaaarecord.list_ext(dns_owner_id=dns_owner_id):
            ret.append((dns.AAAA_RECORD, row['aaaa_record_id']))
        hi = HostInfo.HostInfo(self._db)
        try:
            hi.find_by_dns_owner_id(dns_owner_id)
            ret.append((dns.HOST_INFO, hi.entity_id))
        except Errors.NotFoundError:
            pass
        dns_owner = DnsOwner.DnsOwner(self._db)
        for row in dns_owner.list_srv_records(owner_id=dns_owner_id):
            ret.append((dns.SRV_OWNER, row['service_owner_id']))
        for row in dns_owner.list_general_dns_records(
                dns_owner_id=dns_owner_id):
            ret.append((dns.GENERAL_DNS_RECORD, row['dns_owner_id']))
        cn = CNameRecord.CNameRecord(self._db)
        for row in cn.list_ext(cname_owner=dns_owner_id):
            ret.append((dns.CNAME_OWNER, row['cname_id']))
        if only_type:
            return [x[0] for x in ret]
        return ret
Esempio n. 2
0
    def _get_zone_data(self, zone):
        # ARecord has key=a_record_id
        # HostInfo, SrvRecord has key=dns_owner_id
        # CnameRecords key=target_owner_id
        # entity2txt, entity2note has_key=entity_id
        for row in ARecord.ARecord(db).list_ext(zone=zone):
            id = int(row['dns_owner_id'])
            if self.a_records_by_dns_owner.has_key(id):
                self.a_records_by_dns_owner[id] += [row]
            else:
                self.a_records_by_dns_owner[id] = [row]

            # Following dict is populated to support HostFile
            self.a_records[int(row['a_record_id'])] = row
        logger.debug("... arecords")

        for row in AAAARecord.AAAARecord(db).list_ext(zone=zone):
            id = int(row['dns_owner_id'])
            if self.aaaa_records_by_dns_owner.has_key(id):
                self.aaaa_records_by_dns_owner[id] += [row]
            else:
                self.aaaa_records_by_dns_owner[id] = [row]
            # Following dict is populated to support HostFile
            self.aaaa_records[int(row['aaaa_record_id'])] = row
        logger.debug("... aaaarecords")

        for row in HostInfo.HostInfo(db).list(zone=zone):
            # Unique constraint on dns_owner_id
            self.hosts[int(row['dns_owner_id'])] = row

        logger.debug("... hosts")
        for row in CNameRecord.CNameRecord(db).list_ext(zone=zone):
            # TBD:  skal vi ha unique constraint på dns_owner?
            self.cnames.setdefault(int(row['target_owner_id']), []).append(row)
        logger.debug("... cnames")

        # From mix-in classes
        for row in DnsOwner.MXSet(db).list_mx_sets():
            self.mx_sets.setdefault(int(row['mx_set_id']), []).append(row)

        logger.debug("... mx_sets")
        for row in DnsOwner.DnsOwner(db).list(zone=zone):
            self.owner_id2mx_set[int(row['dns_owner_id'])] = int(
                row['mx_set_id'] or 0)

        logger.debug("... mx_set owners")

        for row in DnsOwner.DnsOwner(db).list_general_dns_records(
                field_type=co.field_type_txt, zone=zone):
            self.dnsowner2txt_record[int(row['dns_owner_id'])] = row
        logger.debug("... txt reocrds")

        for row in DnsOwner.DnsOwner(db).list_srv_records(zone=zone):
            # We want them listed in the same place
            # TODO: while doing that, we want it below the first target_owner_id
            self.srv_records.setdefault(int(row['service_owner_id']),
                                        []).append(row)
        logger.debug("... srv records")
Esempio n. 3
0
    def remove_arecord(self, a_record_id, try_dns_remove=False):
        """Remove an a-record identified by a_record_id.

        Will also update override_revmap and remove the entry in ip_number if
        it is no longer referred to by other tables.

        :param int a_record_id: The ARecords id.
        :param bool try_dns_remove: Remove the DNSOwner too.
        """
        ip_type = dns.IP_NUMBER
        record_type = dns.A_RECORD
        arecord = ARecord.ARecord(self._db)

        try:
            arecord.find(a_record_id)
            ip_number_id = arecord.ip_number_id
            ipnumber = IPNumber.IPNumber(self._db)
        except Errors.NotFoundError:
            arecord = AAAARecord.AAAARecord(self._db)
            arecord.find(a_record_id)
            ip_type = dns.IPv6_NUMBER
            record_type = dns.AAAA_RECORD
            ip_number_id = arecord.ipv6_number_id
            ipnumber = IPv6Number.IPv6Number(self._db)

        ipnumber.find(ip_number_id)

        dns_owner_id = arecord.dns_owner_id
        arecord._delete()

        refs = self._find.find_referers(ip_number_id=ipnumber.entity_id,
                                        ip_type=ip_type)
        if dns.REV_IP_NUMBER in refs:
            self._update_override(ipnumber.entity_id, dns_owner_id)
            refs = self._find.find_referers(ip_number_id=ipnumber.entity_id,
                                            ip_type=ip_type)

        if not (dns.REV_IP_NUMBER in refs or record_type in refs):
            # IP no longer used
            ipnumber.delete()

        # Assert that any cname/srv targets still point to atleast one
        # a-record.  Assert that host_info has atleast one associated
        # a_record.
        # TODO: This check should be somewhere that makes it is easier
        # to always enforce this constraint.
        refs = set(self._find.find_referers(dns_owner_id=dns_owner_id,
                                            ip_type=dns.IP_NUMBER))
        refs.update(self._find.find_referers(dns_owner_id=dns_owner_id,
                                             ip_type=dns.IPv6_NUMBER))
        if not any(r_type in refs
                   for r_type in (dns.A_RECORD, dns.AAAA_RECORD)):
            if dns.SRV_TARGET in refs or dns.CNAME_TARGET in refs:
                raise DNSError("Host is used as target for CNAME or SRV")

        if try_dns_remove:
            self.remove_dns_owner(dns_owner_id)
Esempio n. 4
0
    def __init__(self, *args, **kwargs):
        super(TSDUtils, self).__init__(*args, **kwargs)
        self.ou = Factory.get('OU')(self.db)
        self.et = EntityTrait.EntityTrait(self.db)

        self.dnsowner = DnsOwner.DnsOwner(self.db)
        self.subnet = Subnet.Subnet(self.db)
        self.subnet6 = IPv6Subnet.IPv6Subnet(self.db)
        self.ar = ARecord.ARecord(self.db)
        self.aaaar = AAAARecord.AAAARecord(self.db)
Esempio n. 5
0
    def __init__(self, *args, **kwargs):
        """Instantiate dns specific functionality."""
        super(HostSync, self).__init__(*args, **kwargs)
        self.host = HostInfo.HostInfo(self.db)

        self.subnet = Subnet.Subnet(self.db)
        self.subnet6 = IPv6Subnet.IPv6Subnet(self.db)

        self.ar = ARecord.ARecord(self.db)
        self.aaaar = AAAARecord.AAAARecord(self.db)
Esempio n. 6
0
 def __init__(self, db, default_zone):
     self._db = db
     self._ip_number = IPNumber.IPNumber(db)
     self._ipv6_number = IPv6Number.IPv6Number(db)
     self._arecord = ARecord.ARecord(db)
     self._aaaarecord = AAAARecord.AAAARecord(db)
     self._dns_owner = DnsOwner.DnsOwner(db)
     self._mx_set = DnsOwner.MXSet(db)
     self._host = HostInfo.HostInfo(db)
     self._cname = CNameRecord.CNameRecord(db)
     self._dns_parser = DnsParser(db, default_zone)
Esempio n. 7
0
    def set_ttl(self, owner_id, ttl):
        """Set TTL entries for this dns_owner"""

        # TODO: Currently we do this by updating the TTL in all
        # tables.  It has been decided to move ttl-information into
        # dns_owner.  However, we will not do this until after we have
        # gone into production to avoid a huge diff when comparing
        # autogenerated zone files to the original ones.

        dns_owner = DnsOwner.DnsOwner(self.db)
        dns_owner.find(owner_id)

        arecord = ARecord.ARecord(self.db)
        for row in arecord.list_ext(dns_owner_id=owner_id):
            arecord.clear()
            arecord.find(row['a_record_id'])
            arecord.ttl = ttl
            arecord.write_db()

        aaaarecord = AAAARecord.AAAARecord(self.db)
        for row in aaaarecord.list_ext(dns_owner_id=owner_id):
            aaaarecord.clear()
            aaaarecord.find(row['aaaa_record_id'])
            aaaarecord.ttl = ttl
            aaaarecord.write_db()

        host = HostInfo.HostInfo(self.db)
        try:
            host.find_by_dns_owner_id(owner_id)
        except Errors.NotFoundError:
            pass
        else:
            host.ttl = ttl
            host.write_db()

        for row in dns_owner.list_general_dns_records(dns_owner_id=owner_id):
            dns_owner.update_general_dns_record(owner_id, row['field_type'],
                                                ttl, row['data'])

        mx_set = DnsOwner.MXSet(self.db)
        for row in mx_set.list_mx_sets(target_id=owner_id):
            mx_set.clear()
            mx_set.find(row['mx_set_id'])
            mx_set.update_mx_set_member(ttl, row['pri'], row['target_id'])
        cname = CNameRecord.CNameRecord(self.db)
        for row in cname.list_ext(cname_owner=owner_id):
            cname.clear()
            cname.find(row['cname_id'])
            cname.ttl = ttl
            cname.write_db()

        for row in dns_owner.list_srv_records(owner_id=owner_id):
            dns_owner.update_srv_record_ttl(owner_id, ttl)
Esempio n. 8
0
    def find_a_record(self, host_name, ip=None):
        owner_id = self.find_target_by_parsing(host_name, dns.DNS_OWNER)

        # Check for IPv6 / IPv4
        if ip and ip.count(':') > 1:
            ar = AAAARecord.AAAARecord(self._db)
            ip_type = dns.IPv6_NUMBER
            rt = 'AAAA-record'
        elif host_name.count(':') > 1:
            # No IP specified.
            # See if host_name is an IPv6 addr and select an IPv6-type if it is
            ar = AAAARecord.AAAARecord(self._db)
            ip_type = dns.IPv6_NUMBER
            rt = 'AAAA-record'
        else:
            ar = ARecord.ARecord(self._db)
            ip_type = dns.IP_NUMBER
            rt = 'A-record'
        if ip:
            a_ip = ip
            ip = self.find_target_by_parsing(
                ip, ip_type)
            try:
                ar.find_by_owner_and_ip(ip, owner_id)
            except Errors.NotFoundError:
                raise CerebrumError(
                    "No %s with name=%s and ip=%s" % (rt, host_name, a_ip))
        else:
            try:
                ar.find_by_dns_owner_id(owner_id)
            except Errors.NotFoundError:
                raise CerebrumError("No %s with name=%s" % (rt, host_name))
            except Errors.TooManyRowsError:
                raise CerebrumError("Multiple %s with name=%s" %
                                    (rt, host_name))
        return ar.entity_id
Esempio n. 9
0
    def _populate_dnsowner(self, hostname):
        """Create or update a DnsOwner connected to the given project.

        The DnsOwner is given a trait, to affiliate it with this project-OU.

        This should rather be put in the DNS module, but due to its complexity,
        its weird layout, and my lack of IQ points to understand it, I started
        just using its API instead.

        :param str hostname: The given *FQDN* for the host.

        :rtype: DnsOwner object
        :return:
            The DnsOwner object that is created or updated.
        """
        dns_owner = DnsOwner.DnsOwner(self._db)
        dnsfind = Utils.Find(self._db, cereconf.DNS_DEFAULT_ZONE)
        ipv6number = IPv6Number.IPv6Number(self._db)
        aaaarecord = AAAARecord.AAAARecord(self._db)
        ipnumber = IPNumber.IPNumber(self._db)
        arecord = ARecord.ARecord(self._db)

        try:
            dns_owner.find_by_name(hostname)
        except Errors.NotFoundError:
            # TODO: create owner here?
            dns_owner.populate(self.const.DnsZone(cereconf.DNS_DEFAULT_ZONE),
                               hostname)
            dns_owner.write_db()
        # Affiliate with project:
        dns_owner.populate_trait(self.const.trait_project_host,
                                 target_id=self.entity_id)
        dns_owner.write_db()
        for (subnets, ipnum, record, ipstr) in (
                (self.ipv6_subnets, ipv6number, aaaarecord, "IPv6"),
                (self.ipv4_subnets, ipnumber, arecord, "IPv4")):
            # TODO: check if dnsowner already has an ip address.
            try:
                ip = dnsfind.find_free_ip(subnets.next(), no_of_addrs=1)[0]
            except StopIteration:
                raise Errors.NotFoundError("No %s-subnet for project %s" %
                                           (ipstr, self.get_project_id()))
            ipnum.populate(ip)
            ipnum.write_db()
            record.populate(dns_owner.entity_id, ipnum.entity_id)
            record.write_db()
        return dns_owner
Esempio n. 10
0
    def _get_reverse_data(self):
        for row in IPv6Number.IPv6Number(db).list(start=self.start,
                                                  stop=self.stop):
            self.ip_numbers[int(row['ipv6_number_id'])] = row

        for row in AAAARecord.AAAARecord(db).list_ext(start=self.start,
                                                      stop=self.stop):
            self.a_records.setdefault(int(row['ipv6_number_id']),
                                      []).append(row)

        for row in IPv6Number.IPv6Number(db).list_override(start=self.start,
                                                           stop=self.stop):
            self.override_ip.setdefault(int(row['ipv6_number_id']),
                                        []).append(row)
        logger.debug(
            "_get_reverse_ipv6_data -> %i, %i, %i" %
            (len(self.ip_numbers), len(self.a_records), len(self.override_ip)))
Esempio n. 11
0
    def __init__(self, operator_id):
        """Constructor. Since we are using access control, we need the
        authenticated entity's ID as a parameter.

        """
        self.db = Factory.get('Database')()
        self.db.cl_init(change_program='resource_service')
        self.co = Factory.get('Constants')(self.db)
        self.finder = Utils.Find(self.db, self.default_zone)
        self.subnet = Subnet.Subnet(self.db)
        self.aaaa = AAAARecord.AAAARecord(self.db)
        self.ip = IPv6Number.IPv6Number(self.db)

        # TODO: could we save work by only using a single, shared object of
        # the auth class? It is supposed to be thread safe.
        #self.ba = BofhdAuth(self.db)
        self.operator_id = operator_id
Esempio n. 12
0
    def full_remove_dns_owner(self, dns_owner_id):
        # fjerner alle entries der dns_owner vil være til venstre i
        # sonefila.

        self.remove_host_info(dns_owner_id)
        arecord = ARecord.ARecord(self._db)
        for row in arecord.list_ext(dns_owner_id=dns_owner_id):
            self.remove_arecord(row['a_record_id'])
        aaaarecord = AAAARecord.AAAARecord(self._db)
        for row in aaaarecord.list_ext(dns_owner_id=dns_owner_id):
            self.remove_arecord(row['aaaa_record_id'])
        self.remove_cname(dns_owner_id)
        dns_owner = DnsOwner.DnsOwner(self._db)
        for row in dns_owner.list_general_dns_records(
                dns_owner_id=dns_owner_id):
            dns_owner.delete_general_dns_record(dns_owner_id,
                                                row['field_type'])
        self.remove_dns_owner(dns_owner_id)
Esempio n. 13
0
 def __init__(self, db, logger, default_zone):
     self.logger = logger
     self.db = db
     self.const = Factory.get('Constants')(self.db)
     # TBD: This pre-allocating may interfere with multi-threaded bofhd
     self._arecord = ARecord.ARecord(self.db)
     self._aaaarecord = AAAARecord.AAAARecord(self.db)
     self._host = HostInfo.HostInfo(self.db)
     self._dns_owner = DnsOwner.DnsOwner(self.db)
     self._ip_number = IPNumber.IPNumber(self.db)
     self._ipv6_number = IPv6Number.IPv6Number(self.db)
     self._cname = CNameRecord.CNameRecord(self.db)
     self._validator = IntegrityHelper.Validator(self.db, default_zone)
     self._update_helper = IntegrityHelper.Updater(self.db)
     self._mx_set = DnsOwner.MXSet(self.db)
     self.default_zone = default_zone
     self._find = Utils.Find(self.db, default_zone)
     self._parser = Utils.DnsParser(self.db, default_zone)
Esempio n. 14
0
    def _update_override(self, ip_number_id, dns_owner_id):
        """Handles the updating of the override_reversemap when an
        ARecord is removed."""

        # Select correct IP-variant
        try:
            ipnumber = IPNumber.IPNumber(self._db)
            ipnumber.clear()
            ipnumber.find(ip_number_id)
            ar = ARecord.ARecord(self._db)
        except Errors.NotFoundError:
            ipnumber = IPv6Number.IPv6Number(self._db)
            ar = AAAARecord.AAAARecord(self._db)

        owners = []
        for row in ipnumber.list_override(ip_number_id=ip_number_id):
            if dns_owner_id == row['dns_owner_id']:
                # Always remove the reverse which corresponds to the
                # ARecord which is being removed.
                ipnumber.delete_reverse_override(ip_number_id, dns_owner_id)
            elif row['dns_owner_id'] == None:
                # We know that this IP has been associated with an
                # ARecord.  If PTR generation has been surpressed by
                # setting owner to NULL, we want to remove the reverse
                # to avoid surprises when the IP is reused.
                ipnumber.delete_reverse_override(ip_number_id, None)
            else:
                owners.append(row['dns_owner_id'])

        if len(owners) != 1:
            return

        # The single entry left is redundant if there is only one
        # ARecord referring to the IP.
        rows = ar.list_ext(ip_number_id=ip_number_id)
        if len(rows) == 1 and rows[0]['dns_owner_id'] == owners[0]:
            ipnumber.delete_reverse_override(ip_number_id, owners[0])
Esempio n. 15
0
    def get_ttl(self, owner_id):
        """Retrieve TTL ('Time to Live') setting for the records
        associated with gievn DNS-owner.

        """
        # Caveat: if TTL is set for one of the host's A*-records, it is
        # set for the host in general. If no A*-record exists, we don't
        # acknowledge any other TTL than "default"
        dns_owner = DnsOwner.DnsOwner(self.db)
        dns_owner.find(owner_id)

        # This adaption to A- and AAAA-records is very ugly, but it honours
        # "The Old Way" of getting the TTL for a host.
        ar = ARecord.ARecord(self.db)
        ar.clear()
        for r in ar.list_ext(dns_owner_id=owner_id):
            ar.find(r['a_record_id'])
            return ar.ttl
        ar = AAAARecord.AAAARecord(self.db)
        ar.clear()
        for r in ar.list_ext(dns_owner_id=owner_id):
            ar.find(r['aaaa_record_id'])
            return ar.ttl
        return None
Esempio n. 16
0
    def process_dns(self):
        """Sync all DNS data with the gateway.

        In order, this function will:

          1. Look up Cerebrum subnets and VLANs
          2. Look up, compare and update VLANs in gateway
          3. Look up, compare and update subnets in gateway
          4. Look up Cerebrum hosts and IPs
          5. Look up, compare and update hosts in gateway
          6. Look up, compare and update IPs in gateway
        """
        logger.debug("Processing DNS")

        # Map subnets to projects:
        sub2ouid = dict(
            (row['entity_id'], row['target_id'])
            for row in self.ent.list_traits(code=self.co.trait_project_subnet)
            if row['target_id'] in self.ouid2pid)
        sub2ouid.update(
            dict((row['entity_id'], row['target_id'])
                 for row in self.ent.list_traits(
                     code=self.co.trait_project_subnet6)))
        logger.debug("Mapped %d subnets to OUs", len(sub2ouid))
        sub2pid = dict((k, self.ouid2pid[v]) for k, v in sub2ouid.iteritems()
                       if v in self.ouid2pid)
        logger.debug("Mapped %d subnets to projects", len(sub2pid))

        # Process subnets and VLANs:
        subnets, vlans = self._get_subnets_and_vlans(sub2pid)
        self._process_vlans(self.gw.list_vlans(), vlans)
        self._process_subnets(self.gw.list_subnets(), subnets, sub2ouid)

        # Mapping hosts to projects by what subnet they're on:
        hostid2pid = dict(
            (r['entity_id'], self.ouid2pid.get(r['target_id']))
            for r in self.ent.list_traits(code=self.co.trait_project_host)
            if r['target_id'] in self.ouid2pid)
        host2project = dict()
        host2ips = dict()

        def _collect(record, ip_attr):
            if record['dns_owner_id'] not in hostid2pid:
                # Host is not connected to a project, and is therefore ignored.
                logger.debug2("Host not connected to project: %s",
                              record['name'])
                return
            hostname = record['name'].rstrip('.')
            host2project[hostname] = hostid2pid[record['dns_owner_id']]
            host2ips.setdefault(hostname, set()).add(record[ip_attr])

        for row in AAAARecord.AAAARecord(self.db).list_ext():
            _collect(row, 'aaaa_ip')

        for row in ARecord.ARecord(self.db).list_ext():
            _collect(row, 'a_ip')

        logger.debug2("Mapped %d hosts to projects", len(host2project))
        logger.debug2("Mapped %d hosts with at least one IP address",
                      len(host2ips))

        # Process hosts and ips:
        self._process_hosts(self.gw.list_hosts(), host2project)
        self._process_ips(self.gw.list_ips(), host2project, host2ips)