Exemplo n.º 1
0
    def __init__(self,
                 if_addr,
                 if_net,
                 if_len,
                 ip_addr_range_bottom,
                 ip_addr_range_top,
                 mac_ip_map=None,
                 allow_unmapped=False,
                 debug=False,
                 ip_mode=4):
        assert ip_mode in (4, 6)
        ttbl.power.impl_c.__init__(self)
        ttbl.tt_power_control_impl.__init__(self)
        self.allow_unmapped = allow_unmapped
        if mac_ip_map == None:
            self._mac_ip_map = {}
        else:
            self._mac_ip_map = mac_ip_map

        # FIXME: move to power_on_do, to get this info from target's tags
        self._params = dict(
            ip_mode=ip_mode,
            tftp_prefix=tftp_prefix,
            if_net=if_net,
            if_addr=if_addr,
            if_len=if_len,
            ip_addr_range_bottom=ip_addr_range_bottom,
            ip_addr_range_top=ip_addr_range_top,
            dhcp_architecture_types=self._mk_pxe_arch_type_config(),
        )

        self.ip_mode = ip_mode
        if ip_mode == 4:
            self._params['if_netmask'] = commonl.ipv4_len_to_netmask_ascii(
                if_len)

        if allow_unmapped:
            self._params["allow_known_clients"] = "allow known clients;"
        else:
            self._params["allow_known_clients"] = "# all clients allowed"

        self.debug = debug
        self.log = None
        self.target = None  # we find this when power_*_do() is called
        self.state_dir = None
        self.pxe_dir = None
        self.dhcpd_pidfile = None
Exemplo n.º 2
0
Arquivo: dhcp.py Projeto: intel/tcf
    def __init__(self,
                 if_addr,
                 if_net,
                 if_len,
                 ip_addr_range_bottom,
                 ip_addr_range_top,
                 mac_ip_map = None,
                 allow_unmapped = False,
                 debug = False,
                 ip_mode = 4):
        assert ip_mode in (4, 6)
        ttbl.tt_power_control_impl.__init__(self)
        self.allow_unmapped = allow_unmapped
        if mac_ip_map == None:
            self._mac_ip_map = {}
        else:
            self._mac_ip_map = mac_ip_map

        # FIXME: move to power_on_do, to get this info from target's tags
        self._params = dict(
            ip_mode = ip_mode,
            tftp_prefix = tftp_prefix,
            if_net = if_net,
            if_addr = if_addr,
            if_len = if_len,
            ip_addr_range_bottom = ip_addr_range_bottom,
            ip_addr_range_top = ip_addr_range_top,
            dhcp_architecture_types = self._mk_pxe_arch_type_config(),
        )

        self.ip_mode = ip_mode
        if ip_mode == 4:
            self._params['if_netmask'] = commonl.ipv4_len_to_netmask_ascii(if_len)

        if allow_unmapped:
            self._params["allow_known_clients"] = "allow known clients;"
        else:
            self._params["allow_known_clients"] = "# all clients allowed"

        self.debug = debug
        self.log = None
        self.target = None	# we find this when power_*_do() is called
        self.state_dir = None
        self.pxe_dir = None
        self.dhcpd_pidfile = None
Exemplo n.º 3
0
    def eval(self, target):

        SANBOOT_URL = os.environ.get("SANBOOT_URL", None)
        if SANBOOT_URL == None:
            raise tcfl.tc.blocked_e("No SANBOOT_URL environment given")

        target.power.cycle()

        boot_ic = target.kws['pos_boot_interconnect']
        mac_addr = target.kws['interconnects'][boot_ic]['mac_addr']
        tcfl.biosl.boot_network_pxe(
            target,
            # Eg: UEFI PXEv4 (MAC:4AB0155F98A1)
            r"UEFI PXEv4 \(MAC:%s\)" % mac_addr.replace(":", "").upper().strip())

        # can't wait also for the "ok" -- debugging info might pop in th emiddle
        target.expect("iPXE initialising devices...")
        # if the connection is slow, we have to start sending Ctrl-B's
        # ASAP
        #target.expect(re.compile("iPXE .* -- Open Source Network Boot Firmware"))

        # send Ctrl-B to go to the PXE shell, to get manual control of iPXE
        #
        # do this as soon as we see the boot message from iPXE because
        # otherwise by the time we see the other message, it might already
        # be trying to boot pre-programmed instructions--we'll see the
        # Ctrl-B message anyway, so we expect for it.
        #
        # before sending these "Ctrl-B" keystrokes in ANSI, but we've seen
        # sometimes the timing window being too tight, so we just blast
        # the escape sequence to the console.
        target.console.write("\x02\x02")	# use this iface so expecter
        time.sleep(0.3)
        target.console.write("\x02\x02")	# use this iface so expecter
        time.sleep(0.3)
        target.console.write("\x02\x02")	# use this iface so expecter
        time.sleep(0.3)
        target.expect("Ctrl-B")
        target.console.write("\x02\x02")	# use this iface so expecter
        time.sleep(0.3)
        target.console.write("\x02\x02")	# use this iface so expecter
        time.sleep(0.3)
        target.expect("iPXE>")
        prompt_orig = target.shell.shell_prompt_regex
        try:
            #
            # When matching end of line, match against \r, since depends
            # on the console it will send one or two \r (SoL vs SSH-SoL)
            # before \n -- we removed that in the kernel driver by using
            # crnl in the socat config
            #
            # FIXME: block on anything here? consider infra issues
            # on "Connection timed out", http://ipxe.org...
            target.shell.shell_prompt_regex = "iPXE>"
            kws = dict(target.kws)
            boot_ic = target.kws['pos_boot_interconnect']
            ipv4_addr = target.kws['interconnects'][boot_ic]['ipv4_addr']
            ipv4_prefix_len = target.kws['interconnects'][boot_ic]['ipv4_prefix_len']
            kws['ipv4_netmask'] = commonl.ipv4_len_to_netmask_ascii(ipv4_prefix_len)

            if False: #dhcp:
                target.shell.run("dhcp", re.compile("Configuring.*ok"))
                target.shell.run("show net0/ip", "ipv4 = %s" % ipv4_addr)
            else:
                # static is much faster and we know the IP address already
                # anyway; but then we don't have DNS as it is way more
                # complicated to get it
                target.shell.run("set net0/ip %s" % ipv4_addr)
                target.shell.run("set net0/netmask %s" % kws['ipv4_netmask'])
                target.shell.run("ifopen")

            target.send("sanboot %s" % SANBOOT_URL)
            # can't use shell.run...it will timeout, since we'll print no more prompt
            target.expect("Booting from SAN device")
        finally:
            target.shell.shell_prompt_regex = prompt_orig
