def render(self, session, service, protocol, dns_domain, target, dns_environment, **kwargs): name = "_%s._%s" % (service.strip().lower(), protocol.strip().lower()) dbfqdn = Fqdn.get_unique(session, name=name, dns_domain=dns_domain, dns_environment=dns_environment) if target: dbtarget = Fqdn.get_unique(session, target, compel=True) else: dbtarget = None rrs = [] if dbfqdn: for rr in dbfqdn.dns_records: if not isinstance(rr, SrvRecord): # pragma: no cover # This case can't happen right now. Maybe one day if we # add support for DNSSEC... continue if dbtarget and rr.target != dbtarget: continue rrs.append(rr) if not rrs: if dbtarget: msg = ", with target %s" % dbtarget.fqdn else: msg = "" raise NotFoundException("%s for service %s, protocol %s in DNS " "domain %s%s not found." % (SrvRecord._get_class_label(), service, protocol, dns_domain, msg)) map(delete_dns_record, rrs) session.flush() return
def rename_hardware(session, dbhw_ent, rename_to): if "." in rename_to: if not dbhw_ent.primary_name: raise ArgumentError( "{0} does not have a primary name, renaming " "using an FQDN is not possible.".format(dbhw_ent)) old_domain = dbhw_ent.primary_name.fqdn.dns_domain dns_env = dbhw_ent.primary_name.fqdn.dns_environment new_label, new_domain = parse_fqdn(session, rename_to) else: new_label = rename_to if dbhw_ent.primary_name: old_domain = new_domain = dbhw_ent.primary_name.fqdn.dns_domain dns_env = dbhw_ent.primary_name.fqdn.dns_environment else: new_domain = None dns_env = None old_domain.lock_row() if new_domain != old_domain: new_domain.lock_row() dbhw_ent.check_label(new_label) HardwareEntity.get_unique(session, new_label, preclude=True) old_label = dbhw_ent.label fqdns = [] for addr in dbhw_ent.all_addresses(): fqdns.extend([dns_rec.fqdn for dns_rec in addr.dns_records]) # This case handles reserved names if dbhw_ent.primary_name and dbhw_ent.primary_name.fqdn not in fqdns: fqdns.append(dbhw_ent.primary_name.fqdn) # Filter out unrelated FQDNs fqdns = [ fqdn for fqdn in fqdns if fqdn.dns_domain == old_domain and ( fqdn.name == old_label or fqdn.name.startswith(old_label + "-")) ] # Update all state in one go, so disable autoflush for now. with session.no_autoflush: dbhw_ent.label = new_label for dbfqdn in fqdns: new_name = new_label + dbfqdn.name[len(old_label):] Fqdn.get_unique(session, name=new_name, dns_domain=new_domain, dns_environment=dns_env, preclude=True) dbfqdn.dns_domain = new_domain dbfqdn.name = new_name
def rename_hardware(session, dbhw_ent, rename_to): if "." in rename_to: if not dbhw_ent.primary_name: raise ArgumentError("{0} does not have a primary name, renaming " "using an FQDN is not possible." .format(dbhw_ent)) old_domain = dbhw_ent.primary_name.fqdn.dns_domain dns_env = dbhw_ent.primary_name.fqdn.dns_environment new_label, new_domain = parse_fqdn(session, rename_to) else: new_label = rename_to if dbhw_ent.primary_name: old_domain = new_domain = dbhw_ent.primary_name.fqdn.dns_domain dns_env = dbhw_ent.primary_name.fqdn.dns_environment else: new_domain = None dns_env = None old_domain.lock_row() if new_domain != old_domain: new_domain.lock_row() dbhw_ent.check_label(new_label) HardwareEntity.get_unique(session, new_label, preclude=True) old_label = dbhw_ent.label fqdns = [] for addr in dbhw_ent.all_addresses(): fqdns.extend([dns_rec.fqdn for dns_rec in addr.dns_records]) # This case handles reserved names if dbhw_ent.primary_name and dbhw_ent.primary_name.fqdn not in fqdns: fqdns.append(dbhw_ent.primary_name.fqdn) # Filter out unrelated FQDNs fqdns = [fqdn for fqdn in fqdns if fqdn.dns_domain == old_domain and (fqdn.name == old_label or fqdn.name.startswith(old_label + "-"))] # Update all state in one go, so disable autoflush for now. with session.no_autoflush: dbhw_ent.label = new_label for dbfqdn in fqdns: new_name = new_label + dbfqdn.name[len(old_label):] Fqdn.get_unique(session, name=new_name, dns_domain=new_domain, dns_environment=dns_env, preclude=True) dbfqdn.dns_domain = new_domain dbfqdn.name = new_name
def render(self, session, service, protocol, dns_domain, priority, weight, target, port, dns_environment, comments, **kwargs): dbdns_env = DnsEnvironment.get_unique_or_default( session, dns_environment) dbdns_domain = DnsDomain.get_unique(session, dns_domain, compel=True) if dbdns_domain.restricted: raise ArgumentError( "{0} is restricted, SRV records are not allowed.".format( dbdns_domain)) # TODO: we could try looking up the port based on the service, but there # are some caveats: # - the protocol name used in SRV record may not match the name used in # /etc/services # - socket.getservent() may return bogus information (like it does for # e.g. 'kerberos') service = service.strip().lower() target = target.strip().lower() dbtarget = Fqdn.get_unique(session, target, compel=True) dbsrv_rec = SrvRecord(service=service, protocol=protocol, priority=priority, weight=weight, target=dbtarget, port=port, dns_domain=dbdns_domain, dns_environment=dbdns_env, comments=comments) session.add(dbsrv_rec) session.flush() return
def render(self, session, service, protocol, dns_domain, priority, weight, target, port, dns_environment, comments, **kwargs): dbdns_env = DnsEnvironment.get_unique_or_default(session, dns_environment) dbdns_domain = DnsDomain.get_unique(session, dns_domain, compel=True) if dbdns_domain.restricted: raise ArgumentError("{0} is restricted, SRV records are not allowed." .format(dbdns_domain)) # TODO: we could try looking up the port based on the service, but there # are some caveats: # - the protocol name used in SRV record may not match the name used in # /etc/services # - socket.getservent() may return bogus information (like it does for # e.g. 'kerberos') service = service.strip().lower() target = target.strip().lower() dbtarget = Fqdn.get_unique(session, target, compel=True) dbsrv_rec = SrvRecord(service=service, protocol=protocol, priority=priority, weight=weight, target=dbtarget, port=port, dns_domain=dbdns_domain, dns_environment=dbdns_env, comments=comments) session.add(dbsrv_rec) session.flush() return
def rename_interface(session, dbinterface, rename_to): rename_to = rename_to.strip().lower() dbhw_ent = dbinterface.hardware_entity for iface in dbhw_ent.interfaces: if iface.name == rename_to: raise ArgumentError( "{0} already has an interface named {1}.".format( dbhw_ent, rename_to)) fqdn_changes = [] if dbhw_ent.primary_name: primary_fqdn = dbhw_ent.primary_name.fqdn short = primary_fqdn.name dbdns_domain = primary_fqdn.dns_domain dbdns_env = primary_fqdn.dns_environment dbdns_domain.lock_row() # Rename DNS entries that follow the standard naming convention, except # the primary name. The primary name should not have an interface # suffix, but who knows... for addr in dbinterface.assignments: if addr.label: old_name = "%s-%s-%s" % (short, dbinterface.name, addr.label) new_name = "%s-%s-%s" % (short, rename_to, addr.label) else: old_name = "%s-%s" % (short, dbinterface.name) new_name = "%s-%s" % (short, rename_to) fqdn_changes.extend([ (dns_rec.fqdn, new_name) for dns_rec in addr.dns_records if dns_rec.fqdn.name == old_name and dns_rec.fqdn.dns_domain == dbdns_domain and dns_rec.fqdn != primary_fqdn ]) else: dbdns_domain = dbdns_env = None with session.no_autoflush: dbinterface.name = rename_to for dbfqdn, new_name in fqdn_changes: Fqdn.get_unique(session, name=new_name, dns_domain=dbdns_domain, dns_environment=dbdns_env, preclude=True) dbfqdn.name = new_name
def render(self, session, fqdn, dns_environment, **kwargs): self.deprecated_command("The show_fqdn command is deprecated. Please " "use search_dns instead.", **kwargs) dbdns_env = DnsEnvironment.get_unique_or_default(session, dns_environment) dbfqdn = Fqdn.get_unique(session, fqdn=fqdn, dns_environment=dbdns_env, compel=True) q = session.query(DnsRecord) q = q.filter_by(fqdn=dbfqdn) return q.all()
def rename_interface(session, dbinterface, rename_to): rename_to = rename_to.strip().lower() dbhw_ent = dbinterface.hardware_entity for iface in dbhw_ent.interfaces: if iface.name == rename_to: raise ArgumentError("{0} already has an interface named {1}." .format(dbhw_ent, rename_to)) fqdn_changes = [] if dbhw_ent.primary_name: primary_fqdn = dbhw_ent.primary_name.fqdn short = primary_fqdn.name dbdns_domain = primary_fqdn.dns_domain dbdns_env = primary_fqdn.dns_environment dbdns_domain.lock_row() # Rename DNS entries that follow the standard naming convention, except # the primary name. The primary name should not have an interface # suffix, but who knows... for addr in dbinterface.assignments: if addr.label: old_name = "%s-%s-%s" % (short, dbinterface.name, addr.label) new_name = "%s-%s-%s" % (short, rename_to, addr.label) else: old_name = "%s-%s" % (short, dbinterface.name) new_name = "%s-%s" % (short, rename_to) fqdn_changes.extend([(dns_rec.fqdn, new_name) for dns_rec in addr.dns_records if dns_rec.fqdn.name == old_name and dns_rec.fqdn.dns_domain == dbdns_domain and dns_rec.fqdn != primary_fqdn]) else: dbdns_domain = dbdns_env = None with session.no_autoflush: dbinterface.name = rename_to for dbfqdn, new_name in fqdn_changes: Fqdn.get_unique(session, name=new_name, dns_domain=dbdns_domain, dns_environment=dbdns_env, preclude=True) dbfqdn.name = new_name
def __init__(self, service, protocol, dns_domain, dns_environment, priority, weight, port, target, **kwargs): if not isinstance(target, Fqdn): # pragma: no cover raise TypeError("The target of an SRV record must be an Fqdn.") session = object_session(target) if not session: # pragma: no cover raise AquilonError("The target name must already be part of " "the session.") # SRV records are special, as the FQDN is managed internally if "fqdn" in kwargs: # pragma: no cover raise AquilonError("SRV records do not accept an FQDN argument.") self.validate_ushort('priority', priority) self.validate_ushort('weight', weight) self.validate_ushort('port', port) # RFC 2782: # - there must be one or more address records for the target # - the target must not be an alias found_address = False for rr in target.dns_records: if isinstance(rr, ARecord): found_address = True elif isinstance(rr, Alias): raise ArgumentError("The target of an SRV record must not be " "an alias.") if not found_address: raise ArgumentError("The target of an SRV record must resolve to " "one or more addresses.") if protocol not in PROTOCOLS: raise ArgumentError("Unknown protocol %s." % protocol) name = "_%s._%s" % (service.strip().lower(), protocol.strip().lower()) # Disable autoflush because self is not ready to be pushed to the DB yet with session.no_autoflush: fqdn = Fqdn.get_or_create(session, name=name, dns_domain=dns_domain, dns_environment=dns_environment, ignore_name_check=True) # Do not allow two SRV records pointing at the same target for rr in fqdn.dns_records: if isinstance(rr, SrvRecord) and rr.target == target and \ rr.protocol == protocol and rr.service == service: raise ArgumentError("{0} already exists.".format(rr)) super(SrvRecord, self).__init__(fqdn=fqdn, priority=priority, weight=weight, port=port, target=target, **kwargs)
def render(self, session, dbuser, fqdn, building, ip, network_environment, comments, **arguments): dbnet_env = NetworkEnvironment.get_unique_or_default( session, network_environment) self.az.check_network_environment(dbuser, dbnet_env) if building: dbbuilding = Building.get_unique(session, building, compel=True) else: dbbuilding = None (short, dbdns_domain) = parse_fqdn(session, fqdn) dbfqdn = Fqdn.get_or_create(session, name=short, dns_domain=dbdns_domain, dns_environment=dbnet_env.dns_environment) if ip: dbnetwork = get_net_id_from_ip(session, ip, dbnet_env) dbdns_rec = ARecord.get_or_create(session, fqdn=dbfqdn, ip=ip, network=dbnetwork) else: dbdns_rec = ARecord.get_unique(session, dbfqdn, compel=True) ip = dbdns_rec.ip dbnetwork = dbdns_rec.network assert ip in dbnetwork.network, "IP %s is outside network %s" % ( ip, dbnetwork.ip) if ip in dbnetwork.router_ips: raise ArgumentError( "IP address {0} is already present as a router " "for {1:l}.".format(ip, dbnetwork)) # Policy checks are valid only for internal networks if dbnetwork.is_internal: if ip >= dbnetwork.first_usable_host or \ int(ip) - int(dbnetwork.ip) in dbnetwork.reserved_offsets: raise ArgumentError( "IP address {0} is not a valid router address " "on {1:l}.".format(ip, dbnetwork)) dbnetwork.routers.append( RouterAddress(ip=ip, location=dbbuilding, dns_environment=dbdns_rec.fqdn.dns_environment, comments=comments)) session.flush() # TODO: update the templates of Zebra hosts on the network return
def render(self, session, fqdn, dns_environment, **kwargs): self.deprecated_command( "The show_fqdn command is deprecated. Please " "use search_dns instead.", **kwargs) dbdns_env = DnsEnvironment.get_unique_or_default( session, dns_environment) dbfqdn = Fqdn.get_unique(session, fqdn=fqdn, dns_environment=dbdns_env, compel=True) q = session.query(DnsRecord) q = q.filter_by(fqdn=dbfqdn) return q.all()
def get_unique(cls, session, fqdn=None, name=None, dns_domain=None, dns_environment=None, compel=False, preclude=False, **kwargs): # Proxy FQDN lookup to the Fqdn class if not fqdn or not isinstance(fqdn, Fqdn): if not isinstance(dns_environment, DnsEnvironment): dns_environment = DnsEnvironment.get_unique_or_default( session, dns_environment) if fqdn: if name or dns_domain: # pragma: no cover raise TypeError("fqdn and name/dns_domain cannot be mixed") (name, dns_domain) = parse_fqdn(session, fqdn) try: # Do not pass preclude=True to Fqdn fqdn = Fqdn.get_unique(session, name=name, dns_domain=dns_domain, dns_environment=dns_environment, compel=compel) except NotFoundException: # Replace the "Fqdn ... not found" message with a more user # friendly one msg = "%s %s.%s, %s not found." % ( cls._get_class_label(), name, dns_domain, format(dns_environment, "l")) raise NotFoundException(msg) if not fqdn: return None # We already have the FQDN, no need to load it again if "query_options" not in kwargs: kwargs["query_options"] = [lazyload("fqdn")] result = super(DnsRecord, cls).get_unique(session, fqdn=fqdn, compel=compel, preclude=preclude, **kwargs) if result: # Make sure not to load the relation again if we already know its # value set_committed_value(result, 'fqdn', fqdn) return result
def render(self, session, dbuser, fqdn, building, ip, network_environment, comments, **arguments): dbnet_env = NetworkEnvironment.get_unique_or_default(session, network_environment) self.az.check_network_environment(dbuser, dbnet_env) if building: dbbuilding = Building.get_unique(session, building, compel=True) else: dbbuilding = None (short, dbdns_domain) = parse_fqdn(session, fqdn) dbfqdn = Fqdn.get_or_create(session, name=short, dns_domain=dbdns_domain, dns_environment=dbnet_env.dns_environment) if ip: dbnetwork = get_net_id_from_ip(session, ip, dbnet_env) dbdns_rec = ARecord.get_or_create(session, fqdn=dbfqdn, ip=ip, network=dbnetwork) else: dbdns_rec = ARecord.get_unique(session, dbfqdn, compel=True) ip = dbdns_rec.ip dbnetwork = dbdns_rec.network assert ip in dbnetwork.network, "IP %s is outside network %s" % (ip, dbnetwork.ip) if ip in dbnetwork.router_ips: raise ArgumentError("IP address {0} is already present as a router " "for {1:l}.".format(ip, dbnetwork)) # Policy checks are valid only for internal networks if dbnetwork.is_internal: if ip >= dbnetwork.first_usable_host or \ int(ip) - int(dbnetwork.ip) in dbnetwork.reserved_offsets: raise ArgumentError("IP address {0} is not a valid router address " "on {1:l}.".format(ip, dbnetwork)) dbnetwork.routers.append(RouterAddress(ip=ip, location=dbbuilding, dns_environment=dbdns_rec.fqdn.dns_environment, comments=comments)) session.flush() # TODO: update the templates of Zebra hosts on the network return
def render(self, session, fqdn, record_type, dns_environment, network_environment=None, **arguments): if network_environment: if not isinstance(network_environment, NetworkEnvironment): network_environment = NetworkEnvironment.get_unique_or_default( session, network_environment) if not dns_environment: dns_environment = network_environment.dns_environment dbdns_env = DnsEnvironment.get_unique_or_default( session, dns_environment) # No compel here. query(DnsRecord).filter_by(fqdn=None) will fail if the # FQDN is invalid, and that will give a better error message. dbfqdn = Fqdn.get_unique(session, fqdn=fqdn, dns_environment=dbdns_env) if record_type: if record_type in DNS_RRTYPE_MAP: cls = DNS_RRTYPE_MAP[record_type] else: cls = DnsRecord.polymorphic_subclass( record_type, "Unknown DNS record type") else: cls = DnsRecord # We want to query(ARecord) instead of # query(DnsRecord).filter_by(record_type='a_record'), because the former # works for DynamicStub as well q = session.query(cls) if cls == DnsRecord: q = q.with_polymorphic('*') q = q.filter_by(fqdn=dbfqdn) result = q.all() if not result: raise NotFoundException("%s %s not found." % (cls._get_class_label(), fqdn)) return result
def render(self, session, logger, fqdn, dns_environment, target, comments, **kwargs): dbdns_env = DnsEnvironment.get_unique_or_default(session, dns_environment) dbfqdn = Fqdn.get_or_create(session, dns_environment=dbdns_env, fqdn=fqdn) if dbfqdn.dns_domain.restricted: raise ArgumentError("{0} is restricted, aliases are not allowed." .format(dbfqdn.dns_domain)) DnsRecord.get_unique(session, fqdn=dbfqdn, preclude=True) dbtarget = create_target_if_needed(session, logger, target, dbdns_env) try: db_record = Alias(fqdn=dbfqdn, target=dbtarget, comments=comments) session.add(db_record) except ValueError, err: raise ArgumentError(err.message)
def add_manager(self, session, logger, dbmachine, old_ip, old_network): if not old_ip: logger.client_info("No IP address available for system being " "removed, not auto-creating manager for %s." % dbmachine.label) return if not dbmachine.host: logger.client_info("Machine %s is not linked to a host, not " "auto-creating manager with IP address " "%s." % (dbmachine.label, old_ip)) return manager = "%sr.%s" % (dbmachine.primary_name.fqdn.name, dbmachine.primary_name.fqdn.dns_domain.name) try: dbfqdn = Fqdn.get_or_create(session, fqdn=manager, preclude=True) except ArgumentError, e: logger.client_info("Could not create manager with name %s and " "IP address %s for machine %s: %s" % (manager, old_ip, dbmachine.label, e)) return
def get_unique(cls, session, fqdn=None, name=None, dns_domain=None, dns_environment=None, compel=False, preclude=False, **kwargs): # Proxy FQDN lookup to the Fqdn class if not fqdn or not isinstance(fqdn, Fqdn): if not isinstance(dns_environment, DnsEnvironment): dns_environment = DnsEnvironment.get_unique_or_default(session, dns_environment) if fqdn: if name or dns_domain: # pragma: no cover raise TypeError("fqdn and name/dns_domain cannot be mixed") (name, dns_domain) = parse_fqdn(session, fqdn) try: # Do not pass preclude=True to Fqdn fqdn = Fqdn.get_unique(session, name=name, dns_domain=dns_domain, dns_environment=dns_environment, compel=compel) except NotFoundException: # Replace the "Fqdn ... not found" message with a more user # friendly one msg = "%s %s.%s, %s not found." % (cls._get_class_label(), name, dns_domain, format(dns_environment, "l")) raise NotFoundException(msg) if not fqdn: return None # We already have the FQDN, no need to load it again if "query_options" not in kwargs: kwargs["query_options"] = [lazyload("fqdn")] result = super(DnsRecord, cls).get_unique(session, fqdn=fqdn, compel=compel, preclude=preclude, **kwargs) if result: # Make sure not to load the relation again if we already know its # value set_committed_value(result, 'fqdn', fqdn) return result
def render(self, session, logger, fqdn, dns_environment, target, comments, **kwargs): dbdns_env = DnsEnvironment.get_unique_or_default( session, dns_environment) dbfqdn = Fqdn.get_or_create(session, dns_environment=dbdns_env, fqdn=fqdn) if dbfqdn.dns_domain.restricted: raise ArgumentError( "{0} is restricted, aliases are not allowed.".format( dbfqdn.dns_domain)) DnsRecord.get_unique(session, fqdn=dbfqdn, preclude=True) dbtarget = create_target_if_needed(session, logger, target, dbdns_env) try: db_record = Alias(fqdn=dbfqdn, target=dbtarget, comments=comments) session.add(db_record) except ValueError, err: raise ArgumentError(err.message)
def render(self, session, fqdn, record_type, dns_environment, network_environment=None, **arguments): if network_environment: if not isinstance(network_environment, NetworkEnvironment): network_environment = NetworkEnvironment.get_unique_or_default(session, network_environment) if not dns_environment: dns_environment = network_environment.dns_environment dbdns_env = DnsEnvironment.get_unique_or_default(session, dns_environment) # No compel here. query(DnsRecord).filter_by(fqdn=None) will fail if the # FQDN is invalid, and that will give a better error message. dbfqdn = Fqdn.get_unique(session, fqdn=fqdn, dns_environment=dbdns_env) if record_type: if record_type in DNS_RRTYPE_MAP: cls = DNS_RRTYPE_MAP[record_type] else: cls = DnsRecord.polymorphic_subclass(record_type, "Unknown DNS record type") else: cls = DnsRecord # We want to query(ARecord) instead of # query(DnsRecord).filter_by(record_type='a_record'), because the former # works for DynamicStub as well q = session.query(cls) if cls == DnsRecord: q = q.with_polymorphic('*') q = q.filter_by(fqdn=dbfqdn) result = q.all() if not result: raise NotFoundException("%s %s not found." % (cls._get_class_label(), fqdn)) return result
def create_target_if_needed(session, logger, target, dbdns_env): """ Create FQDNs in restricted domains. This is used to allow pointing CNAME and PTR records to DNS domains we otherwise don't manage. """ name, dbtarget_domain = parse_fqdn(session, target) dbtarget_domain.lock_row() q = session.query(Fqdn) q = q.filter_by(dns_environment=dbdns_env) q = q.filter_by(dns_domain=dbtarget_domain) q = q.filter_by(name=name) try: dbtarget = q.one() except NoResultFound: if not dbtarget_domain.restricted: raise NotFoundException( "Target FQDN {0} does not exist in {1:l}.".format( target, dbdns_env)) dbtarget = Fqdn(name=name, dns_domain=dbtarget_domain, dns_environment=dbdns_env) try: socket.gethostbyname(dbtarget.fqdn) except socket.gaierror, e: logger.warning("WARNING: Will create a reference to {0.fqdn!s}, " "but trying to resolve it resulted in an error: " "{1.strerror}.".format(dbtarget, e)) session.add(dbtarget) dbtarget_rec = ReservedName(fqdn=dbtarget) session.add(dbtarget_rec)
def grab_address(session, fqdn, ip, network_environment=None, dns_environment=None, comments=None, allow_restricted_domain=False, allow_multi=False, allow_reserved=False, relaxed=False, preclude=False): """ Take ownership of an address. This is a bit complicated because due to DNS propagation delays, we want to allow users to pre-define a DNS address and then assign the address to a host later. Parameters: session: SQLA session handle fqdn: the name to allocate/take over ip: the IP address to allocate/take over network_environment: where the IP address lives dns_enviromnent: where the FQDN lives comments: any comments to attach to the DNS record if it is created as new allow_restricted_domain: if True, adding entries to restricted DNS domains is allowed, otherwise it is denied. Default is False. allow_multi: if True, allow the same FQDN to be added multiple times with different IP addresses. Deault is False. allow_reserved: if True, allow creating a ReservedName instead of an ARecord if no IP address was specified. Default is False. preclude: if True, forbid taking over an existing DNS record, even if it is not referenced by any AddressAssignment records. Default is False. """ if not isinstance(network_environment, NetworkEnvironment): network_environment = NetworkEnvironment.get_unique_or_default(session, network_environment) if not dns_environment: dns_environment = network_environment.dns_environment elif not isinstance(dns_environment, DnsEnvironment): dns_environment = DnsEnvironment.get_unique(session, dns_environment, compel=True) # Non-default DNS environments may contain anything, but we want to keep # the internal environment clean if dns_environment.is_default and not network_environment.is_default: raise ArgumentError("Entering external IP addresses to the " "internal DNS environment is not allowed.") short, dbdns_domain = parse_fqdn(session, fqdn) # Lock the domain to prevent adding/deleting records while we're checking # FQDN etc. availability dbdns_domain.lock_row() if dbdns_domain.restricted and not allow_restricted_domain: raise ArgumentError("{0} is restricted, adding extra addresses " "is not allowed.".format(dbdns_domain)) dbfqdn = Fqdn.get_or_create(session, dns_environment=dns_environment, name=short, dns_domain=dbdns_domain, query_options=[joinedload('dns_records')]) existing_record = None newly_created = False if ip: dbnetwork = get_net_id_from_ip(session, ip, network_environment) check_ip_restrictions(dbnetwork, ip, relaxed=relaxed) dbnetwork.lock_row() # No filtering on DNS environment. If an address is dynamic in one # environment, it should not be considered static in a different # environment. q = session.query(DynamicStub) q = q.filter_by(network=dbnetwork) q = q.filter_by(ip=ip) dbdns_rec = q.first() _forbid_dyndns(dbdns_rec) # Verify that no other record uses the same IP address, this time taking # the DNS environemt into consideration. # While the DNS would allow different A records to point to the same IP # address, the current user expectation is that creating a DNS entry # also counts as a reservation, so we can not allow this use case. If we # want to implement such a feature later, the best way would be to # subclass Alias and let that subclass emit an A record instead of a # CNAME when the dump_dns command is called. q = session.query(ARecord) q = q.filter_by(network=dbnetwork) q = q.filter_by(ip=ip) q = q.join(ARecord.fqdn) q = q.filter_by(dns_environment=dns_environment) dbrecords = q.all() if dbrecords and len(dbrecords) > 1: # pragma: no cover # We're just trying to make sure this never happens raise AquilonError("IP address %s is referenced by multiple " "DNS records: %s" % (ip, ", ".join([format(rec, "a") for rec in dbrecords]))) if dbrecords and dbrecords[0].fqdn != dbfqdn: raise ArgumentError("IP address {0} is already in use by {1:l}." .format(ip, dbrecords[0])) # Check if the name is used already for dbdns_rec in dbfqdn.dns_records: if isinstance(dbdns_rec, ARecord): _forbid_dyndns(dbdns_rec) _check_netenv_compat(dbdns_rec, network_environment) if dbdns_rec.ip == ip and dbdns_rec.network == dbnetwork: existing_record = dbdns_rec elif not allow_multi: raise ArgumentError("{0} points to a different IP address." .format(dbdns_rec)) elif isinstance(dbdns_rec, ReservedName): existing_record = convert_reserved_to_arecord(session, dbdns_rec, dbnetwork, ip) newly_created = True else: # Exclude aliases etc. raise ArgumentError("{0} cannot be used for address assignment." .format(dbdns_rec)) if not existing_record: existing_record = ARecord(fqdn=dbfqdn, ip=ip, network=dbnetwork, comments=comments) session.add(existing_record) newly_created = True else: if not dbfqdn.dns_records: # There's no IP, and the name did not exist before. Create a # reservation, but only if the caller allowed that use case. if not allow_reserved: raise ArgumentError("DNS Record %s does not exist." % dbfqdn) existing_record = ReservedName(fqdn=dbfqdn, comments=comments) newly_created = True else: # There's no IP, but the name is already in use. We need a single IP # address. if len(dbfqdn.dns_records) > 1: raise ArgumentError("{0} does not resolve to a single IP address." .format(dbfqdn)) existing_record = dbfqdn.dns_records[0] _forbid_dyndns(existing_record) if not isinstance(existing_record, ARecord): # Exclude aliases etc. raise ArgumentError("{0} cannot be used for address assignment." .format(existing_record)) # Verify that the existing record is in the network environment the # caller expects _check_netenv_compat(existing_record, network_environment) ip = existing_record.ip dbnetwork = existing_record.network dbnetwork.lock_row() if existing_record.hardware_entity: raise ArgumentError("{0} is already used as the primary name of {1:cl} " "{1.label}.".format(existing_record, existing_record.hardware_entity)) if preclude and not newly_created: raise ArgumentError("{0} already exists.".format(existing_record)) if ip: q = session.query(AddressAssignment) q = q.filter_by(network=dbnetwork) q = q.filter_by(ip=ip) addr = q.first() if addr: raise ArgumentError("IP address {0} is already in use by " "{1:l}.".format(ip, addr.interface)) return (existing_record, newly_created)
def render(self, session, logger, startip, endip, dns_domain, prefix, **arguments): if not prefix: prefix = 'dynamic' dbnet_env = NetworkEnvironment.get_unique_or_default(session) dbdns_env = DnsEnvironment.get_unique_or_default(session) startnet = get_net_id_from_ip(session, startip, dbnet_env) endnet = get_net_id_from_ip(session, endip, dbnet_env) if startnet != endnet: raise ArgumentError("IP addresses %s (%s) and %s (%s) must be on " "the same subnet." % (startip, startnet.ip, endip, endnet.ip)) dbdns_domain = DnsDomain.get_unique(session, dns_domain, compel=True) dbdns_domain.lock_row() startnet.lock_row() q = session.query(AddressAssignment.ip) q = q.filter_by(network=startnet) q = q.filter(AddressAssignment.ip >= startip) q = q.filter(AddressAssignment.ip <= endip) q = q.order_by(AddressAssignment.ip) conflicts = q.all() if conflicts: raise ArgumentError( "Cannot allocate the address range because the " "following IP addresses are already in use:\n" + ", ".join([str(c.ip) for c in conflicts])) # No filtering on DNS environment. If an address is dynamic in one # environment, it should not be considered static in a different # environment. q = session.query(ARecord) q = q.filter_by(network=startnet) q = q.filter(ARecord.ip >= startip) q = q.filter(ARecord.ip <= endip) q = q.order_by(ARecord.ip) conflicts = q.all() if conflicts: raise ArgumentError( "Cannot allocate the address range because the " "following DNS records already exist:\n" + "\n".join([format(c, "a") for c in conflicts])) dsdb_runner = DSDBRunner(logger=logger) with session.no_autoflush: for ipint in range(int(startip), int(endip) + 1): ip = IPv4Address(ipint) check_ip_restrictions(startnet, ip) name = "%s-%s" % (prefix, str(ip).replace('.', '-')) dbfqdn = Fqdn.get_or_create(session, name=name, dns_domain=dbdns_domain, dns_environment=dbdns_env, preclude=True) dbdynamic_stub = DynamicStub(fqdn=dbfqdn, ip=ip, network=startnet) session.add(dbdynamic_stub) dsdb_runner.add_host_details(dbfqdn, ip) session.flush() # This may take some time if the range is big, so be verbose dsdb_runner.commit_or_rollback("Could not add addresses to DSDB", verbose=True) return
def refresh_windows_hosts(self, session, logger, containers): conn = sqlite3.connect(self.config.get("broker", "windows_host_info")) # Enable dictionary-style access to the rows. conn.row_factory = sqlite3.Row windows_hosts = {} interfaces = {} cur = conn.cursor() # There are more fields in the dataset like machine and # aqhostname that might be useful for error messages but these # are sufficient. cur.execute("select ether, windowshostname from machines") for row in cur: host = row["windowshostname"] if host: host = host.strip().lower() else: continue mac = row["ether"] if mac: mac = mac.strip().lower() windows_hosts[host] = mac interfaces[mac] = host success = [] failed = [] q = session.query(Host) q = q.filter_by(comments='Created by refresh_windows_host') for dbhost in q.all(): mac_addresses = [iface.mac for iface in dbhost.machine.interfaces] if dbhost.fqdn in windows_hosts and \ windows_hosts[dbhost.fqdn] in mac_addresses: # All is well continue deps = get_host_dependencies(session, dbhost) if deps: msg = "Skipping removal of host %s with dependencies: %s" % \ (dbhost.fqdn, ", ".join(deps)) failed.append(msg) logger.info(msg) continue dbmachine = dbhost.machine success.append("Removed host entry for %s (%s)" % (dbmachine.label, dbmachine.fqdn)) if dbmachine.vm_container: containers.add(dbmachine.vm_container) session.delete(dbhost) dbdns_rec = dbmachine.primary_name dbmachine.primary_name = None delete_dns_record(dbdns_rec) session.flush() # The Host() creations below fail when autoflush is enabled. session.autoflush = False dbdomain = Domain.get_unique(session, self.config.get("archetype_windows", "host_domain"), compel=InternalError) dbarchetype = Archetype.get_unique(session, "windows", compel=InternalError) dbpersonality = Personality.get_unique(session, archetype=dbarchetype, name="generic", compel=InternalError) dbstatus = HostLifecycle.get_unique(session, "ready", compel=InternalError) dbos = OperatingSystem.get_unique(session, name="windows", version="generic", archetype=dbarchetype, compel=InternalError) for (host, mac) in windows_hosts.items(): try: (short, dbdns_domain) = parse_fqdn(session, host) except AquilonError, err: msg = "Skipping host %s: %s" % (host, err) failed.append(msg) logger.info(msg) continue existing = DnsRecord.get_unique(session, name=short, dns_domain=dbdns_domain) if existing: if not existing.hardware_entity: msg = "Skipping host %s: It is not a primary name." % host failed.append(msg) logger.info(msg) continue # If these are invalid there should have been a deletion # attempt above. if not existing.hardware_entity.interfaces: msg = "Skipping host %s: Host already exists but has " \ "no interface attached." % host failed.append(msg) logger.info(msg) elif existing.hardware_entity.interfaces[0].mac != mac: msg = "Skipping host %s: Host already exists but with " \ "MAC address %s and not %s." % \ (host, existing.hardware_entity.interfaces[0].mac, mac) failed.append(msg) logger.info(msg) continue dbinterface = session.query(Interface).filter_by(mac=mac).first() if not dbinterface: msg = "Skipping host %s: MAC address %s is not present in " \ "AQDB." % (host, mac) failed.append(msg) logger.info(msg) continue q = session.query(Machine) q = q.filter_by(id=dbinterface.hardware_entity.id) dbmachine = q.first() if not dbmachine: msg = "Skipping host %s: The AQDB interface with MAC address " \ "%s is tied to hardware %s instead of a virtual " \ "machine." % \ (host, mac, dbinterface.hardware_entity.label) failed.append(msg) logger.info(msg) continue if dbinterface.assignments: msg = "Skipping host %s: The AQDB interface with MAC address " \ "%s is already tied to %s." % \ (host, mac, dbinterface.assignments[0].fqdns[0]) failed.append(msg) logger.info(msg) continue if dbmachine.host: msg = "Skipping host %s: The AQDB interface with MAC address " \ "%s is already tied to %s." % \ (host, mac, dbmachine.fqdn) failed.append(msg) logger.info(msg) continue dbhost = Host(machine=dbmachine, branch=dbdomain, status=dbstatus, owner_grn=dbpersonality.owner_grn, personality=dbpersonality, operating_system=dbos, comments="Created by refresh_windows_host") session.add(dbhost) if self.config.has_option("archetype_windows", "default_grn_target"): dbhost.grns.append((dbhost, dbgrn, self.config.get("archetype_", "default_grn_target"))) dbfqdn = Fqdn.get_or_create(session, name=short, dns_domain=dbdns_domain, preclude=True) dbdns_rec = ReservedName(fqdn=dbfqdn) session.add(dbdns_rec) dbmachine.primary_name = dbdns_rec success.append("Added host entry for %s (%s)." % (dbmachine.label, dbdns_rec.fqdn)) if dbmachine.vm_container: containers.add(dbmachine.vm_container) session.flush()
failed.append(msg) logger.info(msg) continue if dbmachine.host: msg = "Skipping host %s: The AQDB interface with MAC address " \ "%s is already tied to %s." % (host, mac, dbmachine.fqdn) failed.append(msg) logger.info(msg) continue dbhost = Host(hardware_entity=dbmachine, branch=dbdomain, status=dbstatus, personality=dbpersonality, operating_system=dbos, comments="Created by refresh_windows_host") session.add(dbhost) dbfqdn = Fqdn.get_or_create(session, name=short, dns_domain=dbdns_domain, preclude=True) dbdns_rec = ReservedName(fqdn=dbfqdn) session.add(dbdns_rec) dbmachine.primary_name = dbdns_rec success.append("Added host entry for %s (%s)." % (dbmachine.label, dbdns_rec.fqdn)) logger.info("Added {0:l} to machine {1.label}".format(dbhost, dbmachine)) if dbmachine.vm_container: containers.add(dbmachine.vm_container) session.flush() session.flush() if failed: raise PartialError(success, failed)
class CommandAddAuroraHost(CommandAddHost): required_parameters = ["hostname"] # Look for node name like <building><rack_id>c<chassis_id>n<node_num> nodename_re = re.compile(r'^\s*([a-zA-Z]+)(\d+)c(\d+)n(\d+)\s*$') def render(self, session, logger, hostname, osname, osversion, **kwargs): # Pull relevant info out of dsdb... dsdb_runner = DSDBRunner(logger=logger) try: fields = dsdb_runner.show_host(hostname) except ProcessException, e: raise ArgumentError("Could not find %s in DSDB: %s" % (hostname, e)) fqdn = fields["fqdn"] dsdb_lookup = fields["dsdb_lookup"] if fields["node"]: machine = fields["node"] elif fields["primary_name"]: machine = fields["primary_name"] else: machine = dsdb_lookup # Create a machine dbmodel = Model.get_unique(session, name="aurora_model", vendor="aurora_vendor", compel=True) dbmachine = Machine.get_unique(session, machine) dbslot = None if not dbmachine: m = self.nodename_re.search(machine) if m: (building, rid, cid, nodenum) = m.groups() dbbuilding = session.query(Building).filter_by( name=building).first() if not dbbuilding: raise ArgumentError("Failed to find building %s for " "node %s, please add an Aurora " "machine manually and follow with " "add_host." % (building, machine)) rack = building + rid dbrack = session.query(Rack).filter_by(name=rack).first() if not dbrack: try: rack_fields = dsdb_runner.show_rack(rack) dbrack = Rack(name=rack, fullname=rack, parent=dbbuilding, rack_row=rack_fields["rack_row"], rack_column=rack_fields["rack_col"]) session.add(dbrack) except (ProcessException, ValueError), e: logger.client_info("Rack %s not defined in DSDB." % rack) dblocation = dbrack or dbbuilding chassis = rack + "c" + cid dbdns_domain = session.query(DnsDomain).filter_by( name="ms.com").first() dbchassis = Chassis.get_unique(session, chassis) if not dbchassis: dbchassis_model = Model.get_unique(session, name="aurora_chassis_model", vendor="aurora_vendor", compel=True) dbchassis = Chassis(label=chassis, location=dblocation, model=dbchassis_model) session.add(dbchassis) dbfqdn = Fqdn.get_or_create(session, name=chassis, dns_domain=dbdns_domain, preclude=True) dbdns_rec = ReservedName(fqdn=dbfqdn) session.add(dbdns_rec) dbchassis.primary_name = dbdns_rec dbslot = session.query(ChassisSlot).filter_by( chassis=dbchassis, slot_number=nodenum).first() # Note: Could be even more persnickity here and check that # the slot is currently empty. Seems like overkill. if not dbslot: dbslot = ChassisSlot(chassis=dbchassis, slot_number=nodenum) session.add(dbslot) else: dbnet_env = NetworkEnvironment.get_unique_or_default(session) try: host_ip = gethostbyname(hostname) except gaierror, e: raise ArgumentError("Error when looking up host: %d, %s" % (e.errno, e.strerror)) dbnetwork = get_net_id_from_ip(session, IPv4Address(host_ip), dbnet_env) dblocation = dbnetwork.location.building
def grab_address(session, fqdn, ip, network_environment=None, dns_environment=None, comments=None, allow_restricted_domain=False, allow_multi=False, allow_reserved=False, relaxed=False, preclude=False): """ Take ownership of an address. This is a bit complicated because due to DNS propagation delays, we want to allow users to pre-define a DNS address and then assign the address to a host later. Parameters: session: SQLA session handle fqdn: the name to allocate/take over ip: the IP address to allocate/take over network_environment: where the IP address lives dns_enviromnent: where the FQDN lives comments: any comments to attach to the DNS record if it is created as new allow_restricted_domain: if True, adding entries to restricted DNS domains is allowed, otherwise it is denied. Default is False. allow_multi: if True, allow the same FQDN to be added multiple times with different IP addresses. Deault is False. allow_reserved: if True, allow creating a ReservedName instead of an ARecord if no IP address was specified. Default is False. preclude: if True, forbid taking over an existing DNS record, even if it is not referenced by any AddressAssignment records. Default is False. """ if not isinstance(network_environment, NetworkEnvironment): network_environment = NetworkEnvironment.get_unique_or_default( session, network_environment) if not dns_environment: dns_environment = network_environment.dns_environment elif not isinstance(dns_environment, DnsEnvironment): dns_environment = DnsEnvironment.get_unique(session, dns_environment, compel=True) # Non-default DNS environments may contain anything, but we want to keep # the internal environment clean if dns_environment.is_default and not network_environment.is_default: raise ArgumentError("Entering external IP addresses to the " "internal DNS environment is not allowed.") short, dbdns_domain = parse_fqdn(session, fqdn) # Lock the domain to prevent adding/deleting records while we're checking # FQDN etc. availability dbdns_domain.lock_row() if dbdns_domain.restricted and not allow_restricted_domain: raise ArgumentError("{0} is restricted, adding extra addresses " "is not allowed.".format(dbdns_domain)) dbfqdn = Fqdn.get_or_create(session, dns_environment=dns_environment, name=short, dns_domain=dbdns_domain, query_options=[joinedload('dns_records')]) existing_record = None newly_created = False if ip: dbnetwork = get_net_id_from_ip(session, ip, network_environment) check_ip_restrictions(dbnetwork, ip, relaxed=relaxed) dbnetwork.lock_row() # No filtering on DNS environment. If an address is dynamic in one # environment, it should not be considered static in a different # environment. q = session.query(DynamicStub) q = q.filter_by(network=dbnetwork) q = q.filter_by(ip=ip) dbdns_rec = q.first() _forbid_dyndns(dbdns_rec) # Verify that no other record uses the same IP address, this time taking # the DNS environemt into consideration. # While the DNS would allow different A records to point to the same IP # address, the current user expectation is that creating a DNS entry # also counts as a reservation, so we can not allow this use case. If we # want to implement such a feature later, the best way would be to # subclass Alias and let that subclass emit an A record instead of a # CNAME when the dump_dns command is called. q = session.query(ARecord) q = q.filter_by(network=dbnetwork) q = q.filter_by(ip=ip) q = q.join(ARecord.fqdn) q = q.filter_by(dns_environment=dns_environment) dbrecords = q.all() if dbrecords and len(dbrecords) > 1: # pragma: no cover # We're just trying to make sure this never happens raise AquilonError( "IP address %s is referenced by multiple " "DNS records: %s" % (ip, ", ".join([format(rec, "a") for rec in dbrecords]))) if dbrecords and dbrecords[0].fqdn != dbfqdn: raise ArgumentError( "IP address {0} is already in use by {1:l}.".format( ip, dbrecords[0])) # Check if the name is used already for dbdns_rec in dbfqdn.dns_records: if isinstance(dbdns_rec, ARecord): _forbid_dyndns(dbdns_rec) _check_netenv_compat(dbdns_rec, network_environment) if dbdns_rec.ip == ip and dbdns_rec.network == dbnetwork: existing_record = dbdns_rec elif not allow_multi: raise ArgumentError( "{0} points to a different IP address.".format( dbdns_rec)) elif isinstance(dbdns_rec, ReservedName): existing_record = convert_reserved_to_arecord( session, dbdns_rec, dbnetwork, ip) newly_created = True else: # Exclude aliases etc. raise ArgumentError( "{0} cannot be used for address assignment.".format( dbdns_rec)) if not existing_record: existing_record = ARecord(fqdn=dbfqdn, ip=ip, network=dbnetwork, comments=comments) session.add(existing_record) newly_created = True else: if not dbfqdn.dns_records: # There's no IP, and the name did not exist before. Create a # reservation, but only if the caller allowed that use case. if not allow_reserved: raise ArgumentError("DNS Record %s does not exist." % dbfqdn) existing_record = ReservedName(fqdn=dbfqdn, comments=comments) newly_created = True else: # There's no IP, but the name is already in use. We need a single IP # address. if len(dbfqdn.dns_records) > 1: raise ArgumentError( "{0} does not resolve to a single IP address.".format( dbfqdn)) existing_record = dbfqdn.dns_records[0] _forbid_dyndns(existing_record) if not isinstance(existing_record, ARecord): # Exclude aliases etc. raise ArgumentError( "{0} cannot be used for address assignment.".format( existing_record)) # Verify that the existing record is in the network environment the # caller expects _check_netenv_compat(existing_record, network_environment) ip = existing_record.ip dbnetwork = existing_record.network dbnetwork.lock_row() if existing_record.hardware_entity: raise ArgumentError( "{0} is already used as the primary name of {1:cl} " "{1.label}.".format(existing_record, existing_record.hardware_entity)) if preclude and not newly_created: raise ArgumentError("{0} already exists.".format(existing_record)) if ip: q = session.query(AddressAssignment) q = q.filter_by(network=dbnetwork) q = q.filter_by(ip=ip) addr = q.first() if addr: raise ArgumentError("IP address {0} is already in use by " "{1:l}.".format(ip, addr.interface)) return (existing_record, newly_created)
def render(self, session, fqdn, dns_environment, dns_domain, shortname, record_type, ip, network, network_environment, target, target_domain, primary_name, used, reverse_override, reverse_ptr, fullinfo, style, **kwargs): if record_type: record_type = record_type.strip().lower() if record_type in DNS_RRTYPE_MAP: cls = DNS_RRTYPE_MAP[record_type] else: cls = DnsRecord.polymorphic_subclass(record_type, "Unknown DNS record type") q = session.query(cls) else: q = session.query(DnsRecord) q = q.with_polymorphic('*') dbnet_env = NetworkEnvironment.get_unique_or_default(session, network_environment) if network_environment: # The network environment determines the DNS environment dbdns_env = dbnet_env.dns_environment else: dbdns_env = DnsEnvironment.get_unique_or_default(session, dns_environment) if fqdn: dbfqdn = Fqdn.get_unique(session, fqdn=fqdn, dns_environment=dbdns_env, compel=True) q = q.filter_by(fqdn=dbfqdn) q = q.join((Fqdn, DnsRecord.fqdn_id == Fqdn.id)) q = q.filter_by(dns_environment=dbdns_env) q = q.options(contains_eager('fqdn')) if dns_domain: dbdns_domain = DnsDomain.get_unique(session, dns_domain, compel=True) q = q.filter_by(dns_domain=dbdns_domain) if shortname: q = q.filter_by(name=shortname) q = q.join(DnsDomain) q = q.options(contains_eager('fqdn.dns_domain')) q = q.order_by(Fqdn.name, DnsDomain.name) q = q.reset_joinpoint() if ip: q = q.join(Network) q = q.filter_by(network_environment=dbnet_env) q = q.reset_joinpoint() q = q.filter(ARecord.ip == ip) if network: dbnetwork = Network.get_unique(session, network, network_environment=dbnet_env, compel=True) q = q.filter(ARecord.network == dbnetwork) if target: dbtarget = Fqdn.get_unique(session, fqdn=target, dns_environment=dbdns_env, compel=True) q = q.filter(or_(Alias.target == dbtarget, SrvRecord.target == dbtarget)) if target_domain: dbdns_domain = DnsDomain.get_unique(session, target_domain, compel=True) TargetFqdn = aliased(Fqdn) q = q.join((TargetFqdn, or_(Alias.target_id == TargetFqdn.id, SrvRecord.target_id == TargetFqdn.id))) q = q.filter(TargetFqdn.dns_domain == dbdns_domain) if primary_name is not None: if primary_name: q = q.filter(DnsRecord.hardware_entity.has()) else: q = q.filter(~DnsRecord.hardware_entity.has()) if used is not None: if used: q = q.join(AddressAssignment, and_(ARecord.network_id == AddressAssignment.network_id, ARecord.ip == AddressAssignment.ip)) else: q = q.outerjoin(AddressAssignment, and_(ARecord.network_id == AddressAssignment.network_id, ARecord.ip == AddressAssignment.ip)) q = q.filter(AddressAssignment.id == None) q = q.reset_joinpoint() if reverse_override is not None: if reverse_override: q = q.filter(ARecord.reverse_ptr.has()) else: q = q.filter(~ARecord.reverse_ptr.has()) if reverse_ptr: dbtarget = Fqdn.get_unique(session, fqdn=reverse_ptr, dns_environment=dbdns_env, compel=True) q = q.filter(ARecord.reverse_ptr == dbtarget) if fullinfo or style != "raw": q = q.options(undefer('comments'), subqueryload('hardware_entity'), lazyload('hardware_entity.primary_name'), undefer('alias_cnt')) return q.all() else: return StringAttributeList(q.all(), 'fqdn')
def render(self, session, logger, startip, endip, dns_domain, prefix, **arguments): if not prefix: prefix = 'dynamic' dbnet_env = NetworkEnvironment.get_unique_or_default(session) dbdns_env = DnsEnvironment.get_unique_or_default(session) startnet = get_net_id_from_ip(session, startip, dbnet_env) endnet = get_net_id_from_ip(session, endip, dbnet_env) if startnet != endnet: raise ArgumentError("IP addresses %s (%s) and %s (%s) must be on " "the same subnet." % (startip, startnet.ip, endip, endnet.ip)) dbdns_domain = DnsDomain.get_unique(session, dns_domain, compel=True) dbdns_domain.lock_row() startnet.lock_row() q = session.query(AddressAssignment.ip) q = q.filter_by(network=startnet) q = q.filter(AddressAssignment.ip >= startip) q = q.filter(AddressAssignment.ip <= endip) q = q.order_by(AddressAssignment.ip) conflicts = q.all() if conflicts: raise ArgumentError("Cannot allocate the address range because the " "following IP addresses are already in use:\n" + ", ".join([str(c.ip) for c in conflicts])) # No filtering on DNS environment. If an address is dynamic in one # environment, it should not be considered static in a different # environment. q = session.query(ARecord) q = q.filter_by(network=startnet) q = q.filter(ARecord.ip >= startip) q = q.filter(ARecord.ip <= endip) q = q.order_by(ARecord.ip) conflicts = q.all() if conflicts: raise ArgumentError("Cannot allocate the address range because the " "following DNS records already exist:\n" + "\n".join([format(c, "a") for c in conflicts])) dsdb_runner = DSDBRunner(logger=logger) with session.no_autoflush: for ipint in range(int(startip), int(endip) + 1): ip = IPv4Address(ipint) check_ip_restrictions(startnet, ip) name = "%s-%s" % (prefix, str(ip).replace('.', '-')) dbfqdn = Fqdn.get_or_create(session, name=name, dns_domain=dbdns_domain, dns_environment=dbdns_env, preclude=True) dbdynamic_stub = DynamicStub(fqdn=dbfqdn, ip=ip, network=startnet) session.add(dbdynamic_stub) dsdb_runner.add_host_details(dbfqdn, ip) session.flush() # This may take some time if the range is big, so be verbose dsdb_runner.commit_or_rollback("Could not add addresses to DSDB", verbose=True) return
def render(self, session, fqdn, dns_environment, dns_domain, shortname, record_type, ip, network, network_environment, target, target_domain, primary_name, used, reverse_override, reverse_ptr, fullinfo, style, **kwargs): if record_type: record_type = record_type.strip().lower() if record_type in DNS_RRTYPE_MAP: cls = DNS_RRTYPE_MAP[record_type] else: cls = DnsRecord.polymorphic_subclass( record_type, "Unknown DNS record type") q = session.query(cls) else: q = session.query(DnsRecord) q = q.with_polymorphic('*') dbnet_env = NetworkEnvironment.get_unique_or_default( session, network_environment) if network_environment: # The network environment determines the DNS environment dbdns_env = dbnet_env.dns_environment else: dbdns_env = DnsEnvironment.get_unique_or_default( session, dns_environment) if fqdn: dbfqdn = Fqdn.get_unique(session, fqdn=fqdn, dns_environment=dbdns_env, compel=True) q = q.filter_by(fqdn=dbfqdn) q = q.join((Fqdn, DnsRecord.fqdn_id == Fqdn.id)) q = q.filter_by(dns_environment=dbdns_env) q = q.options(contains_eager('fqdn')) if dns_domain: dbdns_domain = DnsDomain.get_unique(session, dns_domain, compel=True) q = q.filter_by(dns_domain=dbdns_domain) if shortname: q = q.filter_by(name=shortname) q = q.join(DnsDomain) q = q.options(contains_eager('fqdn.dns_domain')) q = q.order_by(Fqdn.name, DnsDomain.name) q = q.reset_joinpoint() if ip: q = q.join(Network) q = q.filter_by(network_environment=dbnet_env) q = q.reset_joinpoint() q = q.filter(ARecord.ip == ip) if network: dbnetwork = Network.get_unique(session, network, network_environment=dbnet_env, compel=True) q = q.filter(ARecord.network == dbnetwork) if target: dbtarget = Fqdn.get_unique(session, fqdn=target, dns_environment=dbdns_env, compel=True) q = q.filter( or_(Alias.target == dbtarget, SrvRecord.target == dbtarget)) if target_domain: dbdns_domain = DnsDomain.get_unique(session, target_domain, compel=True) TargetFqdn = aliased(Fqdn) q = q.join((TargetFqdn, or_(Alias.target_id == TargetFqdn.id, SrvRecord.target_id == TargetFqdn.id))) q = q.filter(TargetFqdn.dns_domain == dbdns_domain) if primary_name is not None: if primary_name: q = q.filter(DnsRecord.hardware_entity.has()) else: q = q.filter(~DnsRecord.hardware_entity.has()) if used is not None: if used: q = q.join( AddressAssignment, and_(ARecord.network_id == AddressAssignment.network_id, ARecord.ip == AddressAssignment.ip)) else: q = q.outerjoin( AddressAssignment, and_(ARecord.network_id == AddressAssignment.network_id, ARecord.ip == AddressAssignment.ip)) q = q.filter(AddressAssignment.id == None) q = q.reset_joinpoint() if reverse_override is not None: if reverse_override: q = q.filter(ARecord.reverse_ptr.has()) else: q = q.filter(~ARecord.reverse_ptr.has()) if reverse_ptr: dbtarget = Fqdn.get_unique(session, fqdn=reverse_ptr, dns_environment=dbdns_env, compel=True) q = q.filter(ARecord.reverse_ptr == dbtarget) if fullinfo: q = q.options(undefer('comments')) q = q.options(subqueryload('hardware_entity')) q = q.options(undefer('alias_cnt')) return q.all() elif style == "raw": return StringAttributeList(q.all(), 'fqdn') else: # This is for the CSV formatter return q.all()