Beispiel #1
0
    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)
Beispiel #2
0
    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
Beispiel #3
0
    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)
Beispiel #4
0
    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
Beispiel #5
0
 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
Beispiel #6
0
 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
Beispiel #7
0
    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)
Beispiel #8
0
    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)
Beispiel #9
0
    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
Beispiel #10
0
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)
Beispiel #11
0
    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()
Beispiel #12
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(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)
Beispiel #13
0
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)
Beispiel #14
0
    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
Beispiel #15
0
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