Exemplo n.º 4
0
def power_on_pre_pos_setup(target):

    pos_mode = target.fsdb.get("pos_mode")
    if pos_mode == None:
        target.log.info("POS boot: ignoring, pos_mode property not set")
        return
    # We only care if mode is set to pxe or local -- local makes us
    # tell the thing to go boot local disk
    if pos_mode != "pxe" and pos_mode != "local":
        target.log.info("POS boot: ignoring, pos_mode set to %s "
                        "(vs PXE or local)" % pos_mode)
        return

    boot_ic = target.tags.get('pos_boot_interconnect', None)
    if boot_ic == None:
        raise RuntimeError('no "pos_boot_interconnect" tag/property defined, '
                           'can\'t boot off network')
    if not boot_ic in target.tags['interconnects']:
        raise RuntimeError('this target does not belong to the '
                           'boot interconnect "%s" defined in tag '
                           '"pos_boot_interconnect"' % boot_ic)

    interconnect = target.tags['interconnects'][boot_ic]
    # FIXME: at some point, for ic-less POS-PXE boot we could get this
    # from pos_mac_addr and default to ic['mac_addr']
    mac_addr = interconnect['mac_addr']

    if pos_mode == "pxe":
        # now this is dirty -- we kinda hacking here but otherwise, how do
        # we get to the pos_* kws?
        ic = ttbl.config.targets[boot_ic]

        # The service
        kws = dict(target.tags)
        if not 'bsp' in target.tags:
            bsps = target.tags['bsps'].keys()
            kws['bsp'] = sorted(bsps)[0]
        kws.update(
            dict(
                ipv4_addr=interconnect['ipv4_addr'],
                ipv4_gateway=interconnect.get('ipv4_gateway', ""),
                ipv4_netmask=commonl.ipv4_len_to_netmask_ascii(
                    interconnect['ipv4_prefix_len']),
                mac_addr=mac_addr,
                name=target.id,
            ))

        # There might be a prefix to the path to the boot kernel and
        # initrd; we let the target override it and default to the
        # network's or nothing
        _tag_get_from_ic_target(kws, 'pos_http_url_prefix', ic, target)
        _tag_get_from_ic_target(kws, 'pos_nfs_server', ic, target)
        _tag_get_from_ic_target(kws, 'pos_nfs_path', ic, target)
        _tag_get_from_ic_target(kws, 'pos_image', ic, target, 'tcf-live')

        # generate configuration for the target to boot the POS's linux
        # kernel with the root fs over NFS
        kws['extra_kopts'] = " ".join(pos_cmdline_opts[kws['pos_image']])

        # Generate the PXE linux configuration
        #
        # note the syslinux/pxelinux format supports no long line
        # breakage, so we use Python's \ for clearer, shorter lines which
        # will be pasted all together
        #
        # Most of the juicy stuff comes from the pos_cmdline_opts for
        # each image (see, eg: tcf-live's) which is filled in
        # extra_opts a few lines above
        config = """\
say TCF Network boot to Service OS
#serial 0 115200
default boot
prompt 0
label boot
  # boot to %(pos_image)s
  linux %(pos_http_url_prefix)svmlinuz-%(pos_image)s
  append console=tty0 console=%(linux_serial_console_default)s,115200 \
    %(extra_kopts)s
"""
        config = template_rexpand(config, kws)
    else:
        # pos_mode is local
        config = """\
say TCF Network boot redirecting to local boot
serial 0 115200
default localboot
prompt 0
label localboot
  localboot 0
"""

    # Write the TFTP configuration  -- when the target boots and does
    # a DHCP request, DHCP daemon will send it a DHCP address and also
    # tell it to boot off TFTP with a pxelinux bootloader; it'll load
    # that bootloader, which will look for different configuration
    # files off TFTP_DIR/TFTP_PREFIX/pxelinux.cfg/ in a given order,
    # one of us being 01-ITSMACADDR
    #
    # It will find this one we are now making and boot to the POS for
    # provisioning or to local boot.

    tftp_config_file_name = os.path.join(
        tftp_dir,
        tftp_prefix,
        "pxelinux.cfg",
        # 01- is the ARP type 1 for ethernet; also note the PXE client
        # asks with the hex digits in lower case.
        "01-" + mac_addr.replace(":", "-").lower())
    with open(tftp_config_file_name, "w") as tf:
        tf.write(config)
        tf.flush()
        # We know the file exists, so it is safe to chmod like this
        os.chmod(tf.name, 0o644)
