def __getPXEFiles(self, dbNode): ''' Returns a list of fully-qualified paths to the PXE file(s) associated with the given nodename. ''' try: # Find the first nic marked as bootable nic = get_provisioning_nic(dbNode) if not nic.mac: return [] return [ self.__getPxelinuxBootFilePath(nic.mac), self.__get_kickstart_file_path(dbNode) ] except NicNotFound: return []
def removeDhcpLease(self, node) -> NoReturn: # Find first provisioning NIC try: nic = get_provisioning_nic(node) except NicNotFound: return self.getLogger().debug( 'Removing DHCP lease for node [%s] MAC [%s]' % ( node.name, nic.mac)) dhcpName = self._getDhcpNodeName(node, nic) cmds = dedent("""\ connect new host set name = "{name}" set hardware-address = {mac} set hardware-type = 1 set ip-address = {ip} open remove """ ) cmds = cmds.format(name=dhcpName, mac=nic.mac, ip=nic.ip) p = subprocess.Popen(['/usr/bin/omshell'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding='utf-8') stdout, stderr = p.communicate(cmds) if p.poll() != 0: self.getLogger().error( 'Error removing DHCP lease for node [%s] (retval=%d): %s' % ( node.name, p.returncode, stdout))
def test_get_provisioning_nic_no_prov_nic(node): with pytest.raises(NicNotFound): get_provisioning_nic(node)
def test_get_provisioning_nic(node_with_nics): nic = get_provisioning_nic(node_with_nics) assert nic assert nic.ip == '192.168.0.4'
def writePXEFile(self, node, localboot=None, hardwareprofile=None, softwareprofile=None): # 'hardwareProfile', 'softwareProfile', and 'localboot' are # overrides. If not specified, node.hardwareprofile, # node.softwareprofile, and node.bootFrom values are used # respectively. hwprofile = hardwareprofile if hardwareprofile else \ node.hardwareprofile swprofile = softwareprofile if softwareprofile else \ node.softwareprofile localboot = bool(localboot) \ if localboot is not None else bool(node.bootFrom) self.getLogger().debug( 'writePXEFile(): node=[%s], hwprofile=[%s],' ' swprofile=[%s], localboot=[%s]' % ( node.name, hwprofile.name, swprofile.name, localboot)) provisioningNics = hwprofile.nics osFamilyInfo = swprofile.os.family # Find the first nic marked as bootable try: nic = get_provisioning_nic(node) except NicNotFound: # Node does not have a nic marked as bootable. return result = "# PXE boot configuration for %s\n" % (node.name) if localboot: result += """ default localdisk prompt 0 label localdisk """ # Use a sane default localBootParams = hwprofile.localBootParams \ if hwprofile.localBootParams else \ "kernel chain.c32;append hd0" for localBootParam in localBootParams.split(';'): result += " %s\n" % (localBootParam) else: result += """\ default Reinstall prompt 0 label Reinstall """ installerIp = provisioningNics[0].ip if hwprofile.installType == 'package': # Default pxelinux.cfg for package-based installations # Find the best IP address to use ksurl = 'http://%s:%d/kickstarts/%s' % ( installerIp, self._cm.getIntWebPort(), self.__get_kickstart_file_name(node)) # Call the external support module try: osSupport = __import__( 'tortuga.os.%s.osSupport' % (osFamilyInfo.name), globals(), locals(), ['osSupport'], 0) if not hasattr(osSupport, 'OSSupport'): self.getLogger().error( 'Invalid OS support module for [%s]' % ( osFamilyInfo.name)) return result += osSupport.OSSupport( osFamilyInfo).getPXEReinstallSnippet( ksurl, node, hardwareprofile=hwprofile, softwareprofile=swprofile) + '\n' except ImportError: self.getLogger().warning( 'OS support module not found for [%s]' % ( osFamilyInfo.name)) else: bootParams = getBootParameters(hwprofile, swprofile) result += ''' kernel %s append initrd=%s hostname=%s %s''' % (bootParams['kernel'], bootParams['initrd'], node.name, bootParams['kernelParams']) # Write file contents filename = self.__getPxelinuxBootFilePath(nic.mac) current_euid = os.geteuid() current_egid = os.getegid() try: # The PXE file needs to be owned by the 'apache' user, so the # WS API can update it. os.setegid(self.passdata.pw_gid) os.seteuid(self.passdata.pw_uid) fp = os.open( filename, os.O_CREAT | os.O_TRUNC | os.O_WRONLY, 0o644) os.write(fp, result.encode()) os.close(fp) finally: os.seteuid(current_euid) os.setegid(current_egid) if hwprofile.installType == 'package': # Now write out the kickstart file self._writeKickstartFile(node, hwprofile, swprofile) # Write 'cloud-init' configuration self.write_other_boot_files(node, hwprofile, swprofile)
def __dhcp_discovery(self, addNodesRequest, dbSession, dbHardwareProfile, dbSoftwareProfile): # Listen for DHCP requests if not dbHardwareProfile.nics: raise CommandFailed( 'Hardware profile [%s] does not have a provisioning' ' NIC defined' % (dbHardwareProfile.name)) newNodes = [] deviceName = addNodesRequest['deviceName'] \ if 'deviceName' in addNodesRequest else None nodeCount = addNodesRequest['count'] \ if 'count' in addNodesRequest else 0 bGenerateIp = dbHardwareProfile.location != 'remote' # Obtain platform-specific packet capture subprocess object addHostManager = osUtility.getOsObjectFactory().getOsAddHostManager() deviceName = dbHardwareProfile.nics[0].networkdevice.name p1 = addHostManager.dhcpCaptureSubprocess(deviceName) if nodeCount: self.getLogger().debug( 'Adding [%s] new %s' % (nodeCount, 'nodes' if nodeCount > 1 else 'node')) self.looping = True index = 0 # Node count was not specified, so discover DHCP nodes # until manually aborted by user. msg = 'Waiting for new node...' if not nodeCount else \ 'Waiting for new node #1 of %d...' % (nodeCount) try: while self.looping: # May not need this... dataReady = select.select([p1.stdout], [], [], 5) if not dataReady[0]: continue line = p1.stdout.readline() if not line: self.getLogger().debug( 'DHCP packet capture process ended... exiting') break self.getLogger().debug('Read line "%s" len=%s' % (line, len(line))) mac = addHostManager.getMacAddressFromCaptureEntry(line) if not mac: continue self.getLogger().debug('Discovered MAC address [%s]' % (mac)) if self.__is_duplicate_mac(mac, newNodes): # Ignore DHCP request from known MAC self.getLogger().debug( 'MAC address [%s] is already known' % (mac)) continue addNodeRequest = {} if 'rack' in addNodesRequest: addNodeRequest['rack'] = addNodesRequest['rack'] # Get nics based on hardware profile networks addNodeRequest['nics'] = initialize_nics( dbHardwareProfile.nics[0], dbHardwareProfile.hardwareprofilenetworks, mac) # We may be trying to create the same node for the # second time so we'll ignore errors try: node = self.nodeApi.createNewNode(None, addNodeRequest, dbHardwareProfile, dbSoftwareProfile, bGenerateIp=bGenerateIp) except NodeAlreadyExists as ex: existingNodeName = ex.args[0] self.getLogger().debug('Node [%s] already exists' % (existingNodeName)) continue except MacAddressAlreadyExists: self.getLogger().debug('MAC address [%s] already exists' % (mac)) continue except IpAlreadyExists as ex: self.getLogger().debug('IP address already in use by node' ' [%s]: %s' % (existingNodeName, ex)) continue # Add the newly created node to the session dbSession.add(node) # Create DHCP/PXE configuration self.writeLocalBootConfiguration(node, dbHardwareProfile, dbSoftwareProfile) index += 1 # Use first provisioning nic nic = get_provisioning_nic(node) try: msg = 'Added node [%s] IP [%s]' % (node.name, nic.ip) if nic.mac: msg += ' MAC [%s]' % (nic.mac) self.getLogger().info(msg) except Exception as ex: self.getLogger().exception('Error setting status message') self._pre_add_host(node.name, dbHardwareProfile.name, dbSoftwareProfile.name, nic.ip) newNodes.append(node) if nodeCount > 0: nodeCount -= 1 if not nodeCount: self.looping = False except Exception as msg: self.getLogger().exception('DHCP discovery failed') try: os.kill(p1.pid, signal.SIGKILL) os.waitpid(p1.pid, 0) except Exception: self.getLogger().exception('Error killing network capture process') # This is a necessary evil for the time being, until there's # a proper context manager implemented. self.addHostApi.clear_session_nodes(newNodes) return newNodes