Exemplo n.º 1
0
    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 []
Exemplo n.º 2
0
    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))
Exemplo n.º 3
0
def test_get_provisioning_nic_no_prov_nic(node):
    with pytest.raises(NicNotFound):
        get_provisioning_nic(node)
Exemplo n.º 4
0
def test_get_provisioning_nic(node_with_nics):
    nic = get_provisioning_nic(node_with_nics)

    assert nic

    assert nic.ip == '192.168.0.4'
Exemplo n.º 5
0
    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)
Exemplo n.º 6
0
    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