Exemplo n.º 5
0
    def _dhcp_conf_write(self, f):
        kws = dict(self._params)
        # generate DHCP configuration file based on hackish templating
        self.log.info(
            "%(if_name)s: IPv%(ip_mode)d addr/net/mask "
            "%(if_addr)s/%(if_net)s/%(if_len)s", self._params)
        if self.ip_mode == 4:
            # We only do PXE over ipv4
            # FIXME: make it so using pxelinux is a configuratio template
            # (likewise on the tftp side, so we can switch to EFI boot or
            # whatever we want)
            # %(dhcp_architecture_types)s is the output of
            # _mk_pxe_arch_type_config()
            f.write("""\
option space pxelinux;
option pxelinux.magic code 208 = string;
option pxelinux.configfile code 209 = text;
option pxelinux.pathprefix code 210 = text;
option pxelinux.reboottime code 211 = unsigned integer 32;
# To be used in the pxeclients class
option architecture-type code 93 = unsigned integer 16;

subnet %(if_net)s netmask %(if_netmask)s {
        pool {
                %(allow_known_clients)s
                range %(ip_addr_range_bottom)s  %(ip_addr_range_top)s;
        }
        class "pxeclients" {
                match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
                # http://www.syslinux.org/wiki/index.php?title=PXELINUX#UEFI
%(dhcp_architecture_types)s
                # Point to the TFTP server, which is the same as this
                next-server %(if_addr)s;
        }
}
""" % self._params)
        else:
            f.write("""\
# This one line must be outside any bracketed scope
option architecture-type code 93 = unsigned integer 16;

subnet6 %(if_net)s/%(if_len)s {
        range6 %(ip_addr_range_bottom)s %(ip_addr_range_top)s;

        class "pxeclients" {
                match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
                # http://www.syslinux.org/wiki/index.php?title=PXELINUX#UEFI
%(dhcp_architecture_types)s
                # Point to the TFTP server, which is the same as this
#                next-server %(if_addr)s;
        }
}
""" % self._params)

        # Now, enumerate the targets that are in this local
        # configuration and figure out what's their IP address in
        # this network; create a hardcoded entry for them.
        #
        # FIXME: This leaves a gap, as targets in other servers could
        # be connected to this network. Sigh.
        for target_id, target in ttbl.config.targets.iteritems():
            interconnects = target.tags.get('interconnects', {})
            ic = self.target

            boot_ic = target.tags.get('pos_boot_interconnect', None)
            if boot_ic == None:
                ic.log.info('%s: target has no "pos_boot_interconnect" '
                            'tag/property defined, ignoring' % target_id)
                continue
            # FIXME: these two checks shall be consistency done when
            # the target is being added
            if not boot_ic in target.tags['interconnects']:
                raise RuntimeError('%s: target does not belong to the '
                                   'boot interconnect "%s" defined in tag '
                                   '"pos_boot_interconnect"' %
                                   (target_id, boot_ic))
            if not boot_ic in ttbl.config.targets:
                raise RuntimeError('%s: this target\'s boot interconnect %s '
                                   'defined in "pos_boot_interconnect" tag '
                                   'is not available in this server' %
                                   (target_id, boot_ic))

            if not 'bsp' in target.tags:
                bsps = target.tags['bsps'].keys()
                kws['bsp'] = sorted(bsps)[0]
            kws.update(
                dict(
                    ipv4_gateway=ic.tags.get('ipv4_gateway', ""),
                    ipv4_netmask=commonl.ipv4_len_to_netmask_ascii(
                        ic.tags['ipv4_prefix_len']),
                    name=target.id,
                ))

            # There might be a prefix to the path to the boot kernel and
            # initrd; we let the target override it and default to the
            # network's or nothing
            # FIXME: need v6 nfs_server and http_url
            _tag_get_from_ic_target(kws, 'pos_http_url_prefix', ic, target)
            _tag_get_from_ic_target(kws, 'pos_nfs_server', ic, target)
            _tag_get_from_ic_target(kws, 'pos_nfs_path', ic, target)

            for ic_id, interconnect in interconnects.iteritems():
                if '#' in ic_id:
                    real_ic_id, instance = ic_id.split("#", 1)
                    kws['hostname'] = target.id + "-" + instance
                else:
                    real_ic_id = ic_id
                    kws['hostname'] = target.id
                if real_ic_id != self.target.id:
                    continue
                kws['mac_addr'] = interconnect.get('mac_addr', None)
                kws['ipv4_addr'] = interconnect.get('ipv4_addr', None)
                kws['ipv6_addr'] = interconnect.get('ipv6_addr', None)

                if self.ip_mode == 4:
                    config = """\
host %(hostname)s {
        hardware ethernet %(mac_addr)s;
        fixed-address %(ipv4_addr)s;
        option host-name "%(hostname)s";
        # note how we are forcing NFSv3, as it might default to v2
        # FIXME: parameter?
        # Also UDP, more resilient for our use and soft so we can
        # recover in some cases more easily
        option root-path "%(pos_nfs_server)s:%(pos_nfs_path)s,udp,soft,nfsvers=3";
}
"""
                else:
                    config = """\
host %(hostname)s {
        hardware ethernet %(mac_addr)s;
        fixed-address6 %(ipv6_addr)s;
        option host-name "%(hostname)s";
        # note how we are forcing NFSv3, as it might default to v2
        # FIXME: parameter?
        # Also UDP, more resilient for our use and soft so we can
        # recover in some cases more easily
        # FIXME: pos_nfs_server6?
        option root-path "%(pos_nfs_server)s:%(pos_nfs_path)s,udp,soft,nfsvers=3";
}
"""
                f.write(template_rexpand(config, kws))
