def render(self, session, logger, switch, label, model, rack, type, ip, interface, mac, vendor, serial, comments, **arguments): dbmodel = Model.get_unique(session, name=model, vendor=vendor, machine_type='switch', compel=True) dblocation = get_location(session, rack=rack) dbdns_rec, newly_created = grab_address(session, switch, ip, allow_restricted_domain=True, allow_reserved=True, preclude=True) if not label: label = dbdns_rec.fqdn.name try: Switch.check_label(label) except ArgumentError: raise ArgumentError("Could not deduce a valid hardware label " "from the switch name. Please specify " "--label.") # FIXME: What do the error messages for an invalid enum (switch_type) # look like? dbswitch = Switch(label=label, switch_type=type, location=dblocation, model=dbmodel, serial_no=serial, comments=comments) session.add(dbswitch) dbswitch.primary_name = dbdns_rec # FIXME: get default name from the model iftype = "oa" if not interface: interface = "xge" ifcomments = "Created automatically by add_switch" else: ifcomments = None if interface.lower().startswith("lo"): iftype = "loopback" dbinterface = get_or_create_interface(session, dbswitch, name=interface, mac=mac, interface_type=iftype, comments=ifcomments) dbnetwork = get_net_id_from_ip(session, ip) # TODO: should we call check_ip_restrictions() here? assign_address(dbinterface, ip, dbnetwork) session.flush() plenary = PlenarySwitch(dbswitch, logger=logger) with plenary.get_write_key() as key: plenary.stash() try: plenary.write(locked=True) dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.update_host(dbswitch, None) dsdb_runner.commit_or_rollback("Could not add switch to DSDB") except: plenary.restore_stash() raise return
def render(self, session, logger, network_device, label, model, type, ip, interface, iftype, mac, vendor, serial, comments, **arguments): dbmodel = Model.get_unique(session, name=model, vendor=vendor, compel=True) if not dbmodel.model_type.isNetworkDeviceType(): raise ArgumentError("This command can only be used to " "add network devices.") dblocation = get_location(session, compel=True, **arguments) dbdns_rec, newly_created = grab_address(session, network_device, ip, allow_restricted_domain=True, allow_reserved=True, preclude=True) if not label: label = dbdns_rec.fqdn.name try: NetworkDevice.check_label(label) except ArgumentError: raise ArgumentError("Could not deduce a valid hardware label " "from the network device name. Please specify " "--label.") # FIXME: What do the error messages for an invalid enum (switch_type) # look like? dbnetdev = NetworkDevice(label=label, switch_type=type, location=dblocation, model=dbmodel, serial_no=serial, comments=comments) session.add(dbnetdev) dbnetdev.primary_name = dbdns_rec check_netdev_iftype(iftype) dbinterface = get_or_create_interface(session, dbnetdev, name=interface, mac=mac, interface_type=iftype) dbnetwork = get_net_id_from_ip(session, ip) # TODO: should we call check_ip_restrictions() here? assign_address(dbinterface, ip, dbnetwork, logger=logger) session.flush() plenary = Plenary.get_plenary(dbnetdev, logger=logger) with plenary.get_key(): plenary.stash() try: plenary.write(locked=True) dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.update_host(dbnetdev, None) dsdb_runner.commit_or_rollback("Could not add network device to DSDB") except: plenary.restore_stash() raise return
def render(self, session, logger, chassis, label, rack, model, vendor, ip, interface, mac, serial, comments, **arguments): dbdns_rec, newly_created = grab_address(session, chassis, ip, allow_restricted_domain=True, allow_reserved=True, preclude=True) if not label: label = dbdns_rec.fqdn.name try: Chassis.check_label(label) except ArgumentError: raise ArgumentError("Could not deduce a valid hardware label " "from the chassis name. Please specify " "--label.") dblocation = get_location(session, rack=rack) dbmodel = Model.get_unique(session, name=model, vendor=vendor, machine_type='chassis', compel=True) # FIXME: Precreate chassis slots? dbchassis = Chassis(label=label, location=dblocation, model=dbmodel, serial_no=serial, comments=comments) session.add(dbchassis) dbchassis.primary_name = dbdns_rec # FIXME: get default name from the model if not interface: interface = "oa" ifcomments = "Created automatically by add_chassis" else: ifcomments = None dbinterface = get_or_create_interface(session, dbchassis, name=interface, mac=mac, interface_type="oa", comments=ifcomments) if ip: dbnetwork = get_net_id_from_ip(session, ip) check_ip_restrictions(dbnetwork, ip) assign_address(dbinterface, ip, dbnetwork) session.flush() if ip: dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.update_host(dbchassis, None) dsdb_runner.commit_or_rollback("Could not add chassis to DSDB") return
def render(self, session, logger, hostname, manager, interface, mac, comments, **arguments): dbhost = hostname_to_host(session, hostname) dbmachine = dbhost.machine oldinfo = DSDBRunner.snapshot_hw(dbmachine) if not manager: manager = "%sr.%s" % (dbmachine.primary_name.fqdn.name, dbmachine.primary_name.fqdn.dns_domain.name) dbinterface = get_or_create_interface(session, dbmachine, name=interface, mac=mac, interface_type='management') addrs = ", ".join(["%s [%s]" % (addr.logical_name, addr.ip) for addr in dbinterface.assignments]) if addrs: raise ArgumentError("{0} already has the following addresses: " "{1}.".format(dbinterface, addrs)) audit_results = [] ip = generate_ip(session, logger, dbinterface, compel=True, audit_results=audit_results, **arguments) dbdns_rec, newly_created = grab_address(session, manager, ip, comments=comments, preclude=True) assign_address(dbinterface, ip, dbdns_rec.network) session.flush() plenary_info = PlenaryMachineInfo(dbmachine, logger=logger) key = plenary_info.get_write_key() try: lock_queue.acquire(key) plenary_info.write(locked=True) dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.update_host(dbmachine, oldinfo) dsdb_runner.commit_or_rollback("Could not add host to DSDB") except: plenary_info.restore_stash() raise finally: lock_queue.release(key) if dbmachine.host: # XXX: Host needs to be reconfigured. pass for name, value in audit_results: self.audit_result(session, name, value, **arguments) return
def update_primary_ip(session, dbhw_ent, ip): if not dbhw_ent.primary_name: raise ArgumentError( "{0} does not have a primary name.".format(dbhw_ent)) dbnetwork = get_net_id_from_ip(session, ip) check_ip_restrictions(dbnetwork, ip) # The primary address must be unique 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)) # Convert ReservedName to ARecord if needed if isinstance(dbhw_ent.primary_name, ReservedName): convert_reserved_to_arecord(session, dbhw_ent.primary_name, dbnetwork, ip) # When converting a ReservedName to an ARecord, we have to bind the # primary address to an interface. Try to pick one. dbinterface = first_of(dbhw_ent.interfaces, lambda x: x.bootable) if not dbinterface: dbinterface = first_of(dbhw_ent.interfaces, lambda x: x.interface_type != "management") if not dbinterface: # pragma: no cover raise AquilonError("Cannot update the primary IP address of {0:l} " "because it does not have any interfaces " "defined.".format(dbhw_ent)) assign_address(dbinterface, ip, dbnetwork) else: dns_rec = dbhw_ent.primary_name q = session.query(AddressAssignment) q = q.filter_by(network=dns_rec.network) q = q.filter_by(ip=dns_rec.ip) q = q.join(Interface) q = q.filter_by(hardware_entity=dbhw_ent) # In case of Zebra, the address may be assigned to multiple interfaces addrs = q.all() dns_rec.ip = ip dns_rec.network = dbnetwork for addr in addrs: addr.ip = ip addr.network = dbnetwork
def update_primary_ip(session, logger, dbhw_ent, ip): if not dbhw_ent.primary_name: raise ArgumentError("{0} does not have a primary name." .format(dbhw_ent)) dbnetwork = get_net_id_from_ip(session, ip) check_ip_restrictions(dbnetwork, ip) # The primary address must be unique 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)) # Convert ReservedName to ARecord if needed if isinstance(dbhw_ent.primary_name, ReservedName): convert_reserved_to_arecord(session, dbhw_ent.primary_name, dbnetwork, ip) # When converting a ReservedName to an ARecord, we have to bind the # primary address to an interface. Try to pick one. dbinterface = first_of(dbhw_ent.interfaces, lambda x: x.bootable) if not dbinterface: dbinterface = first_of(dbhw_ent.interfaces, lambda x: x.interface_type != "management") if not dbinterface: # pragma: no cover raise AquilonError("Cannot update the primary IP address of {0:l} " "because it does not have any interfaces " "defined.".format(dbhw_ent)) assign_address(dbinterface, ip, dbnetwork, logger=logger) else: dns_rec = dbhw_ent.primary_name q = session.query(AddressAssignment) q = q.filter_by(network=dns_rec.network) q = q.filter_by(ip=dns_rec.ip) q = q.join(Interface) q = q.filter_by(hardware_entity=dbhw_ent) # In case of Zebra, the address may be assigned to multiple interfaces addrs = q.all() dns_rec.ip = ip dns_rec.network = dbnetwork for addr in addrs: addr.ip = ip addr.network = dbnetwork
def apply_service_address(dbhost, ifnames, srv_addr): for ifname in ifnames: dbinterface = None for iface in dbhost.machine.interfaces: if iface.name == ifname: dbinterface = iface break if not dbinterface: raise ArgumentError("{0} does not have an interface named " "{1}.".format(dbhost.machine, ifname)) assign_address( dbinterface, srv_addr.dns_record.ip, srv_addr.dns_record.network, label=srv_addr.name, resource=srv_addr )
def apply_service_address(dbhost, ifnames, srv_addr): for ifname in ifnames: dbinterface = None for iface in dbhost.machine.interfaces: if iface.name == ifname: dbinterface = iface break if not dbinterface: raise ArgumentError("{0} does not have an interface named " "{1}.".format(dbhost.machine, ifname)) assign_address(dbinterface, srv_addr.dns_record.ip, srv_addr.dns_record.network, label=srv_addr.name, resource=srv_addr)
def render(self, session, logger, chassis, label, rack, model, vendor, ip, interface, mac, serial, comments, **arguments): dbdns_rec, newly_created = grab_address(session, chassis, ip, allow_restricted_domain=True, allow_reserved=True, preclude=True) if not label: label = dbdns_rec.fqdn.name try: Chassis.check_label(label) except ArgumentError: raise ArgumentError("Could not deduce a valid hardware label " "from the chassis name. Please specify " "--label.") dblocation = get_location(session, rack=rack) dbmodel = Model.get_unique(session, name=model, vendor=vendor, machine_type='chassis', compel=True) # FIXME: Precreate chassis slots? dbchassis = Chassis(label=label, location=dblocation, model=dbmodel, serial_no=serial, comments=comments) session.add(dbchassis) dbchassis.primary_name = dbdns_rec # FIXME: get default name from the model if not interface: interface = "oa" ifcomments = "Created automatically by add_chassis" else: ifcomments = None dbinterface = get_or_create_interface(session, dbchassis, name=interface, mac=mac, interface_type="oa", comments=ifcomments) if ip: dbnetwork = get_net_id_from_ip(session, ip) check_ip_restrictions(dbnetwork, ip) assign_address(dbinterface, ip, dbnetwork) session.flush() if ip: dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.update_host(dbchassis, None) dsdb_runner.commit_or_rollback("Could not add chassis to DSDB") return
def assign_zebra_address(self, session, dbmachine, dbdns_rec, zebra_interfaces): """ Assign a Zebra-managed address to multiple interfaces """ # Reset the routing configuration for iface in dbmachine.interfaces: if iface.default_route: iface.default_route = False # Disable autoflush, since the ServiceAddress object won't be complete # until add_resource() is called with session.no_autoflush: resholder = HostResource(host=dbmachine.host) session.add(resholder) dbsrv_addr = ServiceAddress(name="hostname", dns_record=dbdns_rec) resholder.resources.append(dbsrv_addr) for name in zebra_interfaces.split(","): dbinterface = None for iface in dbmachine.interfaces: if iface.name == name: dbinterface = iface if not dbinterface: raise ArgumentError("{0} does not have an interface named " "{1}.".format(dbmachine, name)) assign_address(dbinterface, dbdns_rec.ip, dbdns_rec.network, label="hostname", resource=dbsrv_addr) # Make sure the transit IPs resolve to the primary name for addr in dbinterface.assignments: if addr.label: continue for dnr in addr.dns_records: dnr.reverse_ptr = dbdns_rec.fqdn # Transits should be providers of the default route dbinterface.default_route = True return dbsrv_addr
def assign_zebra_address(self, session, dbmachine, dbdns_rec, zebra_interfaces, logger): """ Assign a Zebra-managed address to multiple interfaces """ # Reset the routing configuration for iface in dbmachine.interfaces: if iface.default_route: iface.default_route = False # Disable autoflush, since the ServiceAddress object won't be complete # until add_resource() is called with session.no_autoflush: resholder = HostResource(host=dbmachine.host) session.add(resholder) dbsrv_addr = ServiceAddress(name="hostname", dns_record=dbdns_rec) resholder.resources.append(dbsrv_addr) for name in zebra_interfaces.split(","): dbinterface = None for iface in dbmachine.interfaces: if iface.name == name: dbinterface = iface if not dbinterface: raise ArgumentError("{0} does not have an interface named " "{1}.".format(dbmachine, name)) assign_address(dbinterface, dbdns_rec.ip, dbdns_rec.network, label="hostname", resource=dbsrv_addr, logger=logger) # Make sure the transit IPs resolve to the primary name for addr in dbinterface.assignments: if addr.label: continue for dnr in addr.dns_records: dnr.reverse_ptr = dbdns_rec.fqdn # Transits should be providers of the default route dbinterface.default_route = True return dbsrv_addr
def add_address(iface, ifname, ip, label, relaxed): """ Helper for adding an IP address, honouring dryrun """ if label: name = "%s-%s-%s" % (dbswitch.primary_name.fqdn.name, ifname, label) else: name = "%s-%s" % (dbswitch.primary_name.fqdn.name, ifname) fqdn = "%s.%s" % (name, dbswitch.primary_name.fqdn.dns_domain) if dryrun: args = ["add_interface_address", "--interface", ifname, "--ip", ip] if label: args.extend(["--label", label]) aqcmd(*args) else: # Doing the DSDB update if the address existed before would be # tricky, so prevent that case by passing preclude=True dbdns_rec, newly_created = grab_address(session, fqdn, ip, dbnet_env, relaxed=relaxed, preclude=True) assign_address(iface, ip, dbdns_rec.network, label=label)
def render(self, session, logger, hostname, manager, interface, mac, comments, **arguments): dbhost = hostname_to_host(session, hostname) dbmachine = dbhost.machine oldinfo = DSDBRunner.snapshot_hw(dbmachine) if not manager: manager = "%sr.%s" % (dbmachine.primary_name.fqdn.name, dbmachine.primary_name.fqdn.dns_domain.name) dbinterface = get_or_create_interface(session, dbmachine, name=interface, mac=mac, interface_type='management') addrs = ", ".join([ "%s [%s]" % (addr.logical_name, addr.ip) for addr in dbinterface.assignments ]) if addrs: raise ArgumentError("{0} already has the following addresses: " "{1}.".format(dbinterface, addrs)) audit_results = [] ip = generate_ip(session, logger, dbinterface, compel=True, audit_results=audit_results, **arguments) dbdns_rec, newly_created = grab_address(session, manager, ip, comments=comments, preclude=True) assign_address(dbinterface, ip, dbdns_rec.network) session.flush() plenary_info = PlenaryMachineInfo(dbmachine, logger=logger) key = plenary_info.get_write_key() try: lock_queue.acquire(key) plenary_info.write(locked=True) dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.update_host(dbmachine, oldinfo) dsdb_runner.commit_or_rollback("Could not add host to DSDB") except: plenary_info.restore_stash() raise finally: lock_queue.release(key) if dbmachine.host: # XXX: Host needs to be reconfigured. pass for name, value in audit_results: self.audit_result(session, name, value, **arguments) return
def render(self, session, logger, interface, machine, mac, model, vendor, boot, pg, autopg, comments, master, clear_master, default_route, rename_to, **arguments): """This command expects to locate an interface based only on name and machine - all other fields, if specified, are meant as updates. If the machine has a host, dsdb may need to be updated. The boot flag can *only* be set to true. This is mostly technical, as at this point in the interface it is difficult to tell if the flag was unset or set to false. However, it also vastly simplifies the dsdb logic - we never have to worry about a user trying to remove the boot flag from a host in dsdb. """ audit_results = [] dbhw_ent = Machine.get_unique(session, machine, compel=True) dbinterface = Interface.get_unique(session, hardware_entity=dbhw_ent, name=interface, compel=True) oldinfo = DSDBRunner.snapshot_hw(dbhw_ent) if arguments.get('hostname', None): # Hack to set an intial interface for an aurora host... dbhost = dbhw_ent.host if dbhost.archetype.name == 'aurora' and \ dbhw_ent.primary_ip and not dbinterface.addresses: assign_address(dbinterface, dbhw_ent.primary_ip, dbhw_ent.primary_name.network) # We may need extra IP verification (or an autoip option)... # This may also throw spurious errors if attempting to set the # port_group to a value it already has. if pg is not None and dbinterface.port_group != pg.lower().strip(): dbinterface.port_group = verify_port_group( dbinterface.hardware_entity, pg) elif autopg: dbinterface.port_group = choose_port_group( session, logger, dbinterface.hardware_entity) audit_results.append(('pg', dbinterface.port_group)) if master: if dbinterface.addresses: # FIXME: as a special case, if the only address is the # primary IP, then we could just move it to the master # interface. However this can be worked around by bonding # the interface before calling "add host", so don't bother # for now. raise ArgumentError("Can not enslave {0:l} because it has " "addresses.".format(dbinterface)) dbmaster = Interface.get_unique(session, hardware_entity=dbhw_ent, name=master, compel=True) if dbmaster in dbinterface.all_slaves(): raise ArgumentError( "Enslaving {0:l} would create a circle, " "which is not allowed.".format(dbinterface)) dbinterface.master = dbmaster if clear_master: if not dbinterface.master: raise ArgumentError("{0} is not a slave.".format(dbinterface)) dbinterface.master = None if comments: dbinterface.comments = comments if boot: # Should we also transfer the primary IP to the new boot interface? # That could get tricky if the new interface already has an IP # address... for i in dbhw_ent.interfaces: if i == dbinterface: i.bootable = True i.default_route = True else: i.bootable = False i.default_route = False if default_route is not None: dbinterface.default_route = default_route if not first_of(dbhw_ent.interfaces, lambda x: x.default_route): logger.client_info("Warning: {0:l} has no default route, hope " "that's ok.".format(dbhw_ent)) #Set this mac address last so that you can update to a bootable #interface *before* adding a mac address. This is so the validation #that takes place in the interface class doesn't have to be worried #about the order of update to bootable=True and mac address if mac: q = session.query(Interface).filter_by(mac=mac) other = q.first() if other and other != dbinterface: raise ArgumentError("MAC address {0} is already in use by " "{1:l}.".format(mac, other)) dbinterface.mac = mac if model or vendor: if not dbinterface.model_allowed: raise ArgumentError( "Model/vendor can not be set for a {0:lc}.".format( dbinterface)) dbmodel = Model.get_unique(session, name=model, vendor=vendor, machine_type='nic', compel=True) dbinterface.model = dbmodel if rename_to: rename_interface(session, dbinterface, rename_to) session.flush() session.refresh(dbhw_ent) plenary_info = PlenaryMachineInfo(dbhw_ent, logger=logger) key = plenary_info.get_write_key() try: lock_queue.acquire(key) plenary_info.write(locked=True) if dbhw_ent.host and dbhw_ent.host.archetype.name != "aurora": dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.update_host(dbhw_ent, oldinfo) dsdb_runner.commit_or_rollback() except AquilonError, err: plenary_info.restore_stash() raise ArgumentError(err)
def render(self, session, logger, interface, machine, mac, model, vendor, boot, pg, autopg, comments, master, clear_master, default_route, rename_to, **arguments): """This command expects to locate an interface based only on name and machine - all other fields, if specified, are meant as updates. If the machine has a host, dsdb may need to be updated. The boot flag can *only* be set to true. This is mostly technical, as at this point in the interface it is difficult to tell if the flag was unset or set to false. However, it also vastly simplifies the dsdb logic - we never have to worry about a user trying to remove the boot flag from a host in dsdb. """ audit_results = [] dbhw_ent = Machine.get_unique(session, machine, compel=True) dbinterface = Interface.get_unique(session, hardware_entity=dbhw_ent, name=interface, compel=True) oldinfo = DSDBRunner.snapshot_hw(dbhw_ent) if arguments.get('hostname', None): # Hack to set an intial interface for an aurora host... dbhost = dbhw_ent.host if dbhost.archetype.name == 'aurora' and \ dbhw_ent.primary_ip and not dbinterface.addresses: assign_address(dbinterface, dbhw_ent.primary_ip, dbhw_ent.primary_name.network, logger=logger) # We may need extra IP verification (or an autoip option)... # This may also throw spurious errors if attempting to set the # port_group to a value it already has. if pg is not None and dbinterface.port_group != pg.lower().strip(): dbinterface.port_group = verify_port_group( dbinterface.hardware_entity, pg) elif autopg: dbinterface.port_group = choose_port_group( session, logger, dbinterface.hardware_entity) audit_results.append(('pg', dbinterface.port_group)) if master: if dbinterface.addresses: # FIXME: as a special case, if the only address is the # primary IP, then we could just move it to the master # interface. However this can be worked around by bonding # the interface before calling "add host", so don't bother # for now. raise ArgumentError("Can not enslave {0:l} because it has " "addresses.".format(dbinterface)) dbmaster = Interface.get_unique(session, hardware_entity=dbhw_ent, name=master, compel=True) if dbmaster in dbinterface.all_slaves(): raise ArgumentError("Enslaving {0:l} would create a circle, " "which is not allowed.".format(dbinterface)) dbinterface.master = dbmaster if clear_master: if not dbinterface.master: raise ArgumentError("{0} is not a slave.".format(dbinterface)) dbinterface.master = None if comments: dbinterface.comments = comments if boot: # Figure out if the current bootble interface also has the # default route set; the new bootable interface probably # wants to have the same settings. Note that if # old_default_route is None there was no bootable interface. old_default_route = None for i in dbhw_ent.interfaces: if i.bootable == True: old_default_route = i.default_route break # Apply the bootable flag to the supplied interface, clearing # it on all other interfaces. for i in dbhw_ent.interfaces: if i == dbinterface: i.bootable = True else: i.bootable = False # If the user was not explicit about the default route flag # (default_route is None); there was an existing bootable # interface (old_default_route is not None); the new default # route setting differs from the old - then produce a warning. if (default_route is None and old_default_route is not None and dbinterface.default_route != old_default_route): if old_default_route: logger.client_info("Warning: New boot interface {0} is no " "longer provides the default route; it " "did before!".format(dbinterface)) else: logger.client_info("Warning: New boot interface {0} now " "provides the default route; it didn't " "before!".format(dbinterface)) # Should we also transfer the primary IP to the new boot interface? # That could get tricky if the new interface already has an IP # address... if default_route is not None: dbinterface.default_route = default_route if not first_of(dbhw_ent.interfaces, lambda x: x.default_route): logger.client_info("Warning: {0:l} has no default route, hope " "that's ok.".format(dbhw_ent)) #Set this mac address last so that you can update to a bootable #interface *before* adding a mac address. This is so the validation #that takes place in the interface class doesn't have to be worried #about the order of update to bootable=True and mac address if mac: q = session.query(Interface).filter_by(mac=mac) other = q.first() if other and other != dbinterface: raise ArgumentError("MAC address {0} is already in use by " "{1:l}.".format(mac, other)) dbinterface.mac = mac if model or vendor: if not dbinterface.model_allowed: raise ArgumentError("Model/vendor can not be set for a {0:lc}." .format(dbinterface)) dbmodel = Model.get_unique(session, name=model, vendor=vendor, model_type=NicType.Nic, compel=True) dbinterface.model = dbmodel if rename_to: rename_interface(session, dbinterface, rename_to) session.flush() plenaries = PlenaryCollection(logger=logger) plenaries.append(Plenary.get_plenary(dbhw_ent)) # Interface renaming affects the host and service addresses if dbhw_ent.host: plenaries.append(Plenary.get_plenary(dbhw_ent.host)) for addr in dbinterface.assignments: if addr.service_address: plenaries.append(Plenary.get_plenary(addr.service_address)) with plenaries.get_key(): try: plenaries.write(locked=True) if dbhw_ent.host and dbhw_ent.host.archetype.name != "aurora": dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.update_host(dbhw_ent, oldinfo) dsdb_runner.commit_or_rollback() except AquilonError, err: plenaries.restore_stash() raise ArgumentError(err) except:
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
def render(self, session, logger, hostname, machine, archetype, domain, sandbox, osname, osversion, buildstatus, personality, comments, zebra_interfaces, grn, eon_id, skip_dsdb_check=False, **arguments): dbarchetype = Archetype.get_unique(session, archetype, compel=True) section = "archetype_" + dbarchetype.name # This is for the various add_*_host commands if not domain and not sandbox: domain = self.config.get(section, "host_domain") (dbbranch, dbauthor) = get_branch_and_author(session, logger, domain=domain, sandbox=sandbox, compel=True) if hasattr(dbbranch, "allow_manage") and not dbbranch.allow_manage: raise ArgumentError( "Adding hosts to {0:l} is not allowed.".format(dbbranch)) if not buildstatus: buildstatus = 'build' dbstatus = HostLifecycle.get_unique(session, buildstatus, compel=True) dbmachine = Machine.get_unique(session, machine, compel=True) oldinfo = DSDBRunner.snapshot_hw(dbmachine) if not personality: if self.config.has_option(section, "default_personality"): personality = self.config.get(section, "default_personality") else: personality = 'generic' dbpersonality = Personality.get_unique(session, name=personality, archetype=dbarchetype, compel=True) if not osname: if self.config.has_option(section, "default_osname"): osname = self.config.get(section, "default_osname") if not osversion: if self.config.has_option(section, "default_osversion"): osversion = self.config.get(section, "default_osversion") if not osname or not osversion: raise ArgumentError("Can not determine a sensible default OS " "for archetype %s. Please use the " "--osname and --osversion parameters." % (dbarchetype.name)) dbos = OperatingSystem.get_unique(session, name=osname, version=osversion, archetype=dbarchetype, compel=True) if (dbmachine.model.machine_type == 'aurora_node' and dbpersonality.archetype.name != 'aurora'): raise ArgumentError("Machines of type aurora_node can only be " "added with archetype aurora.") if dbmachine.host: raise ArgumentError("{0:c} {0.label} is already allocated to " "{1:l}.".format(dbmachine, dbmachine.host)) if grn or eon_id: dbgrn = lookup_grn(session, grn, eon_id, logger=logger, config=self.config) else: dbgrn = dbpersonality.owner_grn dbhost = Host(machine=dbmachine, branch=dbbranch, owner_grn=dbgrn, sandbox_author=dbauthor, personality=dbpersonality, status=dbstatus, operating_system=dbos, comments=comments) session.add(dbhost) if self.config.has_option("archetype_" + archetype, "default_grn_target"): dbhost.grns.append((dbhost, dbgrn, self.config.get("archetype_" + archetype, "default_grn_target"))) if zebra_interfaces: # --autoip does not make sense for Zebra (at least not the way it's # implemented currently) dbinterface = None else: dbinterface = get_boot_interface(dbmachine) # This method is allowed to return None. This can only happen # (currently) using add_aurora_host, add_windows_host, or possibly by # bypassing the aq client and posting a request directly. audit_results = [] ip = generate_ip(session, logger, dbinterface, audit_results=audit_results, **arguments) dbdns_rec, newly_created = grab_address(session, hostname, ip, allow_restricted_domain=True, allow_reserved=True, preclude=True) dbmachine.primary_name = dbdns_rec # Fix up auxiliary addresses to point to the primary name by default if ip: dns_env = dbdns_rec.fqdn.dns_environment for addr in dbmachine.all_addresses(): if addr.interface.interface_type == "management": continue if addr.service_address_id: # pragma: no cover continue for rec in addr.dns_records: if rec.fqdn.dns_environment == dns_env: rec.reverse_ptr = dbdns_rec.fqdn if zebra_interfaces: if not ip: raise ArgumentError( "Zebra configuration requires an IP address.") dbsrv_addr = self.assign_zebra_address(session, dbmachine, dbdns_rec, zebra_interfaces) else: if ip: if not dbinterface: raise ArgumentError( "You have specified an IP address for the " "host, but {0:l} does not have a bootable " "interface.".format(dbmachine)) assign_address(dbinterface, ip, dbdns_rec.network) dbsrv_addr = None session.flush() plenaries = PlenaryCollection(logger=logger) plenaries.append(Plenary.get_plenary(dbmachine)) if dbmachine.vm_container: plenaries.append(Plenary.get_plenary(dbmachine.vm_container)) if dbsrv_addr: plenaries.append(Plenary.get_plenary(dbsrv_addr)) key = plenaries.get_write_key() try: lock_queue.acquire(key) plenaries.write(locked=True) # XXX: This (and some of the code above) is horrible. There # should be a generic/configurable hook here that could kick # in based on archetype and/or domain. dsdb_runner = DSDBRunner(logger=logger) if dbhost.archetype.name == 'aurora': # For aurora, check that DSDB has a record of the host. if not skip_dsdb_check: try: dsdb_runner.show_host(hostname) except ProcessException, e: raise ArgumentError("Could not find host in DSDB: %s" % e) elif not dbmachine.primary_ip: logger.info("No IP for %s, not adding to DSDB." % dbmachine.fqdn)
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, 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, switch, chassis, mac, user, **arguments): if not (machine or switch or chassis or mac): raise ArgumentError("Please specify at least one of --chassis, " "--machine, --switch or --mac.") if machine: dbhw_ent = Machine.get_unique(session, machine, compel=True) elif switch: dbhw_ent = Switch.get_unique(session, switch, compel=True) elif chassis: dbhw_ent = Chassis.get_unique(session, chassis, compel=True) else: dbhw_ent = None dbinterface = Interface.get_unique(session, hardware_entity=dbhw_ent, name=interface, mac=mac, compel=True) if not dbhw_ent: dbhw_ent = dbinterface.hardware_entity if dbinterface.vlans: vlans = ", ".join([iface.name for iface in dbinterface.vlans.values()]) raise ArgumentError("{0} is the parent of the following VLAN " "interfaces, delete them first: " "{1}.".format(dbinterface, vlans)) if dbinterface.slaves: slaves = ", ".join([iface.name for iface in dbinterface.slaves]) raise ArgumentError("{0} is the master of the following slave " "interfaces, delete them first: " "{1}.".format(dbinterface, slaves)) try: for addr in dbinterface.assignments: if addr.ip != dbhw_ent.primary_ip: continue # Special handling: if this interface was created automatically, # and there is exactly one other interface with no IP address, # then re-assign the primary address to that interface if not dbinterface.mac and dbinterface.comments is not None and \ dbinterface.comments.startswith("Created automatically") and \ len(dbhw_ent.interfaces) == 2: if dbinterface == dbhw_ent.interfaces[0]: other = dbhw_ent.interfaces[1] else: other = dbhw_ent.interfaces[0] if len(other.assignments) == 0: assign_address(other, dbhw_ent.primary_ip, dbhw_ent.primary_name.network) dbinterface.addresses.remove(dbhw_ent.primary_ip) raise _Goto # If this is a machine, it is possible to delete the host to get rid # of the primary name if dbhw_ent.hardware_type == "machine": msg = " You should delete the host first." else: msg = "" raise ArgumentError("{0} holds the primary address of the {1:cl}, " "therefore it cannot be deleted." "{2}".format(dbinterface, dbhw_ent, msg)) except _Goto: pass addrs = ", ".join(["%s: %s" % (addr.logical_name, addr.ip) for addr in dbinterface.assignments]) if addrs: raise ArgumentError("{0} still has the following addresses " "configured, delete them first: " "{1}.".format(dbinterface, addrs)) dbhw_ent.interfaces.remove(dbinterface) session.flush() if dbhw_ent.hardware_type == 'machine': plenary_info = PlenaryMachineInfo(dbhw_ent, logger=logger) plenary_info.write() return
def render( self, session, logger, interface, machine, mac, model, vendor, boot, pg, autopg, comments, master, clear_master, default_route, rename_to, **arguments ): """This command expects to locate an interface based only on name and machine - all other fields, if specified, are meant as updates. If the machine has a host, dsdb may need to be updated. The boot flag can *only* be set to true. This is mostly technical, as at this point in the interface it is difficult to tell if the flag was unset or set to false. However, it also vastly simplifies the dsdb logic - we never have to worry about a user trying to remove the boot flag from a host in dsdb. """ audit_results = [] dbhw_ent = Machine.get_unique(session, machine, compel=True) dbinterface = Interface.get_unique(session, hardware_entity=dbhw_ent, name=interface, compel=True) oldinfo = DSDBRunner.snapshot_hw(dbhw_ent) if arguments.get("hostname", None): # Hack to set an intial interface for an aurora host... dbhost = dbhw_ent.host if dbhost.archetype.name == "aurora" and dbhw_ent.primary_ip and not dbinterface.addresses: assign_address(dbinterface, dbhw_ent.primary_ip, dbhw_ent.primary_name.network) # We may need extra IP verification (or an autoip option)... # This may also throw spurious errors if attempting to set the # port_group to a value it already has. if pg is not None and dbinterface.port_group != pg.lower().strip(): dbinterface.port_group = verify_port_group(dbinterface.hardware_entity, pg) elif autopg: dbinterface.port_group = choose_port_group(session, logger, dbinterface.hardware_entity) audit_results.append(("pg", dbinterface.port_group)) if master: if dbinterface.addresses: # FIXME: as a special case, if the only address is the # primary IP, then we could just move it to the master # interface. However this can be worked around by bonding # the interface before calling "add host", so don't bother # for now. raise ArgumentError("Can not enslave {0:l} because it has " "addresses.".format(dbinterface)) dbmaster = Interface.get_unique(session, hardware_entity=dbhw_ent, name=master, compel=True) if dbmaster in dbinterface.all_slaves(): raise ArgumentError( "Enslaving {0:l} would create a circle, " "which is not allowed.".format(dbinterface) ) dbinterface.master = dbmaster if clear_master: if not dbinterface.master: raise ArgumentError("{0} is not a slave.".format(dbinterface)) dbinterface.master = None if comments: dbinterface.comments = comments if boot: # Should we also transfer the primary IP to the new boot interface? # That could get tricky if the new interface already has an IP # address... for i in dbhw_ent.interfaces: if i == dbinterface: i.bootable = True i.default_route = True else: i.bootable = False i.default_route = False if default_route is not None: dbinterface.default_route = default_route if not first_of(dbhw_ent.interfaces, lambda x: x.default_route): logger.client_info("Warning: {0:l} has no default route, hope " "that's ok.".format(dbhw_ent)) # Set this mac address last so that you can update to a bootable # interface *before* adding a mac address. This is so the validation # that takes place in the interface class doesn't have to be worried # about the order of update to bootable=True and mac address if mac: q = session.query(Interface).filter_by(mac=mac) other = q.first() if other and other != dbinterface: raise ArgumentError("MAC address {0} is already in use by " "{1:l}.".format(mac, other)) dbinterface.mac = mac if model or vendor: if not dbinterface.model_allowed: raise ArgumentError("Model/vendor can not be set for a {0:lc}.".format(dbinterface)) dbmodel = Model.get_unique(session, name=model, vendor=vendor, machine_type="nic", compel=True) dbinterface.model = dbmodel if rename_to: rename_interface(session, dbinterface, rename_to) session.flush() session.refresh(dbhw_ent) plenary_info = PlenaryMachineInfo(dbhw_ent, logger=logger) key = plenary_info.get_write_key() try: lock_queue.acquire(key) plenary_info.write(locked=True) if dbhw_ent.host and dbhw_ent.host.archetype.name != "aurora": dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.update_host(dbhw_ent, oldinfo) dsdb_runner.commit_or_rollback() except AquilonError, err: plenary_info.restore_stash() raise ArgumentError(err)
def render(self, session, logger, hostname, machine, auxiliary, interface, mac, comments, **arguments): if machine: dbmachine = Machine.get_unique(session, machine, compel=True) if hostname: dbhost = hostname_to_host(session, hostname) if machine and dbhost.machine != dbmachine: raise ArgumentError("Use either --hostname or --machine to " "uniquely identify a system.") dbmachine = dbhost.machine oldinfo = DSDBRunner.snapshot_hw(dbmachine) dbinterface = get_or_create_interface(session, dbmachine, name=interface, mac=mac, interface_type='public', bootable=False) # Multiple addresses will only be allowed with the "add interface # address" command addrs = ", ".join(["%s [%s]" % (addr.logical_name, addr.ip) for addr in dbinterface.assignments]) if addrs: raise ArgumentError("{0} already has the following addresses: " "{1}.".format(dbinterface, addrs)) audit_results = [] ip = generate_ip(session, logger, dbinterface, compel=True, audit_results=audit_results, **arguments) dbdns_rec, newly_created = grab_address(session, auxiliary, ip, comments=comments, preclude=True) if dbmachine.primary_name: # This command cannot use a non-default DNS environment, so no extra # checks are necessary dbdns_rec.reverse_ptr = dbmachine.primary_name.fqdn assign_address(dbinterface, ip, dbdns_rec.network) session.flush() plenary_info = PlenaryMachineInfo(dbmachine, logger=logger) key = plenary_info.get_write_key() try: lock_queue.acquire(key) plenary_info.write(locked=True) dsdb_runner = DSDBRunner(logger=logger) dsdb_runner.update_host(dbmachine, oldinfo) dsdb_runner.commit_or_rollback("Could not add host to DSDB") except: plenary_info.restore_stash() raise finally: lock_queue.release(key) if dbmachine.host: # XXX: Host needs to be reconfigured. pass for name, value in audit_results: self.audit_result(session, name, value, **arguments) return
def render(self, session, logger, hostname, machine, archetype, domain, sandbox, osname, osversion, buildstatus, personality, comments, zebra_interfaces, grn, eon_id, skip_dsdb_check=False, **arguments): dbarchetype = Archetype.get_unique(session, archetype, compel=True) section = "archetype_" + dbarchetype.name # This is for the various add_*_host commands if not domain and not sandbox: domain = self.config.get(section, "host_domain") (dbbranch, dbauthor) = get_branch_and_author(session, logger, domain=domain, sandbox=sandbox, compel=True) if hasattr(dbbranch, "allow_manage") and not dbbranch.allow_manage: raise ArgumentError("Adding hosts to {0:l} is not allowed." .format(dbbranch)) if not buildstatus: buildstatus = 'build' dbstatus = HostLifecycle.get_unique(session, buildstatus, compel=True) dbmachine = Machine.get_unique(session, machine, compel=True) oldinfo = DSDBRunner.snapshot_hw(dbmachine) if not personality: if self.config.has_option(section, "default_personality"): personality = self.config.get(section, "default_personality") else: personality = 'generic' dbpersonality = Personality.get_unique(session, name=personality, archetype=dbarchetype, compel=True) if not osname: if self.config.has_option(section, "default_osname"): osname = self.config.get(section, "default_osname") if not osversion: if self.config.has_option(section, "default_osversion"): osversion = self.config.get(section, "default_osversion") if not osname or not osversion: raise ArgumentError("Can not determine a sensible default OS " "for archetype %s. Please use the " "--osname and --osversion parameters." % (dbarchetype.name)) dbos = OperatingSystem.get_unique(session, name=osname, version=osversion, archetype=dbarchetype, compel=True) if (dbmachine.model.machine_type == 'aurora_node' and dbpersonality.archetype.name != 'aurora'): raise ArgumentError("Machines of type aurora_node can only be " "added with archetype aurora.") if dbmachine.host: raise ArgumentError("{0:c} {0.label} is already allocated to " "{1:l}.".format(dbmachine, dbmachine.host)) if grn or eon_id: dbgrn = lookup_grn(session, grn, eon_id, logger=logger, config=self.config) else: dbgrn = dbpersonality.owner_grn dbhost = Host(machine=dbmachine, branch=dbbranch, owner_grn=dbgrn, sandbox_author=dbauthor, personality=dbpersonality, status=dbstatus, operating_system=dbos, comments=comments) session.add(dbhost) if self.config.has_option("archetype_" + archetype, "default_grn_target"): dbhost.grns.append((dbhost, dbgrn, self.config.get("archetype_" + archetype, "default_grn_target"))) if zebra_interfaces: # --autoip does not make sense for Zebra (at least not the way it's # implemented currently) dbinterface = None else: dbinterface = get_boot_interface(dbmachine) # This method is allowed to return None. This can only happen # (currently) using add_aurora_host, add_windows_host, or possibly by # bypassing the aq client and posting a request directly. audit_results = [] ip = generate_ip(session, logger, dbinterface, audit_results=audit_results, **arguments) dbdns_rec, newly_created = grab_address(session, hostname, ip, allow_restricted_domain=True, allow_reserved=True, preclude=True) dbmachine.primary_name = dbdns_rec # Fix up auxiliary addresses to point to the primary name by default if ip: dns_env = dbdns_rec.fqdn.dns_environment for addr in dbmachine.all_addresses(): if addr.interface.interface_type == "management": continue if addr.service_address_id: # pragma: no cover continue for rec in addr.dns_records: if rec.fqdn.dns_environment == dns_env: rec.reverse_ptr = dbdns_rec.fqdn if zebra_interfaces: if not ip: raise ArgumentError("Zebra configuration requires an IP address.") dbsrv_addr = self.assign_zebra_address(session, dbmachine, dbdns_rec, zebra_interfaces) else: if ip: if not dbinterface: raise ArgumentError("You have specified an IP address for the " "host, but {0:l} does not have a bootable " "interface.".format(dbmachine)) assign_address(dbinterface, ip, dbdns_rec.network) dbsrv_addr = None session.flush() plenaries = PlenaryCollection(logger=logger) plenaries.append(Plenary.get_plenary(dbmachine)) if dbmachine.vm_container: plenaries.append(Plenary.get_plenary(dbmachine.vm_container)) if dbsrv_addr: plenaries.append(Plenary.get_plenary(dbsrv_addr)) key = plenaries.get_write_key() try: lock_queue.acquire(key) plenaries.write(locked=True) # XXX: This (and some of the code above) is horrible. There # should be a generic/configurable hook here that could kick # in based on archetype and/or domain. dsdb_runner = DSDBRunner(logger=logger) if dbhost.archetype.name == 'aurora': # For aurora, check that DSDB has a record of the host. if not skip_dsdb_check: try: dsdb_runner.show_host(hostname) except ProcessException, e: raise ArgumentError("Could not find host in DSDB: %s" % e) elif not dbmachine.primary_ip: logger.info("No IP for %s, not adding to DSDB." % dbmachine.fqdn)