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 _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)))
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, 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
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): """Raise a SubnetError if this subnet has addresses in use, that are really reserved. """ ip_number = IPv6Number.IPv6Number(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 = IPv6Calc.ip_to_long(row['aaaa_ip']) if current_address in self.reserved_adr: res_adr_in_use.append(row['aaaa_ip']) 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 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])
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