Exemplo n.º 6
0
def power_on_pre_pos_setup(target):
    """
    Hook called before power on to setup TFTP to boot a target in
    Provisioning Mode

    The DHCP server started by :mod:`ttbl.dnsmasq` or :mod:`ttbl.dhcp`
    are configured to direct a target to PXE boot *syslinux*; this
    will ask the TFTP server for a config file for the target's MAC
    address.

    This function is called before powering on the target to create
    said configuration file; based on the value of the target's
    *pos_mode* property, a config file that boots the Provisioning OS
    or that redirects to the local disk will be created.
    """
    pos_mode = target.fsdb.get("pos_mode")
    if pos_mode == None:
        target.log.info("POS boot: ignoring, pos_mode property not set")
        return
    # We only care if mode is set to pxe or local -- local makes us
    # tell the thing to go boot local disk
    # if none, we assume go local
    if pos_mode != "pxe" and pos_mode != "local":
        pos_mode = "local"

    boot_ic = target.tags.get('pos_boot_interconnect', None)
    if boot_ic == None:
        raise RuntimeError("CONFIG ERROR: no 'pos_boot_interconnect'"
                           " tag defined, can't boot off network")
    if not boot_ic in target.tags['interconnects']:
        raise RuntimeError("CONFIG ERROR: this target does not belong to"
                           " the boot interconnect '%s' defined in tag "
                           "'pos_boot_interconnect'" % boot_ic)

    interconnect = target.tags['interconnects'][boot_ic]
    # FIXME: at some point, for ic-less POS-PXE boot we could get this
    # from pos_mac_addr and default to ic['mac_addr']
    mac_addr = interconnect['mac_addr']

    # we need the interconnect object to get some values
    ic = ttbl.test_target.get(boot_ic)

    if pos_mode == "local":
        # pos_mode is local
        config = """\
say TCF Network boot redirecting to local boot
serial 0 115200
default localboot
prompt 0
label localboot
  localboot
"""
    else:
        # PXE mode

        # get some values we need to generate the Syslinux config file

        kws = dict(target.tags)
        if not 'bsp' in target.tags:
            bsps = target.tags.get('bsps', {}).keys()
            if bsps:
                kws['bsp'] = sorted(bsps)[0]
        kws.update(
            dict(
                ipv4_addr=interconnect['ipv4_addr'],
                ipv4_gateway=interconnect.get('ipv4_gateway', ""),
                ipv4_netmask=commonl.ipv4_len_to_netmask_ascii(
                    interconnect['ipv4_prefix_len']),
                mac_addr=mac_addr,
                name=target.id,
            ))

        # There might be a prefix to the path to the boot kernel and
        # initrd; we let the target override it and default to the
        # network's or nothing
        tag_get_from_ic_target(kws, 'pos_http_url_prefix', ic, target)
        tag_get_from_ic_target(kws, 'pos_nfs_server', ic, target)
        tag_get_from_ic_target(kws, 'pos_nfs_path', ic, target)
        tag_get_from_ic_target(kws, 'pos_image', ic, target, 'tcf-live')

        # generate configuration for the target to boot the POS's linux
        # kernel with the root fs over NFS
        kws['extra_kopts'] = " ".join(pos_cmdline_opts[kws['pos_image']])

        # Generate the PXE linux configuration
        #
        # note the syslinux/pxelinux format supports no long line
        # breakage, so we use Python's \ for clearer, shorter lines which
        # will be pasted all together
        #
        # Most of the juicy stuff comes from the pos_cmdline_opts for
        # each image (see, eg: tcf-live's) which is filled in
        # extra_opts a few lines above
        #
        ## serial 0 115200
        config = """\
say TCF Network boot to Provisioning OS
default boot
prompt 0
label boot
  linux %(pos_http_url_prefix)svmlinuz-%(pos_image)s
  append console=tty0 console=%(linux_serial_console_default)s,115200 \
    %(extra_kopts)s
"""
        config = template_rexpand(config, kws)

    # Write the TFTP configuration  -- when the target boots and does
    # a DHCP request, DHCP daemon will send it a DHCP address and also
    # tell it to boot off TFTP with a pxelinux bootloader; it'll load
    # that bootloader, which will look for different configuration
    # files off TFTP_ROOT/pxelinux.cfg/ in a given order,
    # one of us being 01-ITSMACADDR
    #
    # It will find this one we are now making and boot to the POS for
    # provisioning or to local boot.

    tftp_root_ic = os.path.join(ic.state_dir, "tftp.root")
    if os.path.isdir(tftp_root_ic):
        # the configuration has one TFTP root per interconnect, drop
        # it there (eg: when using ttbl.dnsmasq)
        tftp_root = tftp_root_ic
        pxelinux_cfg_dir = os.path.join(tftp_root, "pxelinux.cfg")
    else:
        # One global TFTP (eg: when using the system's global tftp
        # daemon)
        tftp_prefix = "ttbd" + ttbl.config.instance_suffix
        pxelinux_cfg_dir = os.path.join(tftp_dir, tftp_prefix, "pxelinux.cfg")

    # config file is named 01-MACADDR
    tftp_config_file_name = os.path.join(
        pxelinux_cfg_dir,
        # 01- is the ARP type 1 for ethernet; also note the PXE client
        # asks with the hex digits in lower case.
        "01-" + mac_addr.replace(":", "-").lower())
    with open(tftp_config_file_name, "w") as tf:
        tf.write(config)
    # We know the file exists, so it is safe to chmod like this
    os.chmod(tftp_config_file_name, 0o644)
