def render(self, session, logger, switch, **arguments): dbswitch = Switch.get_unique(session, switch, compel=True) # Check and complain if the switch has any other addresses than its # primary address addrs = [] for addr in dbswitch.all_addresses(): if addr.ip == dbswitch.primary_ip: continue addrs.append(str(addr.ip)) if addrs: raise ArgumentError("{0} still provides the following addresses, " "delete them first: {1}.".format (dbswitch, ", ".join(addrs))) dbdns_rec = dbswitch.primary_name ip = dbswitch.primary_ip old_fqdn = str(dbswitch.primary_name.fqdn) old_comments = dbswitch.comments session.delete(dbswitch) if dbdns_rec: delete_dns_record(dbdns_rec) session.flush() # Any switch ports hanging off this switch should be deleted with # the cascade delete of the switch. switch_plenary = Plenary.get_plenary(dbswitch, logger=logger) # clusters connected to this switch plenaries = PlenaryCollection(logger=logger) for dbcluster in dbswitch.esx_clusters: plenaries.append(Plenary.get_plenary(dbcluster)) key = CompileKey.merge([switch_plenary.get_remove_key(), plenaries.get_write_key()]) try: lock_queue.acquire(key) switch_plenary.stash() plenaries.write(locked=True) switch_plenary.remove(locked=True) if ip: dsdb_runner = DSDBRunner(logger=logger) # FIXME: restore interface name/MAC on rollback dsdb_runner.delete_host_details(old_fqdn, ip, comments=old_comments) dsdb_runner.commit_or_rollback("Could not remove switch from DSDB") return except: plenaries.restore_stash() switch_plenary.restore_stash() raise finally: lock_queue.release(key)
def del_dynamic_stubs(self, session, logger, dbstubs): dsdb_runner = DSDBRunner(logger=logger) for stub in dbstubs: dsdb_runner.delete_host_details(str(stub.fqdn), stub.ip) delete_dns_record(stub) session.flush() # This may take some time if the range is big, so be verbose dsdb_runner.commit_or_rollback(verbose=True)
def del_dynamic_stubs(self, session, logger, dbstubs): dsdb_runner = DSDBRunner(logger=logger) for stub in dbstubs: dsdb_runner.delete_host_details(str(stub.fqdn), stub.ip) delete_dns_record(stub) session.flush() # This may take some time if the range is big, so be verbose dsdb_runner.commit_or_rollback(verbose=True)
def render(self, session, logger, network_device, **arguments): dbnetdev = NetworkDevice.get_unique(session, network_device, compel=True) # Check and complain if the network device has any other addresses than its # primary address addrs = [] for addr in dbnetdev.all_addresses(): if addr.ip == dbnetdev.primary_ip: continue addrs.append(str(addr.ip)) if addrs: raise ArgumentError("{0} still provides the following addresses, " "delete them first: {1}.".format (dbnetdev, ", ".join(addrs))) dbdns_rec = dbnetdev.primary_name ip = dbnetdev.primary_ip old_fqdn = str(dbnetdev.primary_name.fqdn) old_comments = dbnetdev.comments session.delete(dbnetdev) if dbdns_rec: delete_dns_record(dbdns_rec) session.flush() # Any network device ports hanging off this network device should be deleted with # the cascade delete of the network device. netdev_plenary = Plenary.get_plenary(dbnetdev, logger=logger) # clusters connected to this network device plenaries = PlenaryCollection(logger=logger) for dbcluster in dbnetdev.esx_clusters: plenaries.append(Plenary.get_plenary(dbcluster)) with CompileKey.merge([netdev_plenary.get_key(), plenaries.get_key()]): netdev_plenary.stash() try: plenaries.write(locked=True) netdev_plenary.remove(locked=True) if ip: dsdb_runner = DSDBRunner(logger=logger) # FIXME: restore interface name/MAC on rollback dsdb_runner.delete_host_details(old_fqdn, ip, comments=old_comments) dsdb_runner.commit_or_rollback("Could not remove network device " "from DSDB") except: plenaries.restore_stash() netdev_plenary.restore_stash() raise return
def add_srv_dsdb_callback(session, logger, dbsrv, real_holder=None, oldinfo=None, newly_created=None, comments=None): dsdb_runner = DSDBRunner(logger=logger) if not newly_created: dsdb_runner.delete_host_details(dbsrv.dns_record.fqdn, dbsrv.dns_record.ip) if isinstance(real_holder, Host): dsdb_runner.update_host(real_holder.machine, oldinfo) else: dsdb_runner.add_host_details(dbsrv.dns_record.fqdn, dbsrv.dns_record.ip, comments=comments) dsdb_runner.commit_or_rollback("Could not add host to DSDB")
def del_srv_dsdb_callback(session, logger, holder, dbsrv_addr, oldinfo, keep_dns): dsdb_runner = DSDBRunner(logger=logger) toplevel_holder = holder.toplevel_holder_object if isinstance(toplevel_holder, Host): dsdb_runner.update_host(toplevel_holder.hardware_entity, oldinfo) if keep_dns: dsdb_runner.add_host_details( dbsrv_addr.dns_record.fqdn, dbsrv_addr.dns_record.ip, comments=dbsrv_addr.dns_record.comments ) elif not keep_dns: dsdb_runner.delete_host_details(str(dbsrv_addr.dns_record.fqdn), dbsrv_addr.dns_record.ip) dsdb_runner.commit_or_rollback("Could not delete host from DSDB")
def del_srv_dsdb_callback(session, logger, holder, dbsrv_addr, oldinfo, keep_dns): real_holder = holder.holder_object if isinstance(real_holder, ResourceGroup): real_holder = real_holder.holder.holder_object dsdb_runner = DSDBRunner(logger=logger) if isinstance(real_holder, Host): dsdb_runner.update_host(real_holder.machine, oldinfo) if keep_dns: dsdb_runner.add_host_details(dbsrv_addr.dns_record.fqdn, dbsrv_addr.dns_record.ip, comments=dbsrv_addr.dns_record.comments) elif not keep_dns: dsdb_runner.delete_host_details(str(dbsrv_addr.dns_record.fqdn), dbsrv_addr.dns_record.ip) dsdb_runner.commit_or_rollback("Could not delete host from DSDB")
def del_srv_dsdb_callback(session, logger, holder, dbsrv_addr, oldinfo, keep_dns): real_holder = holder.holder_object if isinstance(real_holder, ResourceGroup): real_holder = real_holder.holder.holder_object dsdb_runner = DSDBRunner(logger=logger) if isinstance(real_holder, Host): dsdb_runner.update_host(real_holder.machine, oldinfo) if keep_dns: dsdb_runner.add_host_details( dbsrv_addr.dns_record.fqdn, dbsrv_addr.dns_record.ip, comments=dbsrv_addr.dns_record.comments) elif not keep_dns: dsdb_runner.delete_host_details(str(dbsrv_addr.dns_record.fqdn), dbsrv_addr.dns_record.ip) dsdb_runner.commit_or_rollback("Could not delete host from DSDB")
def render(self, session, logger, chassis, clear_slots, **arguments): dbchassis = Chassis.get_unique(session, chassis, compel=True) # Check and complain if the chassis has any other addresses than its # primary address addrs = [] for addr in dbchassis.all_addresses(): if addr.ip == dbchassis.primary_ip: continue addrs.append(str(addr.ip)) if addrs: raise ArgumentError("{0} still provides the following addresses, " "delete them first: {1}.".format (dbchassis, ", ".join(addrs))) q = session.query(ChassisSlot) q = q.filter_by(chassis=dbchassis) q = q.filter(ChassisSlot.machine_id != None) machine_count = q.count() if machine_count > 0 and not clear_slots: raise ArgumentError("{0} is still in use by {1} machines. Use " "--clear_slots if you really want to delete " "it.".format(dbchassis, machine_count)) # Order matters here dbdns_rec = dbchassis.primary_name ip = dbchassis.primary_ip old_fqdn = str(dbchassis.primary_name.fqdn) old_comments = dbchassis.primary_name.comments session.delete(dbchassis) if dbdns_rec: delete_dns_record(dbdns_rec) session.flush() if ip: dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.delete_host_details(old_fqdn, ip, comments=old_comments) dsdb_runner.commit_or_rollback("Could not remove chassis from DSDB") return
def add_srv_dsdb_callback(session, logger, dbsrv, real_holder=None, oldinfo=None, newly_created=None, comments=None): dsdb_runner = DSDBRunner(logger=logger) if not newly_created: dsdb_runner.delete_host_details(dbsrv.dns_record.fqdn, dbsrv.dns_record.ip) if isinstance(real_holder, Host): dsdb_runner.update_host(real_holder.machine, oldinfo) else: dsdb_runner.add_host_details(dbsrv.dns_record.fqdn, dbsrv.dns_record.ip, comments=comments) dsdb_runner.commit_or_rollback("Could not add host to DSDB")
def render(self, session, logger, fqdn, ip, dns_environment, network_environment, **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(session, dns_environment, compel=True) with DeleteKey("system", logger=logger): # We can't use get_unique() here, since we always want to filter by # DNS environment, even if no FQDN was given q = session.query(ARecord) if ip: q = q.filter_by(ip=ip) q = q.join(ARecord.fqdn) q = q.options(contains_eager('fqdn')) q = q.filter_by(dns_environment=dbdns_env) if fqdn: (name, dbdns_domain) = parse_fqdn(session, fqdn) q = q.filter_by(name=name) q = q.filter_by(dns_domain=dbdns_domain) try: dbaddress = q.one() except NoResultFound: parts = [] if fqdn: parts.append(fqdn) if ip: parts.append("ip %s" % ip) raise NotFoundException("DNS Record %s not found." % ", ".join(parts)) except MultipleResultsFound: parts = [] if fqdn: parts.append(fqdn) if ip: parts.append("ip %s" % ip) raise NotFoundException("DNS Record %s is not unique." % ", ".join(parts)) if dbaddress.hardware_entity: raise ArgumentError("DNS Record {0:a} is the primary name of " "{1:l}, therefore it cannot be " "deleted.".format(dbaddress, dbaddress.hardware_entity)) if dbaddress.service_address: # TODO: print the holder object raise ArgumentError("DNS Record {0:a} is used as a service " "address, therefore it cannot be deleted." .format(dbaddress)) # Do not allow deleting the DNS record if the IP address is still in # use - except if there are other DNS records having the same # address if dbaddress.assignments: last_use = [] # FIXME: race condition here, we should use # SELECT ... FOR UPDATE for addr in dbaddress.assignments: if len(addr.dns_records) == 1: last_use.append(addr) if last_use: users = " ,".join([format(addr.interface, "l") for addr in last_use]) raise ArgumentError("IP address %s is still in use by %s." % (ip, users)) ip = dbaddress.ip old_fqdn = str(dbaddress.fqdn) old_comments = dbaddress.comments delete_dns_record(dbaddress) session.flush() if dbdns_env.is_default: dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.delete_host_details(old_fqdn, ip, comments=old_comments) dsdb_runner.commit_or_rollback() return
def render(self, session, logger, interface, machine, mac, automac, model, vendor, pg, autopg, type, comments, **arguments): dbmachine = Machine.get_unique(session, machine, compel=True) oldinfo = DSDBRunner.snapshot_hw(dbmachine) audit_results = [] q = session.query(Interface) q = q.filter_by(name=interface, hardware_entity=dbmachine) if q.first(): raise ArgumentError( "Machine %s already has an interface named %s." % (machine, interface)) if not type: type = 'public' management_types = ['bmc', 'ilo', 'ipmi'] for mtype in management_types: if interface.startswith(mtype): type = 'management' break if interface.startswith("bond"): type = 'bonding' elif interface.startswith("br"): type = 'bridge' # Test it last, VLANs can be added on top of almost anything if '.' in interface: type = 'vlan' if type == "oa" or type == "loopback": raise ArgumentError("Interface type '%s' is not valid for " "machines." % type) bootable = None if type == 'public': if interface == 'eth0': bootable = True else: bootable = False dbmanager = None pending_removals = PlenaryCollection() dsdb_runner = DSDBRunner(logger=logger) if mac: prev = session.query(Interface).filter_by(mac=mac).first() if prev and prev.hardware_entity == dbmachine: raise ArgumentError("{0} already has an interface with MAC " "address {1}.".format(dbmachine, mac)) # Is the conflicting interface something that can be # removed? It is if: # - we are currently attempting to add a management interface # - the old interface belongs to a machine # - the old interface is associated with a host # - that host was blindly created, and thus can be removed safely if prev and type == 'management' and \ prev.hardware_entity.hardware_type == 'machine' and \ prev.hardware_entity.host and \ prev.hardware_entity.host.status.name == 'blind': # FIXME: Is this just always allowed? Maybe restrict # to only aqd-admin and the host itself? dummy_machine = prev.hardware_entity dummy_ip = dummy_machine.primary_ip old_fqdn = str(dummy_machine.primary_name) old_iface = prev.name old_mac = prev.mac old_network = get_net_id_from_ip(session, dummy_ip) self.remove_prev(session, logger, prev, pending_removals) session.flush() dsdb_runner.delete_host_details(old_fqdn, dummy_ip, old_iface, old_mac) self.consolidate_names(session, logger, dbmachine, dummy_machine.label, pending_removals) # It seems like a shame to throw away the IP address that # had been allocated for the blind host. Try to use it # as it should be used... dbmanager = self.add_manager(session, logger, dbmachine, dummy_ip, old_network) elif prev: msg = describe_interface(session, prev) raise ArgumentError("MAC address %s is already in use: %s." % (mac, msg)) elif automac: mac = self.generate_mac(session, dbmachine) audit_results.append(('mac', mac)) else: #Ignore now that Mac Address can be null pass if pg is not None: port_group = verify_port_group(dbmachine, pg) elif autopg: port_group = choose_port_group(session, logger, dbmachine) audit_results.append(('pg', port_group)) else: port_group = None dbinterface = get_or_create_interface(session, dbmachine, name=interface, vendor=vendor, model=model, interface_type=type, mac=mac, bootable=bootable, port_group=port_group, comments=comments, preclude=True) # So far, we're *only* creating a manager if we happen to be # removing a blind entry and we can steal its IP address. if dbmanager: assign_address(dbinterface, dbmanager.ip, dbmanager.network) session.add(dbinterface) session.flush() plenaries = PlenaryCollection(logger=logger) plenaries.append(Plenary.get_plenary(dbmachine)) if pending_removals and dbmachine.host: # Not an exact test, but the file won't be re-written # if the contents are the same so calling too often is # not a major expense. plenaries.append(Plenary.get_plenary(dbmachine.host)) # Even though there may be removals going on the write key # should be sufficient here. key = plenaries.get_write_key() try: lock_queue.acquire(key) pending_removals.stash() plenaries.write(locked=True) pending_removals.remove(locked=True) dsdb_runner.update_host(dbmachine, oldinfo) dsdb_runner.commit_or_rollback("Could not update host in DSDB") except: plenaries.restore_stash() pending_removals.restore_stash() raise finally: lock_queue.release(key) if dbmachine.host: # FIXME: reconfigure host pass for name, value in audit_results: self.audit_result(session, name, value, **arguments) return
def render( self, session, logger, interface, machine, mac, automac, model, vendor, pg, autopg, iftype, type, comments, **arguments ): dbmachine = Machine.get_unique(session, machine, compel=True) oldinfo = DSDBRunner.snapshot_hw(dbmachine) audit_results = [] if type: self.deprecated_option("type", "Please use --iftype" "instead.", logger=logger, **arguments) if not iftype: iftype = type if not iftype: iftype = "public" management_types = ["bmc", "ilo", "ipmi"] for mtype in management_types: if interface.startswith(mtype): iftype = "management" break if interface.startswith("bond"): iftype = "bonding" elif interface.startswith("br"): iftype = "bridge" # Test it last, VLANs can be added on top of almost anything if "." in interface: iftype = "vlan" if iftype == "oa" or iftype == "loopback": raise ArgumentError("Interface type '%s' is not valid for " "machines." % iftype) bootable = None if iftype == "public": if interface == "eth0": bootable = True else: bootable = False dbmanager = None pending_removals = PlenaryCollection() dsdb_runner = DSDBRunner(logger=logger) if mac: prev = session.query(Interface).filter_by(mac=mac).first() if prev and prev.hardware_entity == dbmachine: raise ArgumentError("{0} already has an interface with MAC " "address {1}.".format(dbmachine, mac)) # Is the conflicting interface something that can be # removed? It is if: # - we are currently attempting to add a management interface # - the old interface belongs to a machine # - the old interface is associated with a host # - that host was blindly created, and thus can be removed safely if ( prev and iftype == "management" and prev.hardware_entity.hardware_type == "machine" and prev.hardware_entity.host and prev.hardware_entity.host.status.name == "blind" ): # FIXME: Is this just always allowed? Maybe restrict # to only aqd-admin and the host itself? dummy_machine = prev.hardware_entity dummy_ip = dummy_machine.primary_ip old_fqdn = str(dummy_machine.primary_name) old_iface = prev.name old_mac = prev.mac old_network = get_net_id_from_ip(session, dummy_ip) self.remove_prev(session, logger, prev, pending_removals) session.flush() dsdb_runner.delete_host_details(old_fqdn, dummy_ip, old_iface, old_mac) self.consolidate_names(session, logger, dbmachine, dummy_machine.label, pending_removals) # It seems like a shame to throw away the IP address that # had been allocated for the blind host. Try to use it # as it should be used... dbmanager = self.add_manager(session, logger, dbmachine, dummy_ip, old_network) elif prev: msg = describe_interface(session, prev) raise ArgumentError("MAC address %s is already in use: %s." % (mac, msg)) elif automac: mac = self.generate_mac(session, dbmachine) audit_results.append(("mac", mac)) else: # Ignore now that Mac Address can be null pass if pg is not None: port_group = verify_port_group(dbmachine, pg) elif autopg: port_group = choose_port_group(session, logger, dbmachine) audit_results.append(("pg", port_group)) else: port_group = None dbinterface = get_or_create_interface( session, dbmachine, name=interface, vendor=vendor, model=model, interface_type=iftype, mac=mac, bootable=bootable, port_group=port_group, comments=comments, preclude=True, ) # So far, we're *only* creating a manager if we happen to be # removing a blind entry and we can steal its IP address. if dbmanager: assign_address(dbinterface, dbmanager.ip, dbmanager.network, logger=logger) session.add(dbinterface) session.flush() plenaries = PlenaryCollection(logger=logger) plenaries.append(Plenary.get_plenary(dbmachine)) if pending_removals and dbmachine.host: # Not an exact test, but the file won't be re-written # if the contents are the same so calling too often is # not a major expense. plenaries.append(Plenary.get_plenary(dbmachine.host)) # Even though there may be removals going on the write key # should be sufficient here. with plenaries.get_key(): pending_removals.stash() try: plenaries.write(locked=True) pending_removals.remove(locked=True) dsdb_runner.update_host(dbmachine, oldinfo) dsdb_runner.commit_or_rollback("Could not update host in DSDB") except: plenaries.restore_stash() pending_removals.restore_stash() raise if dbmachine.host: # FIXME: reconfigure host pass for name, value in audit_results: self.audit_result(session, name, value, **arguments) return
def render(self, session, logger, machine, chassis, switch, fqdn, interface, label, network_environment, map_to_primary, **kwargs): if machine: hwtype = 'machine' hwname = machine elif chassis: hwtype = 'chassis' hwname = chassis elif switch: hwtype = 'switch' hwname = switch dbnet_env = NetworkEnvironment.get_unique_or_default( session, network_environment) dbhw_ent = HardwareEntity.get_unique(session, hwname, hardware_type=hwtype, compel=True) dbinterface = Interface.get_unique(session, hardware_entity=dbhw_ent, name=interface, compel=True) oldinfo = DSDBRunner.snapshot_hw(dbhw_ent) audit_results = [] ip = generate_ip(session, logger, dbinterface, network_environment=dbnet_env, audit_results=audit_results, **kwargs) if dbinterface.interface_type == "loopback": # Switch loopback interfaces may use e.g. the network address as an # IP address relaxed = True else: relaxed = False if not fqdn: if not dbhw_ent.primary_name: raise ArgumentError("{0} has no primary name, can not " "auto-generate the DNS record. " "Please specify --fqdn.".format(dbhw_ent)) if label: name = "%s-%s-%s" % (dbhw_ent.primary_name.fqdn.name, interface, label) else: name = "%s-%s" % (dbhw_ent.primary_name.fqdn.name, interface) fqdn = "%s.%s" % (name, dbhw_ent.primary_name.fqdn.dns_domain) if label is None: label = "" elif label == "hostname": # When add_host sets up Zebra, it always uses the label 'hostname'. # Due to the primary IP being special, add_interface_address cannot # really emulate what add_host does, so tell the user where to look. raise ArgumentError("The 'hostname' label can only be managed " "by add_host/del_host.") # The label will be used as an nlist key if label: validate_basic("label", label) # TODO: add allow_multi=True dbdns_rec, newly_created = grab_address(session, fqdn, ip, dbnet_env, relaxed=relaxed) ip = dbdns_rec.ip dbnetwork = dbdns_rec.network delete_old_dsdb_entry = not newly_created and not dbdns_rec.assignments # Reverse PTR control. Auxiliary addresses should point to the primary # name by default, with some exceptions. if (map_to_primary is None and dbhw_ent.primary_name and dbinterface.interface_type != "management" and dbdns_rec.fqdn.dns_environment == dbhw_ent.primary_name.fqdn.dns_environment): map_to_primary = True if map_to_primary: if not dbhw_ent.primary_name: raise ArgumentError( "{0} does not have a primary name, cannot " "set the reverse DNS mapping.".format(dbhw_ent)) if (dbhw_ent.primary_name.fqdn.dns_environment != dbdns_rec.fqdn.dns_environment): raise ArgumentError("{0} lives in {1:l}, not {2:l}.".format( dbhw_ent, dbhw_ent.primary_name.fqdn.dns_environment, dbdns_rec.fqdn.dns_environment)) if dbinterface.interface_type == "management": raise ArgumentError("The reverse PTR for management addresses " "should not point to the primary name.") dbdns_rec.reverse_ptr = dbhw_ent.primary_name.fqdn # Check that the network ranges assigned to different interfaces # do not overlap even if the network environments are different, because # that would confuse routing on the host. E.g. if eth0 is an internal # and eth1 is an external interface, then using 192.168.1.10/24 on eth0 # and using 192.168.1.20/26 on eth1 won't work. for addr in dbhw_ent.all_addresses(): if addr.network != dbnetwork and \ addr.network.network.overlaps(dbnetwork.network): raise ArgumentError("{0} in {1:l} used on {2:l} overlaps " "requested {3:l} in " "{4:l}.".format( addr.network, addr.network.network_environment, addr.interface, dbnetwork, dbnetwork.network_environment)) assign_address(dbinterface, ip, dbnetwork, label=label) session.flush() dbhost = getattr(dbhw_ent, "host", None) if dbhost: plenary_info = PlenaryHost(dbhost, logger=logger) key = plenary_info.get_write_key() try: lock_queue.acquire(key) try: plenary_info.write(locked=True) except IncompleteError: # FIXME: if this command is used after "add host" but before # "make", then writing out the template will fail due to # required services not being assigned. Ignore this error # for now. plenary_info.restore_stash() dsdb_runner = DSDBRunner(logger=logger) if delete_old_dsdb_entry: dsdb_runner.delete_host_details(dbdns_rec.fqdn, ip) dsdb_runner.update_host(dbhw_ent, oldinfo) dsdb_runner.commit_or_rollback("Could not add host to DSDB") except: plenary_info.restore_stash() raise finally: lock_queue.release(key) else: dsdb_runner = DSDBRunner(logger=logger) if delete_old_dsdb_entry: dsdb_runner.delete_host_details(dbdns_rec.fqdn, ip) dsdb_runner.update_host(dbhw_ent, oldinfo) dsdb_runner.commit_or_rollback("Could not add host to DSDB") for name, value in audit_results: self.audit_result(session, name, value, **kwargs) return
def render(self, session, logger, machine, chassis, network_device, fqdn, interface, label, network_environment, map_to_primary, **kwargs): if machine: hwtype = 'machine' hwname = machine elif chassis: hwtype = 'chassis' hwname = chassis elif network_device: hwtype = 'network_device' hwname = network_device dbnet_env = NetworkEnvironment.get_unique_or_default(session, network_environment) dbhw_ent = HardwareEntity.get_unique(session, hwname, hardware_type=hwtype, compel=True) dbinterface = Interface.get_unique(session, hardware_entity=dbhw_ent, name=interface, compel=True) oldinfo = DSDBRunner.snapshot_hw(dbhw_ent) audit_results = [] ip = generate_ip(session, logger, dbinterface, network_environment=dbnet_env, audit_results=audit_results, **kwargs) if dbinterface.interface_type == "loopback": # Switch loopback interfaces may use e.g. the network address as an # IP address relaxed = True else: relaxed = False if not fqdn: if not dbhw_ent.primary_name: raise ArgumentError("{0} has no primary name, can not " "auto-generate the DNS record. " "Please specify --fqdn.".format(dbhw_ent)) if label: name = "%s-%s-%s" % (dbhw_ent.primary_name.fqdn.name, interface, label) else: name = "%s-%s" % (dbhw_ent.primary_name.fqdn.name, interface) fqdn = "%s.%s" % (name, dbhw_ent.primary_name.fqdn.dns_domain) if label is None: label = "" elif label == "hostname": # When add_host sets up Zebra, it always uses the label 'hostname'. # Due to the primary IP being special, add_interface_address cannot # really emulate what add_host does, so tell the user where to look. raise ArgumentError("The 'hostname' label can only be managed " "by add_host/del_host.") # The label will be used as an nlist key if label: validate_nlist_key("label", label) # TODO: add allow_multi=True dbdns_rec, newly_created = grab_address(session, fqdn, ip, dbnet_env, relaxed=relaxed) ip = dbdns_rec.ip dbnetwork = dbdns_rec.network delete_old_dsdb_entry = not newly_created and not dbdns_rec.assignments # Reverse PTR control. Auxiliary addresses should point to the primary # name by default, with some exceptions. if (map_to_primary is None and dbhw_ent.primary_name and dbinterface.interface_type != "management" and dbdns_rec.fqdn.dns_environment == dbhw_ent.primary_name.fqdn.dns_environment): map_to_primary = True if map_to_primary: if not dbhw_ent.primary_name: raise ArgumentError("{0} does not have a primary name, cannot " "set the reverse DNS mapping." .format(dbhw_ent)) if (dbhw_ent.primary_name.fqdn.dns_environment != dbdns_rec.fqdn.dns_environment): raise ArgumentError("{0} lives in {1:l}, not {2:l}." .format(dbhw_ent, dbhw_ent.primary_name.fqdn.dns_environment, dbdns_rec.fqdn.dns_environment)) if dbinterface.interface_type == "management": raise ArgumentError("The reverse PTR for management addresses " "should not point to the primary name.") dbdns_rec.reverse_ptr = dbhw_ent.primary_name.fqdn # Check that the network ranges assigned to different interfaces # do not overlap even if the network environments are different, because # that would confuse routing on the host. E.g. if eth0 is an internal # and eth1 is an external interface, then using 192.168.1.10/24 on eth0 # and using 192.168.1.20/26 on eth1 won't work. for addr in dbhw_ent.all_addresses(): if addr.network != dbnetwork and \ addr.network.network.overlaps(dbnetwork.network): raise ArgumentError("{0} in {1:l} used on {2:l} overlaps " "requested {3:l} in " "{4:l}.".format(addr.network, addr.network.network_environment, addr.interface, dbnetwork, dbnetwork.network_environment)) assign_address(dbinterface, ip, dbnetwork, label=label, logger=logger) session.flush() dbhost = getattr(dbhw_ent, "host", None) if dbhost: plenary_info = Plenary.get_plenary(dbhost, logger=logger) with plenary_info.get_key(): try: try: plenary_info.write(locked=True) except IncompleteError: # FIXME: if this command is used after "add host" but # before "make", then writing out the template will fail # due to required services not being assigned. Ignore # this error for now. plenary_info.restore_stash() dsdb_runner = DSDBRunner(logger=logger) if delete_old_dsdb_entry: dsdb_runner.delete_host_details(dbdns_rec.fqdn, ip) dsdb_runner.update_host(dbhw_ent, oldinfo) dsdb_runner.commit_or_rollback("Could not add host to DSDB") except: plenary_info.restore_stash() raise else: dsdb_runner = DSDBRunner(logger=logger) if delete_old_dsdb_entry: dsdb_runner.delete_host_details(dbdns_rec.fqdn, ip) dsdb_runner.update_host(dbhw_ent, oldinfo) dsdb_runner.commit_or_rollback("Could not add host to DSDB") for name, value in audit_results: self.audit_result(session, name, value, **kwargs) return