def _get_reverse_data(self, start, stop): for row in IPNumber.IPNumber(db).list(start=start, stop=stop): self.ip_numbers[int(row['ip_number_id'])] = row for row in ARecord.ARecord(db).list_ext(start=start, stop=stop): self.a_records.setdefault(int(row['ip_number_id']), []).append(row) for row in IPNumber.IPNumber(db).list_override(start=start, stop=stop): self.override_ip.setdefault(int(row['ip_number_id']), []).append(row) logger.debug("_get_reverse_data -> %i, %i, %i", len(self.ip_numbers), len(self.a_records), len(self.override_ip))
def check_reserved_addresses_in_use(self): """TODO: DOC """ # Need to import Utils here, since Utils imports this module, # and we cannot have circular module dependencies. from Cerebrum.modules.dns.Utils import Find default_zone = self.const.DnsZone(getattr(cereconf, 'DNS_DEFAULT_ZONE', 'uio')) find = Find(self._db, default_zone) ip_number = IPNumber.IPNumber(self._db) ip_number.clear() res_adr_in_use = [] for row in ip_number.find_in_range(self.ip_min, self.ip_max): current_address = long(row['ipnr']) if current_address in self.reserved_adr: res_adr_in_use.append(IPCalc.long_to_ip(current_address)) if res_adr_in_use: res_adr_in_use.sort() raise SubnetError( "The following reserved ip's are already in use " + "on (new?) subnet %s/%s: " % (self.subnet_ip, self.subnet_mask) + "'%s'." % (', '.join(res_adr_in_use)))
def get_data_from_DB(): """Retrieves all relevant data needed from database @rtype: dict @return: A dictionary where the keys are MAC-addresses and the values are lists of IP-addresses associated with each MAC-address. """ ips_by_mac = {} ipnumber = IPNumber.IPNumber(db) all_ips = (x for x in ipnumber.list() if x['mac_adr'] is not None) mac_ips = 0 for ip in all_ips: current = ips_by_mac.get(ip['mac_adr'], []) current.append(ip['a_ip']) ips_by_mac[ip['mac_adr']] = current mac_ips += 1 ipnumber = IPv6Number.IPv6Number(db) all_ips = (x for x in ipnumber.list() if x['mac_adr'] is not None) for ip in all_ips: current = ips_by_mac.get(ip['mac_adr'], []) current.append(ip['aaaa_ip']) ips_by_mac[ip['mac_adr']] = current mac_ips += 1 logger.info("Found a total of %s MAC-addesses in DB", len(ips_by_mac.keys())) logger.info("Found a total of %s associated IP-addesses in DB", mac_ips) return ips_by_mac
def remove_reverse_override(self, ip_number_id, dest_host): """Remove reverse-map override for ip_number_id. Will remove dns_owner and ip_number entries if they are no longer in use.""" try: ipnumber = IPv6Number.IPv6Number(self._db) ipnumber.find(ip_number_id) a_type = dns.AAAA_RECORD ip_type = dns.IPv6_NUMBER o_ip_type = dns.IP_NUMBER o_ipnumber = IPNumber.IPNumber(self._db) except Errors.NotFoundError: ipnumber = IPNumber.IPNumber(self._db) ipnumber.find(ip_number_id) a_type = dns.A_RECORD ip_type = dns.IP_NUMBER o_ip_type = dns.IPv6_NUMBER o_ipnumber = IPv6Number.IPv6Number(self._db) ipnumber.delete_reverse_override(ip_number_id, dest_host) refs = self._find.find_referers(ip_number_id=ip_number_id, ip_type=ip_type) if not (dns.REV_IP_NUMBER in refs or a_type in refs): # IP no longer used ipnumber.delete() if dest_host is not None: refs = self._find.find_referers(dns_owner_id=dest_host, ip_type=ip_type) refs += self._find.find_referers(dns_owner_id=dest_host, ip_type=o_ip_type) if not refs: tmp = [] for row in ipnumber.list_override(dns_owner_id=dest_host): # One might argue that find_referers also should find # this type of refs. tmp.append((dns.DNS_OWNER, row['dns_owner_id'])) for row in o_ipnumber.list_override(dns_owner_id=dest_host): tmp.append((dns.DNS_OWNER, row['dns_owner_id'])) if not tmp: dns_owner = DnsOwner.DnsOwner(self._db) dns_owner.find(dest_host) dns_owner.delete()
def __init__(self, db, default_zone): self._db = db self._arecord = ARecord.ARecord(self._db) self._ip_number = IPNumber.IPNumber(self._db) self._dns_owner = DnsOwner.DnsOwner(self._db) self._mx_set = DnsOwner.MXSet(self._db) self._host = HostInfo.HostInfo(self._db) self._cname = CNameRecord.CNameRecord(self._db) self._default_zone = default_zone
def add_reverse_override(self, ip_number_id, dest_host): # TODO: Only allow one None/ip try: ipnumber = IPv6Number.IPv6Number(self._db) ipnumber.find(ip_number_id) except Errors.NotFoundError: ipnumber = IPNumber.IPNumber(self._db) ipnumber.add_reverse_override(ip_number_id, dest_host)
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)
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)
def _find_available_ip(self, subnet, no_of_addrs=None, search_start=0): """Returns all ips that are not reserved or taken on the given subnet in ascending order.""" try: sub = Subnet.Subnet(self._db) sub.find(subnet) ip_number = IPNumber.IPNumber(self._db) ip_key = 'ip_number_id' ipnr = lambda x: x['ipnr'] start = sub.ip_min except SubnetError: sub = IPv6Subnet.IPv6Subnet(self._db) sub.find(subnet) ip_number = IPv6Number.IPv6Number(self._db) ip_key = 'ipv6_number_id' ipnr = lambda x: IPv6Calc.ip_to_long(x['aaaa_ip']) # We'll do this, since we don't want bofh to be stuck forever # trying to fetch all IPv6-addresses. # This is ugly, but it's not only-only. if no_of_addrs is None: no_of_addrs = 100 # A special case for IPv6 subnets, is that we'll want to be able # to start allocating addresses a given place in the subnet, # without using the reserved-addresses-functionality. if search_start >= sub.ip_min: start = search_start else: start = (sub.ip_min + cereconf.DEFAULT_IPv6_SUBNET_ALLOCATION_START + search_start) try: taken = {} for row in ip_number.find_in_range(start, sub.ip_max): taken[long(ipnr(row))] = int(row[ip_key]) stop = sub.ip_max - start + 1 n = 0 ret = [] while n < stop: if no_of_addrs is not None and len(ret) == no_of_addrs: break if ( long(start+n) not in taken and n+start not in sub.reserved_adr ): ret.append(n+start) n += 1 return ret except SubnetError: # Unable to find subnet; therefore, no available ips to report return []
def find_overrides(self, dns_owner_id, only_type=False): """ """ ret = [] ip = IPNumber.IPNumber(self._db) for row in ip.list_override(dns_owner_id=dns_owner_id): ret.append((dns.IP_NUMBER, row['ip_number_id'],)) ip = IPv6Number.IPv6Number(self._db) for row in ip.list_override(dns_owner_id=dns_owner_id): ret.append((dns.IPv6_NUMBER, row['ipv6_number_id'],)) if only_type: return [x[0] for x in ret] return ret
def count_used_ips(self, subnet): """Returns the number of used ips on the given subnet. Returns a long. """ if '.' in subnet: ip_number = IPNumber.IPNumber(self._db) sub = Subnet.Subnet(self._db) else: ip_number = IPv6Number.IPv6Number(self._db) sub = IPv6Subnet.IPv6Subnet(self._db) sub.find(subnet) return ip_number.count_in_range(sub.ip_min, sub.ip_max)
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
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)
def check_reserved_addresses_in_use(self): """TODO: DOC """ ip_number = IPNumber.IPNumber(self._db) ip_number.clear() res_adr_in_use = [] for row in ip_number.find_in_range(self.ip_min, self.ip_max): current_address = long(row['ipnr']) if current_address in self.reserved_adr: res_adr_in_use.append(IPCalc.long_to_ip(current_address)) if res_adr_in_use: res_adr_in_use.sort() raise SubnetError( "The following reserved ip's are already in use" " on (new?) subnet %s/%s: '%s'." % (self.subnet_ip, self.subnet_mask, ', '.join(res_adr_in_use)))
def find_used_ips(self, subnet): """Returns all ips that are taken on the given subnet in ascending order. Addresses returned are as xxx.xxx.xxx.xxx, not longs. """ if '.' in subnet: ip_number = IPNumber.IPNumber(self._db) sub = Subnet.Subnet(self._db) ip_key = 'a_ip' else: ip_number = IPv6Number.IPv6Number(self._db) sub = IPv6Subnet.IPv6Subnet(self._db) ip_key = 'aaaa_ip' ip_number.clear() sub.clear() sub.find(subnet) ret = [] for row in ip_number.find_in_range(sub.ip_min, sub.ip_max): ret.append(row[ip_key]) return ret
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])
from Cerebrum import Group from Cerebrum.Utils import Factory from Cerebrum.modules.dns import ARecord from Cerebrum.modules.dns import CNameRecord from Cerebrum.modules.dns import DnsOwner from Cerebrum.modules.dns import HostInfo from Cerebrum.modules.dns import IPNumber from Cerebrum.modules.dns import Utils sys.argv.extend(["--logger-level", "DEBUG"]) logger = Factory.get_logger("cronjob") db = Factory.get('Database')() db.cl_init(change_program='import_dns') co = Factory.get('Constants')(db) ipnumber = IPNumber.IPNumber(db) arecord = ARecord.ARecord(db) cname = CNameRecord.CNameRecord(db) dnsowner = DnsOwner.DnsOwner(db) host = HostInfo.HostInfo(db) mx_set = DnsOwner.MXSet(db) # logger.setLevel(logger.debug) header_splitter = r'^; AUTOGENERATED: do not edit below this line' class Netgroups(object): class MergeRestart(Exception): pass def __init__(self, fname, default_zone): self._fname = fname self._default_zone = default_zone
def find_referers(self, ip_number_id=None, dns_owner_id=None, only_type=True, ip_type=dns.IP_NUMBER): """Return information about registrations that point to this ip-number/dns-owner. If only_type=True, returns a list of owner_type. Otherwise returns a list of (owner_type, owner_id) tuples""" # We choose classes and record type depending on the ip_type # parameter. This is a bit dirty, but reduces the amount of # functions required. ip_class = IPNumber.IPNumber if ( ip_type == dns.IP_NUMBER ) else IPv6Number.IPv6Number record_class = ARecord.ARecord if ( ip_type == dns.IP_NUMBER ) else AAAARecord.AAAARecord record_type = dns.A_RECORD if ( ip_type == dns.IP_NUMBER ) else dns.AAAA_RECORD ip_key = 'ip_number_id' if ( ip_type == dns.IP_NUMBER ) else 'ipv6_number_id' record_key = 'a_record_id' if ( ip_type == dns.IP_NUMBER ) else 'aaaa_record_id' # Not including entity-note assert not (ip_number_id and dns_owner_id) ret = [] if ip_number_id and ip_type == dns.REV_IP_NUMBER: for ipn, key in [ (IPNumber.IPNumber(self._db), 'ip_number_id'), (IPv6Number.IPv6Number(self._db), 'ipv6_number_id')]: for row in ipn.list_override(ip_number_id=ip_number_id): ret.append((dns.REV_IP_NUMBER, row[key])) if only_type: return [x[0] for x in ret] return ret if ip_number_id: ipnumber = ip_class(self._db) for row in ipnumber.list_override(ip_number_id=ip_number_id): ret.append((dns.REV_IP_NUMBER, row[ip_key])) arecord = record_class(self._db) for row in arecord.list_ext(ip_number_id=ip_number_id): ret.append((record_type, row[record_key])) if only_type: return [x[0] for x in ret] return ret mx = DnsOwner.MXSet(self._db) for row in mx.list_mx_sets(target_id=dns_owner_id): ret.append((dns.MX_SET, row['mx_set_id'])) dns_owner = DnsOwner.DnsOwner(self._db) for row in dns_owner.list_srv_records(target_owner_id=dns_owner_id): ret.append((dns.SRV_TARGET, row['service_owner_id'])) cn = CNameRecord.CNameRecord(self._db) for row in cn.list_ext(target_owner=dns_owner_id): ret.append((dns.CNAME_TARGET, row['cname_id'])) arecord = record_class(self._db) for row in arecord.list_ext(dns_owner_id=dns_owner_id): ret.append((record_type, row[record_key])) hi = HostInfo.HostInfo(self._db) for row in hi.list_ext(dns_owner_id=dns_owner_id): ret.append((dns.HOST_INFO, row['host_id'],)) if only_type: return [x[0] for x in ret] return ret