Exemplo n.º 7
0
Arquivo: dhcp.py Projeto: intel/tcf
def power_on_pre_pos_setup(target):

    pos_mode = target.fsdb.get("pos_mode")
    if pos_mode == None:
        target.log.info("POS boot: ignoring, pos_mode property not set")
        return
    # We only care if mode is set to pxe or local -- local makes us
    # tell the thing to go boot local disk
    if pos_mode != "pxe" and pos_mode != "local":
        target.log.info("POS boot: ignoring, pos_mode set to %s "
                        "(vs PXE or local)" % pos_mode)
        return

    boot_ic = target.tags.get('pos_boot_interconnect', None)
    if boot_ic == None:
        raise RuntimeError('no "pos_boot_interconnect" tag/property defined, '
                           'can\'t boot off network')
    if not boot_ic in target.tags['interconnects']:
        raise RuntimeError('this target does not belong to the '
                           'boot interconnect "%s" defined in tag '
                           '"pos_boot_interconnect"' % boot_ic)

    interconnect = target.tags['interconnects'][boot_ic]
    # FIXME: at some point, for ic-less POS-PXE boot we could get this
    # from pos_mac_addr and default to ic['mac_addr']
    mac_addr = interconnect['mac_addr']

    if pos_mode == "pxe":
        # now this is dirty -- we kinda hacking here but otherwise, how do
        # we get to the pos_* kws?
        ic = ttbl.config.targets[boot_ic]

        # The service
        kws = dict(target.tags)
        if not 'bsp' in target.tags:
            bsps = target.tags['bsps'].keys()
            kws['bsp'] = sorted(bsps)[0]
        kws.update(dict(
            ipv4_addr = interconnect['ipv4_addr'],
            ipv4_gateway = interconnect.get('ipv4_gateway', ""),
            ipv4_netmask = commonl.ipv4_len_to_netmask_ascii(
                interconnect['ipv4_prefix_len']),
            mac_addr = mac_addr,
            name = target.id,
        ))

        # There might be a prefix to the path to the boot kernel and
        # initrd; we let the target override it and default to the
        # network's or nothing
        _tag_get_from_ic_target(kws, 'pos_http_url_prefix', ic, target)
        _tag_get_from_ic_target(kws, 'pos_nfs_server', ic, target)
        _tag_get_from_ic_target(kws, 'pos_nfs_path', ic, target)

        # generate configuration for the target to boot the POS's linux
        # kernel with the root fs over NFS
        # FIXME: the name of the pos image and the command line extras
        # should go over configuration and the target's configuration
        # should be able to say which image it wants (defaulting everyone
        # to whichever).
        kws['extra_kopts'] = ""
        kws['pos_image'] = 'tcf-live'
        kws['root_dev'] = '/dev/nfs'
        # no 'single' so it force starts getty on different ports
        # nfsroot: note we defer to whatever we are given over DHCP
        kws['extra_kopts'] += \
            "initrd=%(pos_http_url_prefix)sinitramfs-%(pos_image)s " \
            "rd.live.image selinux=0 audit=0 ro " \
            "rd.luks=0 rd.lvm=0 rd.md=0 rd.dm=0 rd.multipath=0 " \
            "plymouth.enable=0 "

        # Generate the PXE linux configuration
        #
        # note the syslinux/pxelinux format supports no long line
        # breakage, so we use Python's \ for clearer, shorter lines which
        # will be pasted all together
        #
        # FIXME: move somewhere else more central?
        #
        # IP specification is needed so the kernel acquires an IP address
        # and can syslog/nfsmount, etc Note we know the fields from the
        # target's configuration, as they are pre-assigned
        #
        # ip=DHCP so we get always the same IP address and NFS root
        # info (in option root-path when writing the DHCP config file)
        config = """\
say TCF Network boot to Service OS
#serial 0 115200
default boot
prompt 0
label boot
  # boot to %(pos_image)s
  linux %(pos_http_url_prefix)svmlinuz-%(pos_image)s
  append console=tty0 console=%(linux_serial_console_default)s,115200 \
    ip=dhcp \
    root=%(root_dev)s %(extra_kopts)s
"""
        config = template_rexpand(config, kws)
    else:
        # pos_mode is local
        config = """\
say TCF Network boot redirecting to local boot
serial 0 115200
default localboot
prompt 0
label localboot
  localboot 0
"""

    # Write the TFTP configuration  -- when the target boots and does
    # a DHCP request, DHCP daemon will send it a DHCP address and also
    # tell it to boot off TFTP with a pxelinux bootloader; it'll load
    # that bootloader, which will look for different configuration
    # files off TFTP_DIR/TFTP_PREFIX/pxelinux.cfg/ in a given order,
    # one of us being 01-ITSMACADDR
    #
    # It will find this one we are now making and boot to the POS for
    # provisioning or to local boot.

    tftp_config_file_name = os.path.join(
        tftp_dir, tftp_prefix, "pxelinux.cfg",
        # 01- is the ARP type 1 for ethernet; also note the PXE client
        # asks with the hex digits in lower case.
        "01-" + mac_addr.replace(":", "-").lower())
    with open(tftp_config_file_name, "w") as tf:
        tf.write(config)
        tf.flush()
        # We know the file exists, so it is safe to chmod like this
        os.chmod(tf.name, 0o644)
