def render(self, session, logger, auxiliary, **arguments): # Check dependencies, translate into user-friendly message dbauxiliary = ARecord.get_unique(session, fqdn=auxiliary, compel=True) is_aux = True if not dbauxiliary.assignments or len(dbauxiliary.assignments) > 1: is_aux = False else: assignment = dbauxiliary.assignments[0] dbinterface = assignment.interface if assignment.ip == dbinterface.hardware_entity.primary_ip: is_aux = False if assignment.interface.interface_type == 'management': is_aux = False if not is_aux: raise ArgumentError("{0:a} is not an auxiliary.".format(dbauxiliary)) return super(CommandDelAuxiliary, self).render(session, logger, machine=dbinterface.hardware_entity.label, chassis=None, network_device=None, interface=dbinterface.name, fqdn=auxiliary, ip=assignment.ip, label=None, keep_dns=False, network_environment=None, **arguments)
def render(self, session, dbuser, ip, fqdn, network_environment, **arguments): dbnet_env = NetworkEnvironment.get_unique_or_default(session, network_environment) self.az.check_network_environment(dbuser, dbnet_env) if fqdn: dbdns_rec = ARecord.get_unique(session, fqdn=fqdn, dns_environment=dbnet_env.dns_environment, compel=True) ip = dbdns_rec.ip elif not ip: raise ArgumentError("Please specify either --ip or --fqdn.") dbnetwork = get_net_id_from_ip(session, ip, dbnet_env) dbrouter = None for rtaddr in dbnetwork.routers: if rtaddr.ip == ip: dbrouter = rtaddr break if not dbrouter: raise NotFoundException("IP address {0} is not a router on " "{1:l}.".format(ip, dbnetwork)) map(delete_dns_record, dbrouter.dns_records) dbnetwork.routers.remove(dbrouter) session.flush() # TODO: update the templates of Zebra hosts on the network return
def render(self, session, ip, fqdn, all, network_environment, **arguments): dbnet_env = NetworkEnvironment.get_unique_or_default(session, network_environment) q = session.query(RouterAddress) q = q.join(Network) q = q.filter_by(network_environment=dbnet_env) q = q.options(contains_eager('network')) q = q.reset_joinpoint() q = q.options(undefer(RouterAddress.comments)) q = q.options(joinedload('location')) q = q.options(joinedload('dns_records')) if all: return q.all() if fqdn: dbdns_rec = ARecord.get_unique(session, fqdn=fqdn, compel=True) ip = dbdns_rec.ip errmsg = "named %s" % fqdn elif ip: errmsg = "with IP address %s" % ip else: raise ArgumentError("Please specify either --ip or --fqdn.") q = q.filter(RouterAddress.ip == ip) try: return q.one() except NoResultFound: raise NotFoundException("Router %s not found." % errmsg)
def render(self, session, ip, fqdn, all, network_environment, **arguments): dbnet_env = NetworkEnvironment.get_unique_or_default( session, network_environment) q = session.query(RouterAddress) q = q.join(Network) q = q.filter_by(network_environment=dbnet_env) q = q.options(contains_eager('network')) q = q.reset_joinpoint() q = q.options(undefer(RouterAddress.comments)) q = q.options(joinedload('location')) q = q.options(joinedload('dns_records')) if all: return q.all() if fqdn: dbdns_rec = ARecord.get_unique(session, fqdn=fqdn, compel=True) ip = dbdns_rec.ip errmsg = "named %s" % fqdn elif ip: errmsg = "with IP address %s" % ip else: raise ArgumentError("Please specify either --ip or --fqdn.") q = q.filter(RouterAddress.ip == ip) try: return q.one() except NoResultFound: raise NotFoundException("Router %s not found." % errmsg)
def render(self, session, fqdn, dns_domain, **arguments): dbdns_domain = DnsDomain.get_unique(session, dns_domain, compel=True) dba_rec = ARecord.get_unique(session, fqdn=fqdn, compel=True) ns_record = NsRecord.get_unique(session, dns_domain=dbdns_domain, a_record=dba_rec, compel=True) session.delete(ns_record) return
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, dns_domain, **kw): dbdns = DnsDomain.get_unique(session, dns_domain, compel=True) q = session.query(NsRecord).filter_by(dns_domain=dbdns) dba_record = ARecord.get_unique(session, fqdn=kw['fqdn'], compel=True) q = q.filter_by(a_record=dba_record) ns_rec = q.all() if not ns_rec: raise NotFoundException( "Could not find a dns_record for domain '%s'." % dns_domain) return ns_rec
def render(self, session, auxiliary, **kwargs): dbdns_rec = ARecord.get_unique(session, fqdn=auxiliary, compel=True) if not dbdns_rec.assignments: raise ArgumentError("Address {0:a} is not assigned to any " "interfaces.".format(dbdns_rec)) hws = [] for addr in dbdns_rec.assignments: iface = addr.interface if iface.interface_type != 'public': raise ArgumentError("{0:a} is not an auxiliary.".format(dbdns_rec)) hws.append(iface.hardware_entity) return hws
def render(self, session, logger, auxiliary, **arguments): dbmachine = None with DeleteKey("system", logger=logger): # Check dependencies, translate into user-friendly message dbauxiliary = ARecord.get_unique(session, fqdn=auxiliary, compel=True) is_aux = True if not dbauxiliary.assignments or len(dbauxiliary.assignments) > 1: is_aux = False else: assignment = dbauxiliary.assignments[0] dbmachine = assignment.interface.hardware_entity if assignment.ip == dbmachine.primary_ip: is_aux = False if assignment.interface.interface_type == 'management': is_aux = False if not is_aux: raise ArgumentError( "{0:a} is not an auxiliary.".format(dbauxiliary)) # FIXME: Look for dependencies... oldinfo = DSDBRunner.snapshot_hw(dbmachine) session.delete(assignment) delete_dns_record(dbauxiliary) session.flush() session.expire(dbmachine) dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.update_host(dbmachine, oldinfo) dsdb_runner.commit_or_rollback( "Could not remove host %s from DSDB" % auxiliary) # Past the point of no return here (DSDB has been updated)... # probably not much of an issue if writing the plenary failed. # Commit the session so that we can free the delete lock. session.commit() if dbmachine: plenary_info = PlenaryMachineInfo(dbmachine, logger=logger) # This may create a new lock, so we free first above. plenary_info.write() if dbmachine.host: # FIXME: Reconfigure pass return
def render(self, session, auxiliary, **kwargs): dbdns_rec = ARecord.get_unique(session, fqdn=auxiliary, compel=True) if not dbdns_rec.assignments: raise ArgumentError("Address {0:a} is not assigned to any " "interfaces.".format(dbdns_rec)) hws = [] for addr in dbdns_rec.assignments: iface = addr.interface if iface.interface_type != 'public': raise ArgumentError( "{0:a} is not an auxiliary.".format(dbdns_rec)) hws.append(iface.hardware_entity) return hws
def render(self, session, fqdn, dns_domain, comments, **arguments): dbdns_domain = DnsDomain.get_unique(session, dns_domain, compel=True) dba_rec = ARecord.get_unique(session, fqdn=fqdn, compel=True) NsRecord.get_unique(session, a_record=dba_rec, dns_domain=dbdns_domain, preclude=True) ns_record = NsRecord(a_record=dba_rec, dns_domain=dbdns_domain, comments=comments) session.add(ns_record) return
def render(self, session, logger, auxiliary, **arguments): dbmachine = None with DeleteKey("system", logger=logger): # Check dependencies, translate into user-friendly message dbauxiliary = ARecord.get_unique(session, fqdn=auxiliary, compel=True) is_aux = True if not dbauxiliary.assignments or len(dbauxiliary.assignments) > 1: is_aux = False else: assignment = dbauxiliary.assignments[0] dbmachine = assignment.interface.hardware_entity if assignment.ip == dbmachine.primary_ip: is_aux = False if assignment.interface.interface_type == "management": is_aux = False if not is_aux: raise ArgumentError("{0:a} is not an auxiliary.".format(dbauxiliary)) # FIXME: Look for dependencies... oldinfo = DSDBRunner.snapshot_hw(dbmachine) session.delete(assignment) delete_dns_record(dbauxiliary) session.flush() session.expire(dbmachine) dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.update_host(dbmachine, oldinfo) dsdb_runner.commit_or_rollback("Could not remove host %s from DSDB" % auxiliary) # Past the point of no return here (DSDB has been updated)... # probably not much of an issue if writing the plenary failed. # Commit the session so that we can free the delete lock. session.commit() if dbmachine: plenary_info = PlenaryMachineInfo(dbmachine, logger=logger) # This may create a new lock, so we free first above. plenary_info.write() if dbmachine.host: # FIXME: Reconfigure pass return
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, ip, building, comments, **arguments): if fqdn: dbdns_rec = ARecord.get_unique(session, fqdn, compel=True) ip = dbdns_rec.ip if not ip: raise ArgumentError("Please specify either --ip or --fqdn.") router = RouterAddress.get_unique(session, ip=ip, compel=True) if building: dbbuilding = Building.get_unique(session, name=building, compel=True) router.location = dbbuilding if comments: router.comments = comments session.flush()
def render( self, session, logger, machine, chassis, switch, interface, fqdn, ip, label, keep_dns, network_environment, **kwargs ): if machine: hwtype = "machine" hwname = machine elif chassis: hwtype = "chassis" hwname = chassis elif switch: hwtype = "switch" hwname = switch 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) dbnet_env = NetworkEnvironment.get_unique_or_default(session, network_environment) oldinfo = DSDBRunner.snapshot_hw(dbhw_ent) if fqdn: dbdns_rec = ARecord.get_unique(session, fqdn=fqdn, dns_environment=dbnet_env.dns_environment, compel=True) ip = dbdns_rec.ip addr = None if ip: addr = first_of(dbinterface.assignments, lambda x: x.ip == ip) if not addr: raise ArgumentError("{0} does not have IP address {1} assigned to " "it.".format(dbinterface, ip)) elif label is not None: addr = first_of(dbinterface.assignments, lambda x: x.label == label) if not addr: raise ArgumentError("{0} does not have an address with label " "{1}.".format(dbinterface, label)) if not addr: raise ArgumentError("Please specify the address to be removed " "using either --ip, --label, or --fqdn.") dbnetwork = addr.network ip = addr.ip if dbnetwork.network_environment != dbnet_env: raise ArgumentError( "The specified address lives in {0:l}, not in " "{1:l}. Use the --network_environment option " "to select the correct environment.".format(dbnetwork.network_environment, dbnet_env) ) # Forbid removing the primary name if ip == dbhw_ent.primary_ip: raise ArgumentError("The primary IP address of a hardware entity " "cannot be removed.") dbinterface.assignments.remove(addr) # Check if the address was assigned to multiple interfaces, and remove # the DNS entries if this was the last use q = session.query(AddressAssignment) q = q.filter_by(network=dbnetwork) q = q.filter_by(ip=ip) other_uses = q.all() if not other_uses and not keep_dns: 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=dbnet_env.dns_environment) map(delete_dns_record, q.all()) 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) dsdb_runner.update_host(dbhw_ent, oldinfo) if not other_uses and keep_dns: q = session.query(ARecord) q = q.filter_by(network=dbnetwork) q = q.filter_by(ip=ip) dbdns_rec = q.first() dsdb_runner.add_host_details(dbdns_rec.fqdn, ip) 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) dsdb_runner.update_host(dbhw_ent, oldinfo) dsdb_runner.commit_or_rollback("Could not add host to DSDB") return
def generate_ip(session, logger, dbinterface, ip=None, ipfromip=None, ipfromsystem=None, autoip=None, ipalgorithm=None, compel=False, network_environment=None, audit_results=None, **kwargs): ip_options = [ip, ipfromip, ipfromsystem, autoip] numopts = sum([1 if opt else 0 for opt in ip_options]) if numopts > 1: raise ArgumentError("Only one of --ip, --ipfromip, --ipfromsystem " "and --autoip can be specified.") elif numopts == 0: if compel: raise ArgumentError("Please specify one of the --ip, --ipfromip, " "--ipfromsystem, and --autoip parameters.") return None if ip: return ip dbsystem = None dbnetwork = None if autoip: if not dbinterface: raise ArgumentError("No interface available to automatically " "generate an IP address.") if dbinterface.port_group: # This could either be an interface from a virtual machine # or an interface on an ESX vmhost. dbcluster = None if getattr(dbinterface.hardware_entity, "cluster", None): # VM dbcluster = dbinterface.hardware_entity.cluster elif getattr(dbinterface.hardware_entity, "host", None): dbcluster = dbinterface.hardware_entity.host.cluster if not dbcluster: raise ArgumentError("Can only automatically assign an IP " "address to an interface with a port " "group on virtual machines or ESX hosts.") if not dbcluster.switch: raise ArgumentError( "Cannot automatically assign an IP " "address to an interface with a port group " "since {0} is not associated with a " "switch.".format(dbcluster)) vlan_id = VlanInfo.get_vlan_id(session, dbinterface.port_group) dbnetwork = ObservedVlan.get_network(session, vlan_id=vlan_id, switch=dbcluster.switch, compel=ArgumentError) elif dbinterface.mac: q = session.query(ObservedMac) q = q.filter_by(mac_address=dbinterface.mac) q = q.order_by(desc(ObservedMac.last_seen)) dbom = q.first() if not dbom: raise ArgumentError("No switch found in the discovery table " "for MAC address %s." % dbinterface.mac) if not dbom.switch.primary_ip: raise ArgumentError("{0} does not have a primary IP address " "to use for network " "selection.".format(dbom.switch)) dbnetwork = get_net_id_from_ip(session, dbom.switch.primary_ip) else: raise ArgumentError( "{0} has neither a MAC address nor port group " "information, it is not possible to generate " "an IP address automatically.".format(dbinterface)) if ipfromsystem: # Assumes one system entry, not necessarily correct. dbdns_rec = ARecord.get_unique(session, fqdn=ipfromsystem, compel=True) dbnetwork = dbdns_rec.network if ipfromip: # determine network dbnetwork = get_net_id_from_ip(session, ipfromip, network_environment) if not dbnetwork: raise ArgumentError("Could not determine network to use for %s." % dbsystem.fqdn) # When there are e.g. multiple "add manager --autoip" operations going on in # parallel, we must ensure that they won't try to use the same IP address. # This query places a database lock on the network, which means IP address # generation within a network will be serialized, while operations on # different networks can still run in parallel. The lock will be released by # COMMIT or ROLLBACK. dbnetwork.lock_row() startip = dbnetwork.first_usable_host used_ips = session.query(ARecord.ip) used_ips = used_ips.filter_by(network=dbnetwork) used_ips = used_ips.filter(ARecord.ip >= startip) full_set = set(range(int(startip), int(dbnetwork.broadcast))) used_set = set([int(item.ip) for item in used_ips]) free_set = full_set - used_set if not free_set: raise ArgumentError("No available IP addresses found on " "network %s." % str(dbnetwork.network)) if ipalgorithm is None or ipalgorithm == 'lowest': # Select the lowest available address ip = IPv4Address(min(free_set)) elif ipalgorithm == 'highest': # Select the highest available address ip = IPv4Address(max(free_set)) elif ipalgorithm == 'max': # Return the max. used address + 1 if not used_set: # Avoids ValueError being thrown when used_set is empty ip = IPv4Address(min(free_set)) else: next = max(used_set) if not next + 1 in free_set: raise ArgumentError("Failed to find an IP that is suitable " "for --ipalgorithm=max. Try an other " "algorithm as there are still some free " "addresses.") ip = IPv4Address(next + 1) else: raise ArgumentError("Unknown algorithm %s." % ipalgorithm) if audit_results is not None: if dbinterface: logger.info("Selected IP address {0!s} for {1:l}".format( ip, dbinterface)) else: logger.info("Selected IP address %s" % ip) audit_results.append(('ip', ip)) return ip
def render(self, session, logger, machine, chassis, switch, interface, fqdn, ip, label, keep_dns, network_environment, **kwargs): if machine: hwtype = 'machine' hwname = machine elif chassis: hwtype = 'chassis' hwname = chassis elif switch: hwtype = 'switch' hwname = switch 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) dbnet_env = NetworkEnvironment.get_unique_or_default( session, network_environment) oldinfo = DSDBRunner.snapshot_hw(dbhw_ent) if fqdn: dbdns_rec = ARecord.get_unique( session, fqdn=fqdn, dns_environment=dbnet_env.dns_environment, compel=True) ip = dbdns_rec.ip addr = None if ip: addr = first_of(dbinterface.assignments, lambda x: x.ip == ip) if not addr: raise ArgumentError( "{0} does not have IP address {1} assigned to " "it.".format(dbinterface, ip)) elif label is not None: addr = first_of(dbinterface.assignments, lambda x: x.label == label) if not addr: raise ArgumentError("{0} does not have an address with label " "{1}.".format(dbinterface, label)) if not addr: raise ArgumentError("Please specify the address to be removed " "using either --ip, --label, or --fqdn.") dbnetwork = addr.network ip = addr.ip if dbnetwork.network_environment != dbnet_env: raise ArgumentError("The specified address lives in {0:l}, not in " "{1:l}. Use the --network_environment option " "to select the correct environment.".format( dbnetwork.network_environment, dbnet_env)) # Forbid removing the primary name if ip == dbhw_ent.primary_ip: raise ArgumentError("The primary IP address of a hardware entity " "cannot be removed.") dbinterface.assignments.remove(addr) # Check if the address was assigned to multiple interfaces, and remove # the DNS entries if this was the last use q = session.query(AddressAssignment) q = q.filter_by(network=dbnetwork) q = q.filter_by(ip=ip) other_uses = q.all() if not other_uses and not keep_dns: 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=dbnet_env.dns_environment) map(delete_dns_record, q.all()) 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) dsdb_runner.update_host(dbhw_ent, oldinfo) if not other_uses and keep_dns: q = session.query(ARecord) q = q.filter_by(network=dbnetwork) q = q.filter_by(ip=ip) dbdns_rec = q.first() dsdb_runner.add_host_details(dbdns_rec.fqdn, ip) 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) dsdb_runner.update_host(dbhw_ent, oldinfo) dsdb_runner.commit_or_rollback("Could not add host to DSDB") return
def render(self, session, logger, fqdn, ip, reverse_ptr, dns_environment, network_environment, comments, **arguments): dbnet_env, dbdns_env = get_net_dns_env(session, network_environment, dns_environment) dbdns_rec = ARecord.get_unique(session, fqdn=fqdn, dns_environment=dbdns_env, compel=True) old_ip = dbdns_rec.ip old_comments = dbdns_rec.comments if ip: if dbdns_rec.hardware_entity: raise ArgumentError("{0} is a primary name, and its IP address " "cannot be changed.".format(dbdns_rec)) if dbdns_rec.assignments: ifaces = ", ".join(["%s/%s" % (addr.interface.hardware_entity, addr.interface) for addr in dbdns_rec.assignments]) raise ArgumentError("{0} is already used by the following " "interfaces, and its IP address cannot be " "changed: {1!s}." .format(dbdns_rec, ifaces)) dbnetwork = get_net_id_from_ip(session, ip, dbnet_env) 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=dbdns_env) existing = q.first() if existing: raise ArgumentError("IP address {0!s} is already used by " "{1:l}." .format(ip, existing)) dbdns_rec.network = dbnetwork old_ip = dbdns_rec.ip dbdns_rec.ip = ip if reverse_ptr: old_reverse = dbdns_rec.reverse_ptr set_reverse_ptr(session, logger, dbdns_rec, reverse_ptr) if old_reverse and old_reverse != dbdns_rec.reverse_ptr: delete_target_if_needed(session, old_reverse) if comments: dbdns_rec.comments = comments session.flush() if dbdns_env.is_default and (dbdns_rec.ip != old_ip or dbdns_rec.comments != old_comments): dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.update_host_details(dbdns_rec.fqdn, new_ip=dbdns_rec.ip, old_ip=old_ip, new_comments=dbdns_rec.comments, old_comments=old_comments) dsdb_runner.commit_or_rollback() return
def generate_ip(session, logger, dbinterface, ip=None, ipfromip=None, ipfromsystem=None, autoip=None, ipalgorithm=None, compel=False, network_environment=None, audit_results=None, **kwargs): ip_options = [ip, ipfromip, ipfromsystem, autoip] numopts = sum([1 if opt else 0 for opt in ip_options]) if numopts > 1: raise ArgumentError("Only one of --ip, --ipfromip, --ipfromsystem " "and --autoip can be specified.") elif numopts == 0: if compel: raise ArgumentError("Please specify one of the --ip, --ipfromip, " "--ipfromsystem, and --autoip parameters.") return None if ip: return ip dbsystem = None dbnetwork = None if autoip: if not dbinterface: raise ArgumentError("No interface available to automatically " "generate an IP address.") if dbinterface.port_group: # This could either be an interface from a virtual machine # or an interface on an ESX vmhost. dbcluster = None if getattr(dbinterface.hardware_entity, "cluster", None): # VM dbcluster = dbinterface.hardware_entity.cluster elif getattr(dbinterface.hardware_entity, "host", None): dbcluster = dbinterface.hardware_entity.host.cluster if not dbcluster: raise ArgumentError("Can only automatically assign an IP " "address to an interface with a port " "group on virtual machines or ESX hosts.") if not dbcluster.switch: raise ArgumentError("Cannot automatically assign an IP " "address to an interface with a port group " "since {0} is not associated with a " "switch.".format(dbcluster)) vlan_id = VlanInfo.get_vlan_id(session, dbinterface.port_group) dbnetwork = ObservedVlan.get_network(session, vlan_id=vlan_id, switch=dbcluster.switch, compel=ArgumentError) elif dbinterface.mac: q = session.query(ObservedMac) q = q.filter_by(mac_address=dbinterface.mac) q = q.order_by(desc(ObservedMac.last_seen)) dbom = q.first() if not dbom: raise ArgumentError("No switch found in the discovery table " "for MAC address %s." % dbinterface.mac) if not dbom.switch.primary_ip: raise ArgumentError("{0} does not have a primary IP address " "to use for network " "selection.".format(dbom.switch)) dbnetwork = get_net_id_from_ip(session, dbom.switch.primary_ip) else: raise ArgumentError("{0} has neither a MAC address nor port group " "information, it is not possible to generate " "an IP address automatically." .format(dbinterface)) if ipfromsystem: # Assumes one system entry, not necessarily correct. dbdns_rec = ARecord.get_unique(session, fqdn=ipfromsystem, compel=True) dbnetwork = dbdns_rec.network if ipfromip: # determine network dbnetwork = get_net_id_from_ip(session, ipfromip, network_environment) if not dbnetwork: raise ArgumentError("Could not determine network to use for %s." % dbsystem.fqdn) # When there are e.g. multiple "add manager --autoip" operations going on in # parallel, we must ensure that they won't try to use the same IP address. # This query places a database lock on the network, which means IP address # generation within a network will be serialized, while operations on # different networks can still run in parallel. The lock will be released by # COMMIT or ROLLBACK. dbnetwork.lock_row() startip = dbnetwork.first_usable_host used_ips = session.query(ARecord.ip) used_ips = used_ips.filter_by(network=dbnetwork) used_ips = used_ips.filter(ARecord.ip >= startip) full_set = set(range(int(startip), int(dbnetwork.broadcast))) used_set = set([int(item.ip) for item in used_ips]) free_set = full_set - used_set if not free_set: raise ArgumentError("No available IP addresses found on " "network %s." % str(dbnetwork.network)) if ipalgorithm is None or ipalgorithm == 'lowest': # Select the lowest available address ip = IPv4Address(min(free_set)) elif ipalgorithm == 'highest': # Select the highest available address ip = IPv4Address(max(free_set)) elif ipalgorithm == 'max': # Return the max. used address + 1 if not used_set: # Avoids ValueError being thrown when used_set is empty ip = IPv4Address(min(free_set)) else: next = max(used_set) if not next + 1 in free_set: raise ArgumentError("Failed to find an IP that is suitable " "for --ipalgorithm=max. Try an other " "algorithm as there are still some free " "addresses.") ip = IPv4Address(next + 1) else: raise ArgumentError("Unknown algorithm %s." % ipalgorithm) if audit_results is not None: if dbinterface: logger.info("Selected IP address {0!s} for {1:l}" .format(ip, dbinterface)) else: logger.info("Selected IP address %s" % ip) audit_results.append(('ip', ip)) return ip