Exemplo n.º 8
0
Arquivo: dhcp.py Projeto: intel/tcf
    def _dhcp_conf_write(self, f):
        kws = dict(self._params)
        # generate DHCP configuration file based on hackish templating
        self.log.info(
            "%(if_name)s: IPv%(ip_mode)d addr/net/mask "
            "%(if_addr)s/%(if_net)s/%(if_len)s", self._params)
        if self.ip_mode == 4:
            # We only do PXE over ipv4
            # FIXME: make it so using pxelinux is a configuratio template
            # (likewise on the tftp side, so we can switch to EFI boot or
            # whatever we want)
            # %(dhcp_architecture_types)s is the output of
            # _mk_pxe_arch_type_config()
            f.write("""\
option space pxelinux;
option pxelinux.magic code 208 = string;
option pxelinux.configfile code 209 = text;
option pxelinux.pathprefix code 210 = text;
option pxelinux.reboottime code 211 = unsigned integer 32;
# To be used in the pxeclients class
option architecture-type code 93 = unsigned integer 16;

subnet %(if_net)s netmask %(if_netmask)s {
        pool {
                %(allow_known_clients)s
                range %(ip_addr_range_bottom)s  %(ip_addr_range_top)s;
        }
        class "pxeclients" {
                match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
                # http://www.syslinux.org/wiki/index.php?title=PXELINUX#UEFI
%(dhcp_architecture_types)s
                # Point to the TFTP server, which is the same as this
                next-server %(if_addr)s;
        }
}
""" % self._params)
        else:
            f.write("""\
# This one line must be outside any bracketed scope
option architecture-type code 93 = unsigned integer 16;

subnet6 %(if_net)s/%(if_len)s {
        range6 %(ip_addr_range_bottom)s %(ip_addr_range_top)s;

        class "pxeclients" {
                match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
                # http://www.syslinux.org/wiki/index.php?title=PXELINUX#UEFI
%(dhcp_architecture_types)s
                # Point to the TFTP server, which is the same as this
#                next-server %(if_addr)s;
        }
}
""" % self._params)

        # Now, enumerate the targets that are in this local
        # configuration and figure out what's their IP address in
        # this network; create a hardcoded entry for them.
        #
        # FIXME: This leaves a gap, as targets in other servers could
        # be connected to this network. Sigh.
        for target_id, target in ttbl.config.targets.iteritems():
            interconnects = target.tags.get('interconnects', {})
            ic = self.target

            boot_ic = target.tags.get('pos_boot_interconnect', None)
            if boot_ic == None:
                ic.log.info('%s: target has no "pos_boot_interconnect" '
                            'tag/property defined, ignoring' % target_id)
                continue
            # FIXME: these two checks shall be consistency done when
            # the target is being added
            if not boot_ic in target.tags['interconnects']:
                raise RuntimeError('%s: target does not belong to the '
                                   'boot interconnect "%s" defined in tag '
                                   '"pos_boot_interconnect"'
                                   % (target_id, boot_ic))
            if not boot_ic in ttbl.config.targets:
                raise RuntimeError('%s: this target\'s boot interconnect %s '
                                   'defined in "pos_boot_interconnect" tag '
                                   'is not available in this server'
                                   % (target_id, boot_ic))

            if not 'bsp' in target.tags:
                bsps = target.tags['bsps'].keys()
                kws['bsp'] = sorted(bsps)[0]
            kws.update(dict(
                ipv4_gateway = ic.tags.get('ipv4_gateway', ""),
                ipv4_netmask = commonl.ipv4_len_to_netmask_ascii(
                    ic.tags['ipv4_prefix_len']),
                name = target.id,
            ))

            # There might be a prefix to the path to the boot kernel and
            # initrd; we let the target override it and default to the
            # network's or nothing
            # FIXME: need v6 nfs_server and http_url
            _tag_get_from_ic_target(kws, 'pos_http_url_prefix', ic, target)
            _tag_get_from_ic_target(kws, 'pos_nfs_server', ic, target)
            _tag_get_from_ic_target(kws, 'pos_nfs_path', ic, target)

            for ic_id, interconnect in interconnects.iteritems():
                if '#' in ic_id:
                    real_ic_id, instance = ic_id.split("#", 1)
                    kws['hostname'] = target.id + "-" + instance
                else:
                    real_ic_id = ic_id
                    kws['hostname'] = target.id
                if real_ic_id != self.target.id:
                    continue
                kws['mac_addr'] = interconnect.get('mac_addr', None)
                kws['ipv4_addr'] = interconnect.get('ipv4_addr', None)
                kws['ipv6_addr'] = interconnect.get('ipv6_addr', None)

                if self.ip_mode == 4:
                    config = """\
host %(hostname)s {
        hardware ethernet %(mac_addr)s;
        fixed-address %(ipv4_addr)s;
        option host-name "%(hostname)s";
        # note how we are forcing NFSv3, as it might default to v2
        # FIXME: parameter?
        # Also UDP, more resilient for our use and soft so we can
        # recover in some cases more easily
        option root-path "%(pos_nfs_server)s:%(pos_nfs_path)s,udp,soft,nfsvers=3";
}
"""
                else:
                    config = """\
host %(hostname)s {
        hardware ethernet %(mac_addr)s;
        fixed-address6 %(ipv6_addr)s;
        option host-name "%(hostname)s";
        # note how we are forcing NFSv3, as it might default to v2
        # FIXME: parameter?
        # Also UDP, more resilient for our use and soft so we can
        # recover in some cases more easily
        # FIXME: pos_nfs_server6?
        option root-path "%(pos_nfs_server)s:%(pos_nfs_path)s,udp,soft,nfsvers=3";
}
"""
                f.write(template_rexpand(config, kws))
Exemplo n.º 9
0
def power_on_pre_pos_setup(target):

    pos_mode = target.fsdb.get("pos_mode")
    if pos_mode == None:
        target.log.info("POS boot: ignoring, pos_mode property not set")
        return
    # We only care if mode is set to pxe or local -- local makes us
    # tell the thing to go boot local disk
    if pos_mode != "pxe" and pos_mode != "local":
        target.log.info("POS boot: ignoring, pos_mode set to %s "
                        "(vs PXE or local)" % pos_mode)
        return

    boot_ic = target.tags.get('pos_boot_interconnect', None)
    if boot_ic == None:
        raise RuntimeError('no "pos_boot_interconnect" tag/property defined, '
                           'can\'t boot off network')
    if not boot_ic in target.tags['interconnects']:
        raise RuntimeError('this target does not belong to the '
                           'boot interconnect "%s" defined in tag '
                           '"pos_boot_interconnect"' % boot_ic)

    interconnect = target.tags['interconnects'][boot_ic]
    # FIXME: at some point, for ic-less POS-PXE boot we could get this
    # from pos_mac_addr and default to ic['mac_addr']
    mac_addr = interconnect['mac_addr']

    if pos_mode == "pxe":
        # now this is dirty -- we kinda hacking here but otherwise, how do
        # we get to the pos_* kws?
        ic = ttbl.config.targets[boot_ic]

        # The service
        kws = dict(target.tags)
        if not 'bsp' in target.tags:
            bsps = target.tags['bsps'].keys()
            kws['bsp'] = sorted(bsps)[0]
        kws.update(
            dict(
                ipv4_addr=interconnect['ipv4_addr'],
                ipv4_gateway=interconnect.get('ipv4_gateway', ""),
                ipv4_netmask=commonl.ipv4_len_to_netmask_ascii(
                    interconnect['ipv4_prefix_len']),
                mac_addr=mac_addr,
                name=target.id,
            ))

        # There might be a prefix to the path to the boot kernel and
        # initrd; we let the target override it and default to the
        # network's or nothing
        _tag_get_from_ic_target(kws, 'pos_http_url_prefix', ic, target)
        _tag_get_from_ic_target(kws, 'pos_nfs_server', ic, target)
        _tag_get_from_ic_target(kws, 'pos_nfs_path', ic, target)

        # generate configuration for the target to boot the POS's linux
        # kernel with the root fs over NFS
        # FIXME: the name of the pos image and the command line extras
        # should go over configuration and the target's configuration
        # should be able to say which image it wants (defaulting everyone
        # to whichever).
        kws['extra_kopts'] = ""
        kws['pos_image'] = 'tcf-live'
        kws['root_dev'] = '/dev/nfs'
        # no 'single' so it force starts getty on different ports
        # nfsroot: note we defer to whatever we are given over DHCP
        kws['extra_kopts'] += \
            "initrd=%(pos_http_url_prefix)sinitramfs-%(pos_image)s " \
            "rd.live.image selinux=0 audit=0 ro " \
            "rd.luks=0 rd.lvm=0 rd.md=0 rd.dm=0 rd.multipath=0 " \
            "plymouth.enable=0 "

        # Generate the PXE linux configuration
        #
        # note the syslinux/pxelinux format supports no long line
        # breakage, so we use Python's \ for clearer, shorter lines which
        # will be pasted all together
        #
        # FIXME: move somewhere else more central?
        #
        # IP specification is needed so the kernel acquires an IP address
        # and can syslog/nfsmount, etc Note we know the fields from the
        # target's configuration, as they are pre-assigned
        #
        # ip=DHCP so we get always the same IP address and NFS root
        # info (in option root-path when writing the DHCP config file)
        config = """\
say TCF Network boot to Service OS
#serial 0 115200
default boot
prompt 0
label boot
  # boot to %(pos_image)s
  linux %(pos_http_url_prefix)svmlinuz-%(pos_image)s
  append console=tty0 console=%(linux_serial_console_default)s,115200 \
    ip=dhcp \
    root=%(root_dev)s %(extra_kopts)s
"""
        config = template_rexpand(config, kws)
    else:
        # pos_mode is local
        config = """\
say TCF Network boot redirecting to local boot
serial 0 115200
default localboot
prompt 0
label localboot
  localboot 0
"""

    # Write the TFTP configuration  -- when the target boots and does
    # a DHCP request, DHCP daemon will send it a DHCP address and also
    # tell it to boot off TFTP with a pxelinux bootloader; it'll load
    # that bootloader, which will look for different configuration
    # files off TFTP_DIR/TFTP_PREFIX/pxelinux.cfg/ in a given order,
    # one of us being 01-ITSMACADDR
    #
    # It will find this one we are now making and boot to the POS for
    # provisioning or to local boot.

    tftp_config_file_name = os.path.join(
        tftp_dir,
        tftp_prefix,
        "pxelinux.cfg",
        # 01- is the ARP type 1 for ethernet; also note the PXE client
        # asks with the hex digits in lower case.
        "01-" + mac_addr.replace(":", "-").lower())
    with open(tftp_config_file_name, "w") as tf:
        tf.write(config)
        tf.flush()
        # We know the file exists, so it is safe to chmod like this
        os.chmod(tf.name, 0o644)
Exemplo n.º 10
0
    def eval(self, target):

        if self.sanboot_url == None:
            SANBOOT_URL = os.environ.get("SANBOOT_URL", None)
            if SANBOOT_URL == None:
                raise tcfl.tc.blocked_e("No default sanboot_url programmed or"
                                        " SANBOOT_URL environment given")
            self.sanboot_url = SANBOOT_URL

        target.power.cycle()

        boot_ic = target.kws['pos_boot_interconnect']
        mac_addr = target.kws['interconnects'][boot_ic]['mac_addr']
        tcfl.biosl.boot_network_pxe(
            target,
            # Eg: UEFI PXEv4 (MAC:4AB0155F98A1)
            r"UEFI PXEv4 \(MAC:%s\)" %
            mac_addr.replace(":", "").upper().strip())

        # can't wait also for the "ok" -- debugging info might pop in th emiddle
        target.expect("iPXE initialising devices...")
        # if the connection is slow, we have to start sending Ctrl-B's
        # ASAP
        #target.expect(re.compile("iPXE .* -- Open Source Network Boot Firmware"))

        # send Ctrl-B to go to the PXE shell, to get manual control of iPXE
        #
        # do this as soon as we see the boot message from iPXE because
        # otherwise by the time we see the other message, it might already
        # be trying to boot pre-programmed instructions--we'll see the
        # Ctrl-B message anyway, so we expect for it.
        #
        # before sending these "Ctrl-B" keystrokes in ANSI, but we've seen
        # sometimes the timing window being too tight, so we just blast
        # the escape sequence to the console.
        target.console.write("\x02\x02")  # use this iface so expecter
        time.sleep(0.3)
        target.console.write("\x02\x02")  # use this iface so expecter
        time.sleep(0.3)
        target.console.write("\x02\x02")  # use this iface so expecter
        time.sleep(0.3)
        target.expect("Ctrl-B", timeout=250)
        target.console.write("\x02\x02")  # use this iface so expecter
        time.sleep(0.3)
        target.console.write("\x02\x02")  # use this iface so expecter
        time.sleep(0.3)
        target.expect("iPXE>")
        prompt_orig = target.shell.prompt_regex
        try:
            #
            # When matching end of line, match against \r, since depends
            # on the console it will send one or two \r (SoL vs SSH-SoL)
            # before \n -- we removed that in the kernel driver by using
            # crnl in the socat config
            #
            # FIXME: block on anything here? consider infra issues
            # on "Connection timed out", http://ipxe.org...
            target.shell.prompt_regex = "iPXE>"
            kws = dict(target.kws)
            boot_ic = target.kws['pos_boot_interconnect']
            mac_addr = target.kws['interconnects'][boot_ic]['mac_addr']
            ipv4_addr = target.kws['interconnects'][boot_ic]['ipv4_addr']
            ipv4_prefix_len = target.kws['interconnects'][boot_ic][
                'ipv4_prefix_len']
            kws['ipv4_netmask'] = commonl.ipv4_len_to_netmask_ascii(
                ipv4_prefix_len)

            # Find what network interface our MAC address is; the
            # output of ifstat looks like:
            #
            ## net0: 00:26:55:dd:4a:9d using 82571eb on 0000:6d:00.0 (open)
            ##   [Link:up, TX:8 TXE:1 RX:44218 RXE:44205]
            ##   [TXE: 1 x "Network unreachable (http://ipxe.org/28086090)"]
            ##   [RXE: 43137 x "Operation not supported (http://ipxe.org/3c086083)"]
            ##   [RXE: 341 x "The socket is not connected (http://ipxe.org/380f6093)"]
            ##   [RXE: 18 x "Invalid argument (http://ipxe.org/1c056082)"]
            ##   [RXE: 709 x "Error 0x2a654089 (http://ipxe.org/2a654089)"]
            ## net1: 00:26:55:dd:4a:9c using 82571eb on 0000:6d:00.1 (open)
            ##   [Link:down, TX:0 TXE:0 RX:0 RXE:0]
            ##   [Link status: Down (http://ipxe.org/38086193)]
            ## net2: 00:26:55:dd:4a:9f using 82571eb on 0000:6e:00.0 (open)
            ##   [Link:down, TX:0 TXE:0 RX:0 RXE:0]
            ##   [Link status: Down (http://ipxe.org/38086193)]
            ## net3: 00:26:55:dd:4a:9e using 82571eb on 0000:6e:00.1 (open)
            ##   [Link:down, TX:0 TXE:0 RX:0 RXE:0]
            ##   [Link status: Down (http://ipxe.org/38086193)]
            ## net4: 98:4f:ee:00:05:04 using NII on NII-0000:01:00.0 (open)
            ##   [Link:up, TX:10 TXE:0 RX:8894 RXE:8441]
            ##   [RXE: 8173 x "Operation not supported (http://ipxe.org/3c086083)"]
            ##   [RXE: 268 x "The socket is not connected (http://ipxe.org/380f6093)"]
            #
            # thus we need to match the one that fits our mac address
            ifstat = target.shell.run("ifstat", output=True, trim=True)
            regex = re.compile(
                "(?P<ifname>net[0-9]+): %s using" % mac_addr.lower(),
                re.MULTILINE)
            m = regex.search(ifstat)
            if not m:
                raise tcfl.tc.error_e(
                    "iPXE: cannot find interface name for MAC address %s;"
                    " is the MAC address in the configuration correct?" %
                    mac_addr.lower(),
                    dict(target=target,
                         ifstat=ifstat,
                         mac_addr=mac_addr.lower()))
            ifname = m.groupdict()['ifname']

            dhcp = bool(target.property_get("ipxe.dhcp", True))
            if dhcp:
                target.shell.run("dhcp " + ifname,
                                 re.compile("Configuring.*ok"))
                target.shell.run("show %s/ip" % ifname,
                                 "ipv4 = %s" % ipv4_addr)
            else:
                # static is much faster and we know the IP address already
                # anyway; but then we don't have DNS as it is way more
                # complicated to get it
                target.shell.run("set %s/ip %s" % (ifname, ipv4_addr))
                target.shell.run("set %s/netmask %s" %
                                 (ifname, kws['ipv4_netmask']))
                target.shell.run("ifopen " + ifname)

            if self.sanboot_url == "skip":
                target.report_info("not booting", level=0)
            else:
                target.send("sanboot %s" % self.sanboot_url)
                # can't use shell.run...it will timeout, since we'll print no more prompt
                # ESXi would print now...
                #target.expect("Booting from SAN device")
        finally:
            target.shell.prompt_regex = prompt_orig