示例#1
0
    def evemu_create_device(self, did, descriptor):
        """
        Create a new input device

        :param str did: device id, or name of the device (eg: *mouse
          1*, *keyboard main*, *left joytick*...)

        :param str descriptor: input device descriptor, as described
          by Linux's *evemu-describe* (without the comments); see
          :const:`descriptor_mouse`, :const:`descriptor_kbd`.
        """
        # we'll evolve this into being able to create multiple mice of
        # different implementations (eg: evemu based vs hardware
        # based)...because we might want to type working with multiple mice
        assert isinstance(did, basestring), \
            "did is type %s; expected string" % type(did)
        descriptor = descriptor.strip()
        if not isinstance(did, basestring) \
            or not descriptor_valid_regex.search(descriptor.strip()):
            raise tc.error_e(
                "did %s: descriptor (%s) is not a string or it does not"
                " match the expected format in"
                " tcfl.target_ext_input.descriptor_valid_regex" %
                (did, type(descriptor)), dict(descriptor=descriptor))

        target = self.target
        # Copy the device descriptor to the device, using SSH if
        # possible (faster)
        if hasattr(target, 'ssh') and target.ssh.tunnel_up():
            with open(os.path.join(target.testcase.tmpdir, "input-dev.desc"),
                      "w") as f:
                f.write(descriptor)
                f.flush()
                target.ssh.copy_to(f.name, "/tmp/input-dev.desc")
        else:
            target.shell.string_copy_to_file(descriptor, "/tmp/input-dev.desc")
        output = target.shell.run("%s /tmp/input-dev.desc & sleep 1s" %
                                  self.evemu_device,
                                  output=True,
                                  trim=True)
        # this prints
        ## NAME: /dev/input/eventX, so get it
        input_regex = re.compile(
            r"(?P<name>[^:]+\s*)(?P<devname>\/dev\/input\/event.*)$")
        m = input_regex.search(output)
        if not m:
            raise tc.error_e(
                "can't locate /dev/input/event* name in output to"
                " create new input device", dict(output=output))
        # take only the basename; /dev/input will be added by the FIFO
        # itself, it is less chars to print
        self.devices[did] = os.path.basename(m.groupdict()['devname'])
    def run(self, timeout=None):
        """\
        Run the expectation loop on the testcase until all
        expectations pass or the timeout is exceeded.

        :param int timeout: (optional) maximum time to wait for all
          expectations to be met (defaults to
          :attr:`tcfl.expecter.expecter_c.timeout`)
        """
        if timeout == None:
            timeout = self._timeout
        else:
            assert isinstance(timeout, int)
        # Fresh start
        self.buffers.clear()
        self.ts0 = time.time()
        _active_ts = self.ts0
        functors_pending = self.functors
        while True:
            ts = time.time()
            td = ts - self.ts0
            _functors_pending = list(functors_pending)

            for f, a, has_to_pass in _functors_pending:
                if a == None:
                    args = (self, )
                else:
                    args = (self, ) + a
                try:
                    self._log("running %s %s @%s" %
                              (f.__name__, f.origin, args),
                              dlevel=5)
                    _ts0 = time.time()
                    r = f(*args)
                    _ts1 = time.time()
                except tc.pass_e as e:
                    if has_to_pass:
                        # Report pass information, including attachments
                        tc.result_c.report_from_exception(self.testcase, e)
                        r = True

                # Once it passes, we don't check it anymore
                if has_to_pass and r != None:
                    self.remove(f, a)
                    # if this returned normally or raised a tc.pass_e,
                    # then it means the test passed; except if it
                    # returns None, then ignore it (might have
                    # been a poller)
                    if self.have_to_pass == 0:
                        raise tc.pass_e("all expectations found")

            # Check like this because we want to run the check
            # functions before determining a timeout as some of them
            # might also check on their own and offer better messages
            if td >= self.timeout:
                raise tc.error_e("Timed out (%.2fs)" % self.timeout)
            if ts - _active_ts > self.active_period:
                self.testcase._targets_active()
                _active_ts = ts
            time.sleep(self._poll_period)
示例#3
0
文件: pos_uefi.py 项目: intel/tcf
def _efibootmgr_output_parse(target, output):

    boot_order_match = _boot_order_regex.search(output)
    if not boot_order_match:
        raise tc.error_e("can't extract boot order",
                         attachments = dict(target = target, output = output))
    boot_order = boot_order_match.groupdict()['boot_order'].split(',')

    entry_matches = re.findall(_entry_regex, output)
    # returns a list of [ ( HHHH, ENTRYNAME ) ], HHHH hex digits, all str

    boot_entries = []
    for entry in entry_matches:
        if _name_is_pos_boot(entry[1]):
            section = 0		# POS (PXE, whatever), boot first
        elif _name_is_local_boot(entry[1]):
            section = 10	# LOCAL, boot after
        else:
            section = 20	# others, whatever
        try:
            boot_index = boot_order.index(entry[0])
            boot_entries.append(( entry[0], entry[1], section, boot_index ))
        except ValueError:
            # if the entry is not in the boot order, that is fine,
            # ignore it
            pass

    return boot_order, boot_entries
示例#4
0
文件: expecter.py 项目: intel/tcf
    def run(self, timeout = None):
        """\
        Run the expectation loop on the testcase until all
        expectations pass or the timeout is exceeded.

        :param int timeout: (optional) maximum time to wait for all
          expectations to be met (defaults to
          :attr:`tcfl.expecter.expecter_c.timeout`)
        """
        if timeout == None:
            timeout = self._timeout
        else:
            assert isinstance(timeout, int)
        # Fresh start
        self.buffers.clear()
        self.ts0 = time.time()
        _active_ts = self.ts0
        functors_pending = self.functors
        while True:
            ts = time.time()
            td = ts - self.ts0
            _functors_pending = list(functors_pending)

            for f, a, has_to_pass in _functors_pending:
                if a == None:
                    args = (self,)
                else:
                    args = (self, ) + a
                try:
                    self._log(
                        "running %s %s @%s" % (f.__name__, f.origin, args),
                        dlevel = 5)
                    _ts0 = time.time()
                    r = f(*args)
                    _ts1 = time.time()
                except tc.pass_e as e:
                    if has_to_pass:
                        # Report pass information, including attachments
                        tc.result_c.report_from_exception(self.testcase, e)
                        r = True

                # Once it passes, we don't check it anymore
                if has_to_pass and r != None:
                    self.remove(f, a)
                    # if this returned normally or raised a tc.pass_e,
                    # then it means the test passed; except if it
                    # returns None, then ignore it (might have
                    # been a poller)
                    if self.have_to_pass == 0:
                        raise tc.pass_e("all expectations found")

            # Check like this because we want to run the check
            # functions before determining a timeout as some of them
            # might also check on their own and offer better messages
            if td >= self.timeout:
                raise tc.error_e("Timed out (%.2fs)" % self.timeout)
            if ts - _active_ts > self.active_period:
                self.testcase._targets_active()
                _active_ts = ts
            time.sleep(self._poll_period)
示例#5
0
def _efibootmgr_output_parse(target, output):

    boot_order_match = _boot_order_regex.search(output)
    if not boot_order_match:
        raise tc.error_e("can't extract boot order",
                         attachments=dict(target=target, output=output))
    boot_order = boot_order_match.groupdict()['boot_order'].split(',')

    entry_matches = re.findall(_entry_regex, output)
    # returns a list of [ ( HHHH, ENTRYNAME ) ], HHHH hex digits, all str

    boot_entries = []
    for entry in entry_matches:
        if _name_is_pos_boot(entry[1]):
            section = 0  # POS (PXE, whatever), boot first
        elif _name_is_tcf_local_boot(entry[1]):
            section = 05  # TCF local boots, always first so we
            # can control
        elif _name_is_local_boot(entry[1]):
            section = 10  # LOCAL, boot after
        else:
            section = 20  # others, whatever
        try:
            boot_index = boot_order.index(entry[0])
            boot_entries.append((entry[0], entry[1], section, boot_index))
        except ValueError:
            # if the entry is not in the boot order, that is fine,
            # ignore it
            pass

    return boot_order, boot_entries
示例#6
0
    def check_filter(self, _objdir, arch, board, _filter, origin=None):
        """
        This is going to be called by the App Builder's build function
        to evaluate if we need to filter out a build of a testcase. In
        any other case, it will be ignored.

        :param str objdir: name of the Zephyr's object directory
          where to find configuration files
        :param str arch: name of the architecture we are building
        :param str board: Zephyr's board we are building
        :param str _filter: Zephyr's sanity Check style filter
          expression
        :param str origin: where does this come from?
        """
        if not origin:
            origin = commonl.origin_get()

        if _filter == None or _filter == "":
            return

        self.target.report_info("filter: processing '%s' @%s" %
                                (_filter, origin),
                                dlevel=1)
        self.target.report_info("filter: reading defconfig", dlevel=2)

        _defconfig = self.config_file_read()
        defconfig = {}
        for key, value in _defconfig.iteritems():
            # The testcase.ini filter language doesn't prefix the
            # CONFIG_ stuff, so we are going to strip it with [7:]
            if key.startswith("CONFIG_"):
                defconfig[key[7:]] = value
            # The testcase.yaml filter language prefixes with
            # CONFIG_ stuff, so we don't strip it
            defconfig[key] = value
        self.target.report_info("filter: evaluating", dlevel=2)
        try:
            res = commonl.expr_parser.parse(_filter, defconfig)
            self.target.report_info("filter: evaluated defconfig: %s" % res,
                                    dlevel=1)
            if res == False:
                raise tc.skip_e("filter '%s' @ %s causes TC to be skipped" %
                                (_filter, origin))
            else:
                self.target.report_info(
                    "filter '%s' @ %s causes TC to be continued" %
                    (_filter, origin),
                    dlevel=2)
        except SyntaxError as se:
            raise tc.error_e("filter: failed processing '%s' @ %s: %s" %
                             (_filter, origin, se))
示例#7
0
 def _device_get(self, did):
     if did in self.devices:
         return self.devices[did]
     # if this is an evemu-device (syntehtic) that is already
     # running but we don't really know about it, let's try to find
     # it. File /tmp/evemu-DID.log was created
     output = self.target.shell.run("cat /tmp/evemu-%s.log || true" % did,
                                    output=True,
                                    trim=True)
     eventX = self._evemu_device_log_extract(output)
     if eventX:
         self.devices[did] = eventX
         return eventX
     raise tc.error_e("INPUT: device ID %s does not exist" % did)
示例#8
0
文件: app_zephyr.py 项目: intel/tcf
    def check_filter(self, _objdir, arch, board, _filter, origin = None):
        """
        This is going to be called by the App Builder's build function
        to evaluate if we need to filter out a build of a testcase. In
        any other case, it will be ignored.

        :param str objdir: name of the Zephyr's object directory
          where to find configuration files
        :param str arch: name of the architecture we are building
        :param str board: Zephyr's board we are building
        :param str _filter: Zephyr's sanity Check style filter
          expression
        :param str origin: where does this come from?
        """
        if not origin:
            origin = commonl.origin_get()

        if _filter == None or _filter == "":
            return

        self.target.report_info("filter: processing '%s' @%s"
                                % (_filter, origin), dlevel = 1)
        self.target.report_info("filter: reading defconfig", dlevel = 2)

        _defconfig = self.config_file_read()
        defconfig = {}
        for key, value in _defconfig.iteritems():
            # The testcase.ini filter language doesn't prefix the
            # CONFIG_ stuff, so we are going to strip it with [7:]
            if key.startswith("CONFIG_"):
                defconfig[key[7:]] = value
            # The testcase.yaml filter language prefixes with
            # CONFIG_ stuff, so we don't strip it
            defconfig[key] = value
        self.target.report_info("filter: evaluating", dlevel = 2)
        try:
            res = commonl.expr_parser.parse(_filter, defconfig)
            self.target.report_info("filter: evaluated defconfig: %s" % res,
                                    dlevel = 1)
            if res == False:
                raise tc.skip_e("filter '%s' @ %s causes TC to be skipped"
                                % (_filter, origin))
            else:
                self.target.report_info(
                    "filter '%s' @ %s causes TC to be continued"
                    % (_filter, origin), dlevel = 2)
        except SyntaxError as se:
            raise tc.error_e("filter: failed processing '%s' @ %s: %s"
                             % (_filter, origin, se))
示例#9
0
    def setup(self, console = None):
        """
        Setup the shell for scripting operation

        In the case of a bash shell, this:
        - sets the prompt to something easer to latch on to
        - disables command line editing
        - traps errors in shell execution
        """
        target = self.target
        testcase = target.testcase
        # dereference which console we are using; we don't want to
        # setup our trackers below to the "default" console and leave
        # it floating because then it will get confused if we switch
        # default consoles.
        console = target.console._console_get(console)
        self.run('export PS1="TCF-%s:$PS1"' % self.target.kws['tc_hash'],
                 console = console)
        # disable line editing for proper recording of command line
        # when running bash; otherwise the scrolling readline does
        # messes up the output
        self.run('test ! -z "$BASH" && set +o vi +o emacs',
                 console = console)
        # Trap the shell to complain loud if a command fails, and catch it
        # See that '' in the middle, is so the catcher later doesn't
        # get tripped by the command we sent to set it up
        self.run("trap 'echo ERROR''-IN-SHELL' ERR",
                 console = console)
        testcase.expect_global_append(
            # add a detector for a shell error, make sure to name it
            # after the target and console it will monitor so it
            # doesn't override other targets/consoles we might be
            # touching in parallel
            target.console.text(
                "ERROR-IN-SHELL",
                name = "%s:%s: shell error" % (target.want_name, console),
                console = console, timeout = 0, poll_period = 1,
                raise_on_found = tc.error_e("error detected in shell")),
            # if we have already added detectors for this, that's
            # fine, ignore them
            skip_duplicate = True
        )
示例#10
0
def _efibootmgr_output_parse(target, output):
    boot_order_match = _boot_order_regex.search(output)
    if not boot_order_match:
        raise tc.error_e("can't extract boot order",
                         attachments=dict(target=target, output=output))
    boot_order = boot_order_match.groupdict()['boot_order'].split(',')
    entry_matches = re.findall(_entry_regex, output)
    # returns a list of [ ( HHHH, ENTRYNAME ) ], HHHH hex digits, all str
    target.report_info("POS/UEFI: found %d EFI boot entries" %
                       len(entry_matches),
                       dlevel=3,
                       attachments=dict(boot_entries=entry_matches))
    boot_to_pos = target.pos.capabilities.get('boot_to_pos', None)
    boot_entries = []
    for entry in entry_matches:
        if _name_is_pos_boot(entry[1], boot_to_pos):
            section = 0  # POS (PXE, whatever), boot first
        elif _name_is_tcf_local_boot(entry[1], boot_to_pos):
            section = 05  # TCF local boots, always first so we
            # can control
        elif _name_is_local_boot(entry[1], boot_to_pos):
            section = 10  # LOCAL, boot after
        else:
            section = 20  # others, whatever
        try:
            boot_index = boot_order.index(entry[0])
            # FIXME: should keep this list sorted?
            boot_entries.append((entry[0], entry[1], section, boot_index))
        except ValueError:
            # if the entry is not in the boot order, that is fine,
            # ignore it
            pass
    target.report_info(
        "POS/UEFI: sorted %d EFI boot entries with boot_to_pos method '%s'" %
        (len(entry_matches), boot_to_pos),
        dlevel=1,
        alevel=0,
        attachments=dict(sorted_boot_entries=boot_entries))
    return boot_order, boot_entries
示例#11
0
文件: pos_uefi.py 项目: intel/tcf
def boot_config_multiroot(target, boot_dev, image):
    """
    Configure the target to boot using the multiroot
    """
    boot_dev = target.kws['pos_boot_dev']
    # were we have mounted the root partition
    root_dir = "/mnt"

    linux_kernel_file, linux_initrd_file, linux_options = \
        _linux_boot_guess(target, image)
    if linux_kernel_file == None:
        raise tc.blocked_e(
            "Cannot guess a Linux kernel to boot",
            dict(target = target))
    # remove absolutization (some specs have it), as we need to copy from
    # mounted filesystems
    if os.path.isabs(linux_kernel_file):
        linux_kernel_file = linux_kernel_file[1:]
    if linux_initrd_file and os.path.isabs(linux_initrd_file):
        linux_initrd_file = linux_initrd_file[1:]

    if linux_options == None or linux_options == "":
        target.report_info("WARNING! can't figure out Linux cmdline "
                           "options, taking defaults")
        # below we'll add more stuff
        linux_options = "console=tty0 root=SOMEWHERE"

    # MULTIROOT: indicate which image has been flashed to this
    # partition
    # saved by pos_multiroot.mountfs
    root_part_dev = target.root_part_dev
    root_part_dev_base = os.path.basename(root_part_dev)
    target.property_set('pos_root_' + root_part_dev_base, image)
    # /boot EFI system partition is always /dev/DEVNAME1 (first
    # partition), we partition like that
    # FIXME: we shouldn't harcode this
    boot_part_dev = boot_dev + target.kws['p_prefix'] + "1"

    kws = dict(
        boot_dev = boot_dev,
        boot_part_dev = boot_part_dev,
        root_part_dev = root_part_dev,
        root_part_dev_base = root_part_dev_base,
        root_dir = root_dir,
        linux_kernel_file = linux_kernel_file,
        linux_kernel_file_basename = os.path.basename(linux_kernel_file),
        linux_initrd_file = linux_initrd_file,
        linux_options = linux_options,
    )
    if linux_initrd_file:
        kws['linux_initrd_file_basename'] = os.path.basename(linux_initrd_file)
    else:
        kws['linux_initrd_file_basename'] = None

    kws.update(target.kws)

    if linux_options:
        #
        # Maybe mess with the Linux boot options
        #
        target.report_info("linux cmdline options: %s" % linux_options)
        # FIXME: can this come from config?
        linux_options_replace = {
            # we want to use hard device name rather than LABELS/UUIDs, as
            # we have reformated and those will have changed
            "root": "/dev/%(root_part_dev_base)s" % kws,
            # we have created this in pos_multiroot and will only
            # replace it if the command line option is present.
            "resume": "/dev/disk/by-label/tcf-swap",
        }

        # FIXME: can this come from config?
        # We harcode a serial console on the device where we know the
        # framework is listening
        linux_options_append = \
            "console=%(linux_serial_console_default)s,115200n8" % kws

        linux_options_append += " " + target.rt.get('linux_options_append', "")

        linux_options += " " + linux_options_append

        for option, value in linux_options_replace.iteritems():
            regex = re.compile(r"\b" + option + r"=\S+")
            if regex.search(linux_options):
                linux_options = re.sub(
                    regex,
                    option + "=" + linux_options_replace[option],
                    linux_options)
            else:
                linux_options += " " + option + "=" + value

        kws['linux_options'] = linux_options
        target.report_info("linux cmdline options (modified): %s"
                           % linux_options)

    # Now generate the UEFI system partition that will boot the
    # system; we always override it, so we don't need to decide if it
    # is corrupted or whatever; we'll mount it in /boot (which now is
    # the POS /boot)

    #
    # Mount the /boot fs
    #
    # Try to assume it is ok, try to repair it if not; rsync the
    # kernels in there, it is faster for repeated operation/
    #
    target.report_info("POS/EFI: checking %(boot_part_dev)s" % kws)
    output = target.shell.run(
        "fsck.fat -aw /dev/%(boot_part_dev)s || true" % kws,
        output = True, trim = True)

    # FIXME: parse the output to tell if there was a problem; when bad
    # but recovered, we'll see
    #
    # 0x41: Dirty bit is set. Fs was not properly unmounted and some data may be corrupt.
    #  Automatically removing dirty bit.
    # Performing changes.
    # /dev/sda1: 11 files, 4173/261372 clusters

    # When ok
    #
    ## $ sudo fsck.vfat -wa /dev/nvme0n1p1
    ## fsck.fat 4.1 (2017-01-24)
    ## /dev/sda1: 39 files, 2271/33259 clusters

    # When really hosed it won't print the device line, so we look for
    # that
    #
    ## $ fsck.vfat -wa /dev/trash
    ## fsck.fat 4.1 (2017-01-24)
    ## Logical sector size (49294 bytes) is not a multiple of the physical sector size.
    good_regex = re.compile("^/dev/%(boot_part_dev)s: [0-9]+ files, "
                            "[0-9]+/[0-9]+ clusters$" % kws, re.MULTILINE)
    if not good_regex.search(output):
        target.report_info(
            "POS/EFI: /dev/%(boot_part_dev)s: formatting EFI "
            "filesystem, fsck couldn't fix it"
            % kws, dict(output = output))
        target.shell.run("mkfs.fat -F32 /dev/%(boot_part_dev)s; sync" % kws)
    target.report_info(
        "POS/EFI: /dev/%(boot_part_dev)s: mounting in /boot" % kws)
    target.shell.run(" mount /dev/%(boot_part_dev)s /boot; "
                     " mkdir -p /boot/loader/entries " % kws)

    # Do we have enough space? if not, remove the oldest stuff that is
    # not the file we are looking for
    # This prints
    ## $ df --output=pcent /boot
    ## Use%
    ##   6%
    output = target.shell.run("df --output=pcent /boot", output = True)
    regex = re.compile(r"^\s*(?P<percent>[\.0-9]+)%$", re.MULTILINE)
    match = regex.search(output)
    if not match:
        raise tc.error_e("Can't determine the amount of free space in /boot",
                         dict(output = output))
    used_space = float(match.groupdict()['percent'])
    if used_space > 75:
        target.report_info(
            "POS/EFI: /dev/%(boot_part_dev)s: freeing up space" % kws)
        # List files in /boot, sort by last update (when we rsynced
        # them)
        ## 2018-10-29+08:48:48.0000000000	84590	/boot/EFI/BOOT/BOOTX64.EFI
        ## 2018-10-29+08:48:48.0000000000	84590	/boot/EFI/systemd/systemd-bootx64.efi
        ## 2019-05-14+13:25:06.0000000000	7192832	/boot/vmlinuz-4.12.14-110-default
        ## 2019-05-14+13:25:08.0000000000	9688340	/boot/initrd-4.12.14-110-default
        ## 2019-05-14+13:25:14.0000000000	224	/boot/loader/entries/tcf-boot.conf
        ## 2019-05-14+13:25:14.0000000000	54	/boot/loader/loader.conf
        output = target.shell.run(
            # that double \\ needed so the shell is the one using it
            # as a \t, not python converting to a sequence
            "find /boot/ -type f -printf '%T+\\t%s\\t%p\\n' | sort",
            output = True, trim = True)
        # delete the first half entries over 300k except those that
        # match the kernels we are installing
        to_remove = []
        _linux_initrd_file = kws.get("linux_initrd_file", "%%NONE%%")
        if linux_initrd_file == None:
            _linux_initrd_file = "%%NONE%%"
        for line in output.splitlines():
            _timestamp, size_s, path = line.split(None, 3)
            size = int(size_s)
            if size > 300 * 1024 \
               and not kws['linux_kernel_file_basename'] in path \
               and not _linux_initrd_file in path:
                to_remove.append(path)
        # get older half and wipe it--this means the first half, as we
        # sort from older to newer
        to_remove = to_remove[:len(to_remove)//2]
        for path in to_remove:
            # we could do them in a single go, but we can exceed the
            # command line length -- lazy to optimize it
            target.shell.run("rm -f %s" % path)

    # Now copy all the files needed to boot to the root of the EFI
    # system partition mounted in /boot; remember they are in /mnt/,
    # our root partition
    target.shell.run(
        "time -p rsync --force --inplace /mnt/%(linux_kernel_file)s"
        " /boot/%(linux_kernel_file_basename)s" % kws)
    if kws.get("linux_initrd_file", None):
        target.shell.run(
            "time -p rsync --force --inplace /mnt/%(linux_initrd_file)s"
            " /boot/%(linux_initrd_file_basename)s" % kws)
    # we are the only one who cuts the cod here (yeah, direct Spanish
    # translation for the reader's entertainment), and if not wipe'm to
    # eternity; otherwise systemd will boot something prolly in
    # alphabetical order, not what we want
    target.shell.run("/usr/bin/rm -rf /boot/loader/entries/*.conf")
    # remember paths to the bootloader are relative to /boot
    # merge these two
    tcf_boot_conf = """\
cat <<EOF > /boot/loader/entries/tcf-boot.conf
title TCF-driven local boot
linux /%(linux_kernel_file_basename)s
options %(linux_options)s
"""
    if kws.get("linux_initrd_file", None):
        tcf_boot_conf += "initrd /%(linux_initrd_file_basename)s\n"
    tcf_boot_conf += "EOF\n"
    target.shell.run(tcf_boot_conf % kws)

    # Install new -- we wiped the /boot fs new anyway; if there are
    # multiple options already, bootctl shall be able to handle it.
    # Don't do variables in the any casewe will poke with them later
    # on anyway. Why? Because there is space for a race condition that
    # will leave us with the system booting off the localdisk vs the
    # network for PXE--see efibootmgr_setup()
    target.shell.run("bootctl update --no-variables"
                     " || bootctl install --no-variables;"
                     " sync")

    # Now mess with the EFIbootmgr
    # FIXME: make this a function and a configuration option (if the
    # target does efibootmgr)
    _efibootmgr_setup(target, boot_dev, 1)
    # umount only if things go well
    # Shall we try to unmount in case of error? nope, we are going to
    # have to redo the whole thing anyway, so do not touch it, in case
    # we are jumping in for manual debugging
    target.shell.run("umount /dev/%(boot_part_dev)s" % kws)
示例#12
0
    def up(self,
           tempt=None,
           user=None,
           login_regex=re.compile('login:'******'[Pp]assword:'),
           shell_setup=True,
           timeout=120):
        """Wait for the shell in a console to be ready

        Giving it ample time to boot, wait for a :data:`shell prompt
        <shell_prompt_regex>` and set up the shell so that if an
        error happens, it will print an error message and raise a
        block exception. Optionally login as a user and password.

        >>> target.shell.up(user = '******', password = '******')

        :param str tempt: (optional) string to send before waiting for
          the loging prompt (for example, to send a newline that
          activates the login)

        :param str user: (optional) if provided, it will wait for
          *login_regex* before trying to login with this user name.

        :param str password: (optional) if provided, and a password
          prompt is found, send this password.

        :param str login_regex: (optional) if provided (string
          or compiled regex) and *user* is provided, it will wait for
          this prompt before sending the username.

        :param str password_regex: (optional) if provided (string
          or compiled regex) and *password* is provided, it will wait for
          this prompt before sending the password.

        :param int delay_login: (optional) wait this many seconds
          before sending the user name after finding the login prompt.

        :param bool shell_setup: (optional, default) setup the shell
          up by disabling command line editing (makes it easier for
          the automation) and set up hooks that will raise an
          exception if a shell command fails.

        :param int timeout: [optional] seconds to wait for the login
          prompt to appear

        """
        assert tempt == None or isinstance(tempt, basestring)
        assert user == None or isinstance(user, basestring)
        assert isinstance(login_regex, (basestring, re._pattern_type))
        assert delay_login >= 0
        assert password == None or isinstance(password, basestring)
        assert isinstance(password_regex, (basestring, re._pattern_type))
        assert isinstance(shell_setup, bool)
        assert timeout > 0

        target = self.target

        def _login(target):
            # If we have login info, login to get a shell prompt
            target.expect(login_regex)
            if delay_login:
                target.report_info("Delaying %ss before login in" %
                                   delay_login)
                time.sleep(delay_login)
            target.send(user)
            if password:
                target.expect(password_regex)
                target.send(password)

        try:
            original_timeout = self.target.testcase.tls.expecter.timeout
            self.target.testcase.tls.expecter.timeout = timeout
            if tempt:
                tries = 0
                while tries < self.target.testcase.tls.expecter.timeout:
                    try:
                        self.target.send(tempt)
                        if user:
                            _login(self.target)
                        target.expect(self.linux_prompt_regex)
                        break
                    except tc.error_e as _e:
                        if tries == self.target.testcase.tls.expecter.timeout:
                            raise tc.error_e(
                                "Waited too long (%ds) for shell to come up "
                                "(did not receive '%s')" %
                                (self.target.testcase.tls.expecter.timeout,
                                 self.shell_prompt_regex.pattern))
                        continue
            else:
                if user:
                    _login(self.target)
                target.expect(self.shell_prompt_regex)
        finally:
            self.target.testcase.tls.expecter.timeout = original_timeout

        if shell_setup:
            #
            self.run('export PS1="TCF-%s:$PS1"' % target.kws['tc_hash'])
            # disable line editing for proper recording of command line
            # when running bash; otherwise the scrolling readline does
            # messes up the output
            self.run('test ! -z "$BASH" && set +o vi +o emacs')
            # Trap the shell to complain loud if a command fails, and catch it
            # See that '' in the middle, is so the catcher later doesn't
            # get tripped by the command we sent to set it up
            self.run("trap 'echo ERROR''-IN-SHELL' ERR")
            self.target.on_console_rx("ERROR-IN-SHELL",
                                      result='errr',
                                      timeout=False)
示例#13
0
 def _device_get(self, did):
     if did not in self.devices:
         raise tc.error_e("INPUT: device ID %s does not exist" % did)
     return self.devices[did]
示例#14
0
    def sequence_send(self, sequence):
        """
        Send a sequence of events to one or more devices

        :param str sequence: sequence of events to send to input
          devices as a string.

          No syntax verification is done in the sequence string, it is
          assumed correct. Syntax verification is done for the list
          version of the sequence.

        :param list sequence: sequence of events to send as a list of
          individual events.

          Each event is a tuple in the forms:

            >>> ( 'WAIT', FLOAT )
            >>> ( DEVNAME, EVTYPE, CODE, VALUE[, 'SYNC' ] )

          - *DEVNAME*: device name to send it to; for *evemu* devices
            this will be converted in the target to
            */dev/input/DEVNAME*

          - *EVTYPE*: type of event (as supported by the hardware):

            - *EV_SYN*: synchronizatione vent
            - *EV_KEY*: press a key (eg: keyboard)
            - *EV_REL*: relative movement (eg: mouse)
            - *EV_ABS*: absolute movement (eg: touchpad)
            - *EV_MSC*: miscellaneous events
            - *EV_SW*: set a switch
            - *EV_LED*: set a led
            - *EV_SND*: play a sound
            - *EV_REP*: set repetition
            - *EV_FF*: set force feedback
            - *EV_PWR*: set power
            - *EV_FF_STATUS*: force feedback status

          - *CODE*: depends on the event type, which indicates the
             action being taken

            - *KEY_\**: press/release keys (eg: *KEY_A*, *KEY_B*...
            - *BTN_\**: press/release buttons (eg: *BTN_LEFT*, *BTN_RIGHT*...)
            - *ABS_\**: set absolute axes (eg: *ABS_X*, *ABS_Y*)
            - *SW_\**: miscelaneous switches
            - *MSC_\**: miscellaneous settings
            - *SND_{CLICK,BELL,TONE}*: play a sound
            - *REP_{DELAY,PERIOD}*: set repetition configuration
            - *LED_\**: set different leds

          - *VALUE*: integer value that depends on the *EVTYPE* and
             *CODE*; for example to press a key:

            - 1: press key/button
            - 0: release key/button

        See
        https://www.kernel.org/doc/html/latest/input/event-codes.html
        for more information.

        Examples:

        - press and release key *A*, holding key for 0.1 seconds:

          >>> [
          >>>     ( DEVNAME, 'EV_KEY', 'KEY_A', 1, 'SYNC') ,
          >>>     ( 'WAIT', 0.1 ),
          >>>     ( DEVNAME, 'EV_KEY', 'KEY_A', 0, 'SYNC')
          >>> ]

        - set a touchpad with 100,100 range to the center of its range
          and double click left button

          >>> [
          >>>     ( DEVNAME, 'EV_ABS', 'ABS_X', 50') ,
          >>>     ( DEVNAME, 'EV_ABS', 'ABS_Y', 50')
          >>>     ( DEVNAME, 'EV_KEY', 'BTN_LEFT', 1, 'SYNC')
          >>>     ( 'WAIT', 0.1 ),
          >>>     ( DEVNAME, 'EV_KEY', 'BTN_LEFT', 0, 'SYNC')
          >>>     ( 'WAIT', 0.3 ),
          >>>     ( DEVNAME, 'EV_KEY', 'BTN_LEFT', 1, 'SYNC')
          >>>     ( 'WAIT', 0.1 ),
          >>>     ( DEVNAME, 'EV_KEY', 'BTN_LEFT', 0, 'SYNC')
          >>> ]

        - Type the *@* sign (there is no keycode for it, must press
          shift + plus *2* then release both):

          >>> [
          >>>     ( DEVNAME, 'EV_KEY', 'KEY_LEFTSHIFT', 1 ) ,
          >>>     ( DEVNAME, 'EV_KEY', 'KEY_2', 1, 'SYNC' ) ,
          >>>     ( 'WAIT', 0.1 ),
          >>>     ( DEVNAME, 'EV_KEY', 'KEY_A', 0, 'SYNC' )
          >>>     ( DEVNAME, 'EV_KEY', 'KEY_LEFTSHIFT', 0 ) ,
          >>> ]

        """
        if isinstance(sequence, basestring):
            # send it first to a file, then in a single shot to the
            # FIFO; this ensures the timing on sending the sequence
            # over the serial port (which can be slow if there is a
            # lot of data) does not influence the sequence's timing.
            self.target.shell.run("cat > /tmp/evemu.data\n%s\x04" % sequence)
            self.target.shell.run("cat /tmp/evemu.data > /tmp/evemu.fifo")
            return

        # expect it to be a list of tuples
        count = -1
        self.target.send("cat > /tmp/evemu.data")
        for entry in sequence:
            count += 1
            assert isinstance(entry, (list, tuple))
            devcmd = entry[0]
            if devcmd == "WAIT":
                try:
                    _ = float(entry[1])
                    self.target.send("WAIT %s\n" % entry[1])
                except ValueError as e:
                    raise tc.error_e("INPUT sequence #%d: WAIT:"
                                     " missing or invalid seconds field;"
                                     " can't convert to float: %s" %
                                     (count, e))
                self.target.console.write("WAIT %s\n" % entry[1])
                continue
            if len(entry) != 4 and len(entry) == 5:
                raise tc.error_e("INPUT sequence #%d: event:"
                                 " invalid number of entries;"
                                 " got %d, expect 4 or 5 " %
                                 (count, len(entry)))
            evtype = entry[1]
            if self.evtype_regex_valid.search(evtype):
                raise tc.error_e(
                    "INPUT sequence #%d: event type '%s'"
                    " invalid; must match %s" %
                    (count, evtype, self.evtype_regex_valid.pattern))
            code = entry[2]
            if self.code_regex_valid.search(code):
                raise tc.error_e("INPUT sequence #%d: code '%s'"
                                 " invalid; must match %s" %
                                 (count, code, self.code_regex_valid.pattern))
            value = entry[3]
            if self.value_regex_valid.search(value):
                raise tc.error_e(
                    "INPUT sequence #%d: code '%s'"
                    " invalid; must match %s" %
                    (count, value, self.value_regex_valid.pattern))
            if len(entry) == 5:
                sync = entry[4]
                if sync != "SYNC":
                    raise tc.error_e("INPUT sequence #%d: 5th field '%s'"
                                     " invalid; can only be 'SYNC'" %
                                     (count, sync))
            else:
                sync = ""
            self.target.send("%s %s %s %s\n" % evtype, code, value, sync)
        # complete the cat command we started above
        self.target.send("\x04" % sequence)
        self.target.shell.run("cat /tmp/evemu.data > /tmp/evemu.fifo")
示例#15
0
    def evemu_target_setup(self, ic):
        """
        Setup a target to use the input subsystem with EVEMU

        - creates a simple keyboard
        - creates a simple absolute mouse

        This can be used only on Linux machines with uinput support
        and will drive the mouse via commands in the console.

        Requirements:

        - The server must have been configured to drop
          *evemu.bin.tar.gz* in the web server for test content; thus
          a client accessing
          *http://SERVERIP/ttbd-images-misc/evemu.bin.tar.gz* will be
          able to get this content. The server install instructions
        provide for this to be the case.
        """
        assert isinstance(ic, tc.target_c)
        target = self.target

        # Download the new evemu binaries with statically linked
        # libraries.
        output = target.shell.run("evemu-event --fifo || true", output=True)
        # if evemu-event is installed, it will print it's help
        ## Usage: evemu-event [--sync] <device> --type <type> --code <code> --value <value>
        #
        # if the --fifo extensions are installed, there will be a
        # --fifo in there
        if '<device>' in output:
            # evemu-event is installed
            self.evemu_event = "evemu-event"
            self.evemu_device = "evemu-device"
            if '--fifo' in output:
                # evemu-event has --fifo support
                self.evemu_event_fifo = "evemu-event --fifo="
                target.report_info("INPUT/evemu: distro's with --fifo")
            else:
                # upload helper
                with msgid_c():
                    target.shell.string_to_file(
                        # Each line is in the format:
                        #
                        # - <DEVICE> <TYPE> <CODE> <VALUE> [SYNC]
                        # - WAIT <MILLISECS>
                        # - empty (ignored)
                        #
                        # the helper is a wee hack since it has more
                        # overhead with evemu with the support
                        """\
    #! /bin/bash
    rm -f $1; mkfifo $1
    tail -f $1 | while read dev typetime code value sync; do
        [ "$dev" == "WAIT" ] && sleep $typetime && continue
        [ "${sync:-}" == SYNC ] && sync="--sync"
        echo evemu-event ${sync:-} /dev/input/$dev --type $type --code $code --value $value
    done
    """,
                        "/usr/local/bin/evemu-event-fifo")
                    target.shell.run(
                        "chmod a+x /usr/local/bin/evemu-event-fifo")
                self.evemu_event_fifo = "/usr/local/bin/evemu-event-fifo "
                target.report_info(
                    "INPUT/evemu: distro's with FIFO shell helper")

        else:
            with msgid_c():
                # There is no evemu in the system, so let's upload our
                # semistatic build from the POS cache.
                rsync_server = target.kws.get(
                    'pos_rsync_server', ic.kws.get('pos_rsync_server', None))
                if rsync_server == None:
                    raise tc.error_e(
                        "INPUT/evemu: there is no place where to download"
                        " evemu.bin.tar.gz for, need the"
                        " target or interconnect to export"
                        " *pos_rsync_server* with the location")
                http_server = "http://" \
                    + rsync_server.replace("::images", "/ttbd-images-misc")
                target.shell.run("curl --noproxy '*' -sk %s/evemu.bin.tar.gz"
                                 " --output /tmp/evemu.bin.tar.gz" %
                                 http_server)
                target.shell.run(
                    "tar xvvf /tmp/evemu.bin.tar.gz --overwrite -C /")
            self.evemu_event = "/opt/evemu/bin/evemu-event"
            self.evemu_device = "/opt/evemu/bin/evemu-device"
            self.evemu_event_fifo = "/opt/evemu/bin/evemu-event --fifo="
            target.report_info("INPUT/evemu: TCF's static build w/ --fifo")

        self.evemu_create_device("default_mouse", descriptor_mouse)
        self.evemu_create_device("default_keyboard", descriptor_kbd)
        # start the FIFO pipe
        target.shell.run("nohup %s/tmp/evemu.fifo >& /tmp/evemu.log &" %
                         self.evemu_event_fifo)
示例#16
0
文件: app_zephyr.py 项目: intel/tcf
    def configure(testcase, target, app_src):
        target.kws_required_verify([ 'zephyr_board', 'zephyr_kernelname' ])
        # Adds Zephyr's build zephyr_* vars to the target's keywords
        testcase_file = inspect.getfile(type(testcase))
        zephyr_extra_args = testcase._targets[target.want_name]['kws'].get(
            'app_zephyr_options', "") + " "
        if len(app_src) > 1:
            zephyr_extra_args += " ".join(app_src[1:])
        # Add tags to the target
        srcdir = tcfl.app.get_real_srcdir(testcase_file, app_src[0])
        # Decide if this is cmake style (Zephyr almost 1.10) or not
        # and store it in the keywords, as we'll use in different
        # places to make decissions on if we can run or not.
        ZEPHYR_BASE = os.environ.get('ZEPHYR_BASE',
                                     "__ZEPHYR_BASE_not_defined__")
        if os.path.exists(os.path.join(ZEPHYR_BASE, "CMakeLists.txt")):
            if not os.path.exists(os.path.join(srcdir, "CMakeLists.txt")):
                raise tc.error_e(
                    "%s: Zephyr App is not cmake based, but Zephyr @%s is"
                    % (srcdir, ZEPHYR_BASE))
            is_cmake = True
            # cmake needs the extra args in -DXXX format .. sighs this
            # will blow up at some point, because we are assuming that
            # all extra args passed are VAR=VALUE
            zephyr_extra_args = " ".join("-D" + arg
                                         for arg in zephyr_extra_args.split())
        else:
            if os.path.exists(os.path.join(srcdir, "CMakeLists.txt")):
                raise tc.error_e(
                    "%s: Zephyr App is cmake based, but Zephyr @%s is not"
                    % (srcdir, ZEPHYR_BASE))
            is_cmake = False
        target.kws_set(
            {
                'zephyr_srcdir': srcdir,
                # Place all the build output in the temp directory, to
                # avoid polluting the source directories
                'zephyr_objdir': os.path.join(
                    testcase.tmpdir,
                    "outdir-%(tc_hash)s-%(tg_hash)s-%(zephyr_board)s"
                    % target.kws),
                'zephyr_is_cmake': is_cmake,
            },
            bsp = target.bsp)

        if not target.bsp in target.bsps_stub:
            # Set arguments only for this BSP, not for all of them,
            # and only if we are not building a stub. Note we get the
            # options from app_zephyr_options + OPT1 + OPT2... added
            # after app_src in 'app_zephyr = (SRC, OPT1, OPT2...)
            target.kw_set('zephyr_extra_args', zephyr_extra_args,
                          bsp = target.bsp)
        else:
            # When we are building stubs, we do not take the options
            # from the project itself -- stub needs no options
            target.kw_set('zephyr_extra_args', "", bsp = target.bsp)


        # Do we introduce boot configuration or not?
        if not testcase.build_only:
            # If a testcase is build only, it won't run in HW so we
            # don't need to introduce a boot delay to allow serial
            # ports to be ready.
            # This is needed because build only testcases might be
            # disabling options that are needed to implement boot
            # delays, like clock stuff
            global boot_delay
            _boot_delay = boot_delay.get(target.type, 1000)
            if not target.bsp in target.bsps_stub:
                target.zephyr.config_file_write(
                    "500_boot_config",
                    """\
# Introduce a boot delay of 1s to make sure the system
# has had time to setup the serial ports and start recording
# from them
CONFIG_BOOT_BANNER=y
CONFIG_BOOT_DELAY=%d
""" % _boot_delay,
                    bsp = target.bsp)
            else:
                target.zephyr.config_file_write(
                    "500_boot_config",
                    """\
# Stubs be quiet and don't delay, ready ASAP
CONFIG_BOOT_BANNER=n
CONFIG_BOOT_DELAY=0
""",
                    bsp = target.bsp)

        # Add stubs for other BSPs in this board if there are none
        # We will run this each time we go for a BSP build, but it is fine
        for bsp_stub in target.bsps_stub.keys():
            target.stub_app_add(
                bsp_stub, app_zephyr,
                os.path.join(ZEPHYR_BASE, 'tests', 'booting', 'stub'))
示例#17
0
文件: expecter.py 项目: intel/tcf
def console_rx_eval(expecter, target,
                    regex, console = None, _timeout = None, result = None,
                    uid = None):
    """
    Check what came on a console and act on it

    :param str uid: (optional) identifier to use to store offset data
    """

    if hasattr(regex, "pattern"):
        what = regex.pattern
    else:
        what = regex
        regex = re.compile(re.escape(regex))

    if not uid:
        uid = console_mk_uid(target, what, console, _timeout, result)

    console_id_name, console_code = console_mk_code(target, console)
    # These were set by the poller
    of = expecter.buffers.get(console_code, None)
    if of == None:
        # FIXME: debug lof output here? expecter->tc backlink?
        return None
    ofd = of.fileno()
    ts = time.time()

    # Get the offset we saved before as the last part where we looked
    # at. If none, then get the last offset the poller has
    # recorded. Otherwise, just default to look from the start
    # Note the idea is each eval function has a different offset where
    # it is looking at. Likewise the poller for each console.
    offset_poller_code = "offset_" + console_code
    offset = expecter.buffers_persistent.get(
        uid,
        expecter.buffers_persistent.get(offset_poller_code, 0))
    if _timeout != False:
        timeout = expecter.timeout if _timeout == None else _timeout

        # We can do timeout checks that provide better information
        # than a generic 'timeout'
        if ts - expecter.ts0 > timeout:
            of.seek(offset)	# so we report console from where searched
            raise tc.error_e(
                "expected console output '%s' from console '%s:%s' " \
                "NOT FOUND after %.1f s" \
                % (what, target.id, console_id_name, ts - expecter.ts0),
                { 'target': target, "console output": of })

    # mmap the whole file (which doesn't alter the file pointer)
    #
    # We have to mmap as the file might be getting huge and thus,
    # reading line by line might be dumb.
    #
    # However, we only search starting at @offset, which is set later
    # to the last success searching we had. So we shan't really map
    # the whole file, shall map on demand.

    stat_info = os.fstat(ofd)
    if stat_info.st_size == 0:	# Nothing to read
        return None

    with contextlib.closing(mmap.mmap(of.fileno(), 0, mmap.MAP_PRIVATE,
                                      mmap.PROT_READ, 0)) as mapping:
        target.report_info("looking for `%s` in console %s:%s @%d-%d at "
                           "%.2fs [%s]"
                           % (what, target.fullid, console_id_name, offset,
                              stat_info.st_size, ts - expecter.ts0, of.name),
                           dlevel = 3)
        m = regex.search(mapping[offset:])
        if m:
            new_offset = offset + m.end()
            expecter.buffers_persistent[uid] = new_offset
            if result == None or result == "pass":
                # raising pass gets stopped at expecter.run(), so we
                # print instead, so we can see the console
                offset_tip = of.tell()	# we report console from where searched
                of.seek(offset)	# so we report console from where searched
                target.report_pass(
                    "found expected `%s` in console `%s:%s` at %.2fs"
                    % (what, target.fullid, console_id_name,
                       ts - expecter.ts0),
                    { "console output": of }, dlevel = 1, alevel = 2)
                of.seek(offset_tip)
                raise tc.pass_e(
                    "found expected `%s` in console `%s:%s` at %.2fs"
                    % (what, target.fullid, console_id_name,
                       ts - expecter.ts0),
                    {'target': target })
            elif result == "fail":
                of.seek(offset)	# so we report console from where searched
                raise tc.failed_e(
                    "found expected (for failure) `%s` in console "
                    "`%s:%s` at %.2fs"
                    % (what, target.fullid, console_id_name,
                       ts - expecter.ts0),
                    { 'target': target, "console output": of })
            elif result == "error" or result == "errr":
                of.seek(offset)	# so we report console from where searched
                raise tc.error_e(
                    "found expected (for error) `%s` in console "
                    "`%s:%s` at %.2fs"
                    % (what, target.fullid, console_id_name,
                       ts - expecter.ts0),
                    { 'target': target, "console output": of })
            elif result == "skip":
                of.seek(offset)	# so we report console from where searched
                raise tc.skip_e(
                    "found expected (for skip) `%s` in console "
                    "'%s:%s' at %.2fs"
                    % (what, target.fullid, console_id_name,
                       ts - expecter.ts0),
                    { 'target': target, "console output": of })
            else:
                of.seek(offset)	# so we report console from where searched
                raise tc.blocked_e(
                    "BUG: invalid result requested (%s)" % result)
    return None
示例#18
0
def _disk_partition(target):
    # we assume we are going to work on the boot device
    device_basename = target.kws['pos_boot_dev']
    device = "/dev/" + device_basename
    target.shell.run('swapoff -a || true')  # in case we autoswapped

    # find device size (FIXME: Linux specific)
    dev_info = None
    for blockdevice in target.pos.fsinfo.get('blockdevices', []):
        if blockdevice['name'] == device_basename:
            dev_info = blockdevice
            break
    else:
        raise tc.error_e(
            "%s: can't find information about this block device -- is "
            "the right pos_boot_device set in the configuration?" %
            device_basename, dict(fsinfo=pprint.pformat(target.pos.fsinfo)))
    size_gb = int(dev_info['size']) / 1024 / 1024 / 1024
    target.report_info("POS: %s is %d GiB in size" % (device, size_gb),
                       dlevel=2)

    partsizes = target.kws.get('pos_partsizes', None)
    if partsizes == None:
        raise tc.blocked_e(
            "Can't partition target, it doesn't "
            "specify pos_partsizes tag", {'target': target})
    partsize_l = partsizes.split(":")
    partsize_l = [int(_partsize) for _partsize in partsize_l]
    boot_size = partsize_l[0]
    swap_size = partsize_l[1]
    scratch_size = partsize_l[2]
    root_size = partsize_l[3]

    # note we set partition #0 as boot
    cmdline = """parted -a optimal -ms %(device)s unit GiB \
mklabel gpt \
mkpart primary fat32 0%% %(boot_size)s \
set 1 boot on \
mkpart primary linux-swap %(boot_size)s %(swap_end)s \
mkpart primary ext4 %(swap_end)s %(scratch_end)s \
""" % dict(
        device=device,
        boot_size=boot_size,
        swap_end=boot_size + swap_size,
        scratch_end=boot_size + swap_size + scratch_size,
    )
    offset = boot_size + swap_size + scratch_size
    root_devs = []  # collect the root devices
    pid = 4
    while offset + root_size < size_gb:
        cmdline += ' mkpart primary ext4 %d %d' % (offset, offset + root_size)
        offset += root_size
        root_devs.append(device_basename + target.kws['p_prefix'] + "%d" % pid)
        pid += 1

    target.shell.run(cmdline)
    # Now set the root device information, so we can pick stuff to
    # format quick
    for root_dev in root_devs:
        target.property_set('pos_root_' + root_dev, "EMPTY")

    # Re-read partition tables
    target.shell.run('partprobe %s' % device)

    # now format filesystems
    #
    # note we only format the system boot partition (1), the linux
    # swap(2) and the linux scratch space (3)
    boot_dev = device + target.kws['p_prefix'] + "1"
    swap_dev = device + target.kws['p_prefix'] + "2"
    home_dev = device + target.kws['p_prefix'] + "3"
    # Note: use FAT vs VFAT: vfat name translation creates issues when
    # doing long file names; fat32 does not have that problem.
    target.shell.run("mkfs.fat -F32 -n TCF-BOOT " + boot_dev)
    target.shell.run("mkswap -L tcf-swap " + swap_dev)
    target.shell.run("mkfs.ext4 -FqL tcf-scratch " + home_dev)
示例#19
0
def _disk_partition(target):
    # we assume we are going to work on the boot device
    device_basename = target.kws['pos_boot_dev']
    device = "/dev/" + device_basename
    target.shell.run('swapoff -a || true')	    # in case we autoswapped

    # find device size (FIXME: Linux specific)
    dev_info = None
    for blockdevice in target.pos.fsinfo.get('blockdevices', []):
        if blockdevice['name'] == device_basename:
            dev_info = blockdevice
            break
    else:
        raise tc.error_e(
            "%s: can't find information about this block device -- is "
            "the right pos_boot_device set in the configuration?"
            % device_basename,
            dict(fsinfo = pprint.pformat(target.pos.fsinfo)))
    size_gb = int(dev_info['size']) / 1024 / 1024 / 1024
    target.report_info("POS: %s is %d GiB in size" % (device, size_gb),
                       dlevel = 2)

    partsizes = target.kws.get('pos_partsizes', None)
    if partsizes == None:
        raise tc.blocked_e(
            "Can't partition target, it doesn't "
            "specify pos_partsizes tag",
            { 'target': target } )
    partsize_l = partsizes.split(":")
    partsize_l = [ int(_partsize) for _partsize in partsize_l ]
    boot_size = partsize_l[0]
    swap_size = partsize_l[1]
    scratch_size = partsize_l[2]
    root_size = partsize_l[3]

    # note we set partition #0 as boot
    cmdline = """parted -a optimal -ms %(device)s unit GiB \
mklabel gpt \
mkpart primary fat32 0%% %(boot_size)s \
set 1 boot on \
mkpart primary linux-swap %(boot_size)s %(swap_end)s \
mkpart primary ext4 %(swap_end)s %(scratch_end)s \
""" % dict(
    device = device,
    boot_size = boot_size,
    swap_end = boot_size + swap_size,
    scratch_end = boot_size + swap_size + scratch_size,
)
    offset = boot_size + swap_size + scratch_size
    root_devs = []	# collect the root devices
    pid = 4
    while offset + root_size < size_gb:
        cmdline += ' mkpart primary ext4 %d %d' % (offset, offset + root_size)
        offset += root_size
        root_devs.append(device_basename + target.kws['p_prefix']
                         + "%d" % pid)
        pid += 1

    target.shell.run(cmdline)
    # Now set the root device information, so we can pick stuff to
    # format quick
    for root_dev in root_devs:
        target.property_set('pos_root_' + root_dev, "EMPTY")

    # Re-read partition tables
    target.shell.run('partprobe %s' % device)

    # now format filesystems
    #
    # note we only format the system boot partition (1), the linux
    # swap(2) and the linux scratch space (3)
    boot_dev = device + target.kws['p_prefix'] + "1"
    swap_dev = device + target.kws['p_prefix'] + "2"
    home_dev = device + target.kws['p_prefix'] + "3"
    # Note: use FAT vs VFAT: vfat name translation creates issues when
    # doing long file names; fat32 does not have that problem.
    target.shell.run("mkfs.fat -F32 -n TCF-BOOT " + boot_dev)
    target.shell.run("mkswap -L tcf-swap " + swap_dev)
    target.shell.run("mkfs.ext4 -FqL tcf-scratch " + home_dev)
示例#20
0
    def up(self,
           tempt=None,
           user=None,
           login_regex=re.compile('login:'******'Password:'******'%s')" %
                                (self.target.testcase.tls.expecter.timeout,
                                 self.linux_shell_prompt_regex.pattern))
                        continue
            else:
                if user:
                    _login(self.target)
                self.target.expect(self.linux_shell_prompt_regex)
        finally:
            self.target.testcase.tls.expecter.timeout = original_timeout

        if shell_setup:
            # disable line editing for proper recording of command line
            # when running bash; otherwise the scrolling readline does
            # messes up the output
            self.run('test ! -z "$BASH" && set +o vi +o emacs')
            # Trap the shell to complain loud if a command fails, and catch it
            # See that '' in the middle, is so the catcher later doesn't
            # get tripped by the command we sent to set it up
            self.run("trap 'echo ERROR''-IN-SHELL' ERR")
            self.target.on_console_rx("ERROR-IN-SHELL",
                                      result='errr',
                                      timeout=False)

        # Now commands should timeout fast
        self.target.testcase.tls.expecter.timeout = 30
示例#21
0
    def evemu_create_device(self, did, descriptor):
        """
        Create a new input device

        :param str did: device id, or name of the device (eg: *mouse
          1*, *keyboard main*, *left joytick*...)

        :param str descriptor: input device descriptor, as described
          by Linux's *evemu-describe* (without the comments); see
          :const:`descriptor_mouse`, :const:`descriptor_kbd`.
        """
        # we'll evolve this into being able to create multiple mice of
        # different implementations (eg: evemu based vs hardware
        # based)...because we might want to type working with multiple mice
        assert isinstance(did, basestring), \
            "did is type %s; expected string" % type(did)
        descriptor = descriptor.strip()
        if not isinstance(did, basestring) \
            or not descriptor_valid_regex.search(descriptor.strip()):
            raise tc.error_e(
                "did %s: descriptor (%s) is not a string or it does not"
                " match the expected format in"
                " tcfl.target_ext_input.descriptor_valid_regex" %
                (did, type(descriptor)), dict(descriptor=descriptor))

        target = self.target
        # Copy the device descriptor to the device, using SSH if
        # possible (faster)
        if hasattr(target, 'ssh') and target.ssh.tunnel_up():
            with open(os.path.join(target.testcase.tmpdir, "input-dev.desc"),
                      "w") as f:
                f.write(descriptor)
                f.flush()
                target.ssh.copy_to(f.name, "/tmp/input-dev.desc")
        else:
            target.shell.string_copy_to_file(descriptor, "/tmp/input-dev.desc")
        tries = 4
        for cnt in range(tries):
            # Note the /tmp/evemu-%s.log file is also checked by
            # _device_get() below.
            output = target.shell.run(
                "nohup %s /tmp/input-dev.desc >& /tmp/evemu-%s.log"
                " & sleep 1s;"
                " cat /tmp/evemu-%s.log" % (self.evemu_device, did, did),
                output=True,
                trim=True)
            # this prints
            ## NAME: /dev/input/eventX, so get it
            eventX = self._evemu_device_log_extract(output)
            if not eventX:
                self.target.report_error(
                    "%s: can't locate /dev/input/event* name in output to"
                    " create new input device; retrying %d/%d" %
                    (did, cnt, tries), dict(output=output))
                continue
            # take only the basename; /dev/input will be added by the FIFO
            # itself, it is less chars to print
            self.devices[did] = eventX
            return

        raise tc.error_e("%s: failed %d times to create input device" %
                         (did, tries))
示例#22
0
    def up(self,
           tempt=None,
           user=None,
           login_regex=re.compile('login:'******'[Pp]assword:'),
           shell_setup=True,
           timeout=None,
           console=None):
        """Wait for the shell in a console to be ready

        Giving it ample time to boot, wait for a :data:`shell prompt
        <shell_prompt_regex>` and set up the shell so that if an
        error happens, it will print an error message and raise a
        block exception. Optionally login as a user and password.

        >>> target.shell.up(user = '******', password = '******')

        :param str tempt: (optional) string to send before waiting for
          the loging prompt (for example, to send a newline that
          activates the login)

        :param str user: (optional) if provided, it will wait for
          *login_regex* before trying to login with this user name.

        :param str password: (optional) if provided, and a password
          prompt is found, send this password.

        :param str login_regex: (optional) if provided (string
          or compiled regex) and *user* is provided, it will wait for
          this prompt before sending the username.

        :param str password_regex: (optional) if provided (string
          or compiled regex) and *password* is provided, it will wait for
          this prompt before sending the password.

        :param int delay_login: (optional) wait this many seconds
          before sending the user name after finding the login prompt.

        :param shell_setup: (optional, default) setup the shell
          up by disabling command line editing (makes it easier for
          the automation) and set up hooks that will raise an
          exception if a shell command fails.

          By default calls target.shell.setup(); if *False*, nothing
          will be called. Arguments are passed:

          - *console = CONSOLENAME*: console where to operate; can be
            *None* for the default console.

        :param int timeout: [optional] seconds to wait for the login
          prompt to appear; defaults to 60s plus whatever the target
          specifies in metadata *bios_boot_time*.

        :param str console: [optional] name of the console where to
          operate; if *None* it will update the current default
          console to whatever the server considers it shall be (the
          console called *default*).

          If a previous run set the default console to something else,
          setting it to *None* will update it to what the server
          considers shall be the default console (default console at
          boot).

        """
        assert tempt == None or isinstance(tempt, basestring)
        assert user == None or isinstance(user, basestring)
        assert isinstance(login_regex, (basestring, re._pattern_type))
        assert delay_login >= 0
        assert password == None or isinstance(password, basestring)
        assert isinstance(password_regex, (basestring, re._pattern_type))
        assert isinstance(shell_setup, bool) or callable(shell_setup)
        assert timeout == None or timeout > 0
        assert console == None or isinstance(console, basestring)

        target = self.target
        testcase = target.testcase
        if timeout == None:
            timeout = 60 + int(target.kws.get("bios_boot_time", 0))

        def _login(target):
            # If we have login info, login to get a shell prompt
            target.expect(login_regex, name="login prompt", console=console)
            if delay_login:
                target.report_info("Delaying %ss before login in" %
                                   delay_login)
                time.sleep(delay_login)
            target.send(user)
            if password:
                target.expect(password_regex,
                              name="password prompt",
                              console=console)
                target.send(password, console=console)

        try:
            if console == None:  # reset the default console
                target.console.default = None
            original_timeout = testcase.tls.expect_timeout
            testcase.tls.expect_timeout = timeout
            if tempt:
                tries = 3
                while tries > 0:
                    try:
                        target.send(tempt, console=console)
                        if user:
                            _login(self.target)
                        target.expect(self.shell_prompt_regex,
                                      console=console,
                                      timeout=3,
                                      name="early shell prompt")
                        break
                    except tc.error_e as _e:
                        if tries == 0:
                            raise tc.error_e(
                                "Waited too long (%ds) for shell to come up "
                                "(did not receive '%s')" %
                                (self.target.testcase.tls.expect_timeout,
                                 self.shell_prompt_regex.pattern))
                        continue
                    finally:
                        tries -= 1
            else:
                if user:
                    _login(self.target)
                target.expect(self.shell_prompt_regex,
                              console=console,
                              name="early shell prompt")
        finally:
            testcase.tls.expect_timeout = original_timeout

        # same as target.console.select_preferred()
        if shell_setup == True:  # passed as a parameter
            target.shell.setup(console)
        elif callable(shell_setup):
            shell_setup(console)
def console_rx_eval(expecter,
                    target,
                    regex,
                    console=None,
                    _timeout=None,
                    result=None,
                    uid=None):
    """
    Check what came on a console and act on it

    :param str uid: (optional) identifier to use to store offset data
    """

    if hasattr(regex, "pattern"):
        what = regex.pattern
    else:
        what = regex
        regex = re.compile(re.escape(regex))

    if not uid:
        uid = console_mk_uid(target, what, console, _timeout, result)

    console_id_name, console_code = console_mk_code(target, console)
    # These were set by the poller
    of = expecter.buffers.get(console_code, None)
    if of == None:
        # FIXME: debug lof output here? expecter->tc backlink?
        return None
    ofd = of.fileno()
    ts = time.time()

    # Get the offset we saved before as the last part where we looked
    # at. If none, then get the last offset the poller has
    # recorded. Otherwise, just default to look from the start
    # Note the idea is each eval function has a different offset where
    # it is looking at. Likewise the poller for each console.
    offset_poller_code = "offset_" + console_code
    offset = expecter.buffers_persistent.get(
        uid, expecter.buffers_persistent.get(offset_poller_code, 0))
    if _timeout != False:
        timeout = expecter.timeout if _timeout == None else _timeout

        # We can do timeout checks that provide better information
        # than a generic 'timeout'
        if ts - expecter.ts0 > timeout:
            of.seek(offset)  # so we report console from where searched
            raise tc.error_e(
                "expected console output '%s' from console '%s:%s' " \
                "NOT FOUND after %.1f s" \
                % (what, target.id, console_id_name, ts - expecter.ts0),
                { 'target': target, "console output": of })

    # mmap the whole file (which doesn't alter the file pointer)
    #
    # We have to mmap as the file might be getting huge and thus,
    # reading line by line might be dumb.
    #
    # However, we only search starting at @offset, which is set later
    # to the last success searching we had. So we shan't really map
    # the whole file, shall map on demand.

    stat_info = os.fstat(ofd)
    if stat_info.st_size == 0:  # Nothing to read
        return None

    with contextlib.closing(
            mmap.mmap(of.fileno(), 0, mmap.MAP_PRIVATE, mmap.PROT_READ,
                      0)) as mapping:
        target.report_info("looking for `%s` in console %s:%s @%d-%d at "
                           "%.2fs [%s]" %
                           (what, target.fullid, console_id_name, offset,
                            stat_info.st_size, ts - expecter.ts0, of.name),
                           dlevel=3)
        m = regex.search(mapping[offset:])
        if m:
            new_offset = offset + m.end()
            expecter.buffers_persistent[uid] = new_offset
            if result == None or result == "pass":
                # raising pass gets stopped at expecter.run(), so we
                # print instead, so we can see the console
                offset_tip = of.tell()  # we report console from where searched
                of.seek(offset)  # so we report console from where searched
                target.report_pass(
                    "found expected `%s` in console `%s:%s` at %.2fs @%d" %
                    (what, target.fullid, console_id_name, ts - expecter.ts0,
                     offset + m.start()), {"console output": of},
                    dlevel=1,
                    alevel=2)
                of.seek(offset_tip)
                raise tc.pass_e(
                    "found expected `%s` in console `%s:%s` at %.2fs" %
                    (what, target.fullid, console_id_name, ts - expecter.ts0),
                    {'target': target})
            elif result == "fail":
                of.seek(offset)  # so we report console from where searched
                raise tc.failed_e(
                    "found expected (for failure) `%s` in console "
                    "`%s:%s` at %.2fs" %
                    (what, target.fullid, console_id_name, ts - expecter.ts0),
                    {
                        'target': target,
                        "console output": of
                    })
            elif result == "error" or result == "errr":
                of.seek(offset)  # so we report console from where searched
                raise tc.error_e(
                    "found expected (for error) `%s` in console "
                    "`%s:%s` at %.2fs" %
                    (what, target.fullid, console_id_name, ts - expecter.ts0),
                    {
                        'target': target,
                        "console output": of
                    })
            elif result == "skip":
                of.seek(offset)  # so we report console from where searched
                raise tc.skip_e(
                    "found expected (for skip) `%s` in console "
                    "'%s:%s' at %.2fs" %
                    (what, target.fullid, console_id_name, ts - expecter.ts0),
                    {
                        'target': target,
                        "console output": of
                    })
            else:
                of.seek(offset)  # so we report console from where searched
                raise tc.blocked_e("BUG: invalid result requested (%s)" %
                                   result)
    return None
示例#24
0
    def configure(testcase, target, app_src):
        target.kws_required_verify(['zephyr_board', 'zephyr_kernelname'])
        # Adds Zephyr's build zephyr_* vars to the target's keywords
        testcase_file = inspect.getfile(type(testcase))
        zephyr_extra_args = testcase._targets[target.want_name]['kws'].get(
            'app_zephyr_options', "") + " "
        if len(app_src) > 1:
            zephyr_extra_args += " ".join(app_src[1:])
        # Add tags to the target
        srcdir = tcfl.app.get_real_srcdir(testcase_file, app_src[0])
        # Decide if this is cmake style (Zephyr almost 1.10) or not
        # and store it in the keywords, as we'll use in different
        # places to make decissions on if we can run or not.
        ZEPHYR_BASE = os.environ.get('ZEPHYR_BASE',
                                     "__ZEPHYR_BASE_not_defined__")
        if os.path.exists(os.path.join(ZEPHYR_BASE, "CMakeLists.txt")):
            if not os.path.exists(os.path.join(srcdir, "CMakeLists.txt")):
                raise tc.error_e(
                    "%s: Zephyr App is not cmake based, but Zephyr @%s is" %
                    (srcdir, ZEPHYR_BASE))
            is_cmake = True
            # cmake needs the extra args in -DXXX format .. sighs this
            # will blow up at some point, because we are assuming that
            # all extra args passed are VAR=VALUE
            zephyr_extra_args = " ".join("-D" + arg
                                         for arg in zephyr_extra_args.split())
        else:
            if os.path.exists(os.path.join(srcdir, "CMakeLists.txt")):
                raise tc.error_e(
                    "%s: Zephyr App is cmake based, but Zephyr @%s is not" %
                    (srcdir, ZEPHYR_BASE))
            is_cmake = False
        target.kws_set(
            {
                'zephyr_srcdir':
                srcdir,
                # Place all the build output in the temp directory, to
                # avoid polluting the source directories
                'zephyr_objdir':
                os.path.join(
                    testcase.tmpdir,
                    "outdir-%(tc_hash)s-%(tg_hash)s-%(zephyr_board)s" %
                    target.kws),
                'zephyr_is_cmake':
                is_cmake,
            },
            bsp=target.bsp)

        if not target.bsp in target.bsps_stub:
            # Set arguments only for this BSP, not for all of them,
            # and only if we are not building a stub. Note we get the
            # options from app_zephyr_options + OPT1 + OPT2... added
            # after app_src in 'app_zephyr = (SRC, OPT1, OPT2...)
            target.kw_set('zephyr_extra_args',
                          zephyr_extra_args,
                          bsp=target.bsp)
        else:
            # When we are building stubs, we do not take the options
            # from the project itself -- stub needs no options
            target.kw_set('zephyr_extra_args', "", bsp=target.bsp)

        # Do we introduce boot configuration or not?
        if not testcase.build_only:
            # If a testcase is build only, it won't run in HW so we
            # don't need to introduce a boot delay to allow serial
            # ports to be ready.
            # This is needed because build only testcases might be
            # disabling options that are needed to implement boot
            # delays, like clock stuff
            global boot_delay
            _boot_delay = boot_delay.get(target.type, 1000)
            if not target.bsp in target.bsps_stub:
                target.zephyr.config_file_write("500_boot_config",
                                                """\
# Introduce a boot delay of 1s to make sure the system
# has had time to setup the serial ports and start recording
# from them
CONFIG_BOOT_BANNER=y
CONFIG_BOOT_DELAY=%d
""" % _boot_delay,
                                                bsp=target.bsp)
            else:
                target.zephyr.config_file_write("500_boot_config",
                                                """\
# Stubs be quiet and don't delay, ready ASAP
CONFIG_BOOT_BANNER=n
CONFIG_BOOT_DELAY=0
""",
                                                bsp=target.bsp)

        # Add stubs for other BSPs in this board if there are none
        # We will run this each time we go for a BSP build, but it is fine
        for bsp_stub in target.bsps_stub.keys():
            target.stub_app_add(
                bsp_stub, app_zephyr,
                os.path.join(ZEPHYR_BASE, 'tests', 'booting', 'stub'))
示例#25
0
    def up(self, tempt = None,
           user = None, login_regex = re.compile('login:'******'[Pp]assword:'),
           shell_setup = True, timeout = 120):
        """Wait for the shell in a console to be ready

        Giving it ample time to boot, wait for a :data:`shell prompt
        <linux_shell_prompt_regex>` and set up the shell so that if an
        error happens, it will print an error message and raise a
        block exception. Optionally login as a user and password.

        >>> target.shell.up(user = '******', password = '******')

        :param str tempt: (optional) string to send before waiting for
          the loging prompt (for example, to send a newline that
          activates the login)

        :param str user: (optional) if provided, it will wait for
          *login_regex* before trying to login with this user name.

        :param str password: (optional) if provided, and a password
          prompt is found, send this password.

        :param str login_regex: (optional) if provided (string
          or compiled regex) and *user* is provided, it will wait for
          this prompt before sending the username.

        :param str password_regex: (optional) if provided (string
          or compiled regex) and *password* is provided, it will wait for
          this prompt before sending the password.

        :param int delay_login: (optional) wait this many seconds
          before sending the user name after finding the login prompt.

        :param bool shell_setup: (optional, default) setup the shell
          up by disabling command line editing (makes it easier for
          the automation) and set up hooks that will raise an
          exception if a shell command fails.

        :param int timeout: [optional] seconds to wait for the login
          prompt to appear

        """
        assert tempt == None or isinstance(tempt, basestring)
        assert user == None or isinstance(user, basestring)
        assert isinstance(login_regex, ( basestring, re._pattern_type ))
        assert delay_login >= 0
        assert password == None or isinstance(password, basestring)
        assert isinstance(password_regex, ( basestring, re._pattern_type ))
        assert isinstance(shell_setup, bool)
        assert timeout > 0
        def _login(target):
            # If we have login info, login to get a shell prompt
            target.expect(login_regex)
            if delay_login:
                target.report_info("Delaying %ss before login in"
                                   % delay_login)
                time.sleep(delay_login)
            target.send(user)
            if password:
                target.expect(password_regex)
                target.send(password)

        try:
            original_timeout = self.target.testcase.tls.expecter.timeout
            self.target.testcase.tls.expecter.timeout = timeout
            if tempt:
                tries = 0
                while tries < self.target.testcase.tls.expecter.timeout:
                    try:
                        self.target.send(tempt)
                        if user:
                            _login(self.target)
                        self.target.expect(self.linux_shell_prompt_regex,
                                           timeout = 1)
                        break
                    except tc.error_e as _e:
                        if tries == self.target.testcase.tls.expecter.timeout:
                            raise tc.error_e(
                                "Waited too long (%ds) for shell to come up "
                                "(did not receive '%s')" %
                                (self.target.testcase.tls.expecter.timeout,
                                 self.linux_shell_prompt_regex.pattern))
                        continue
            else:
                if user:
                    _login(self.target)
                self.target.expect(self.linux_shell_prompt_regex)
        finally:
            self.target.testcase.tls.expecter.timeout = original_timeout

        if shell_setup:
            # disable line editing for proper recording of command line
            # when running bash; otherwise the scrolling readline does
            # messes up the output
            self.run('test ! -z "$BASH" && set +o vi +o emacs')
            # Trap the shell to complain loud if a command fails, and catch it
            # See that '' in the middle, is so the catcher later doesn't
            # get tripped by the command we sent to set it up
            self.run("trap 'echo ERROR''-IN-SHELL' ERR")
            self.target.on_console_rx("ERROR-IN-SHELL", result = 'errr',
                                      timeout = False)

        # Now commands should timeout fast
        self.target.testcase.tls.expecter.timeout = 30
示例#26
0
def mount_fs(target, image, boot_dev):
    """
    Boots a root filesystem on /mnt

    The partition used as a root filesystem is picked up based on the
    image that is going to be installed; we look for one that has the
    most similar image already installed and pick that.

    :returns: name of the root partition device
    """
    pos_reinitialize = target.property_get("pos_reinitialize", False)
    if pos_reinitialize:
        # Need to reinit the partition table (we were told to by
        # setting pos_repartition to anything
        target.report_info("POS: repartitioning per pos_reinitialize "
                           "property")
        for tag in target.rt.keys():
            # remove pos_root_*, as they don't apply anymore
            if tag.startswith("pos_root_"):
                target.property_set(tag, None)
        _disk_partition(target)
        target.property_set('pos_reinitialize', None)

    root_part_dev = _rootfs_guess(target, image, boot_dev)
    # save for other functions called later
    target.root_part_dev = root_part_dev
    root_part_dev_base = os.path.basename(root_part_dev)
    image_prev = target.property_get("pos_root_" + root_part_dev_base,
                                     "nothing")
    target.report_info("POS: will use %s for root partition (had %s before)"
                       % (root_part_dev, image_prev))

    # fsinfo looks like described in target.pos._fsinfo_load()
    dev_info = None
    for blockdevice in target.pos.fsinfo.get('blockdevices', []):
        for child in blockdevice.get('children', []):
            if child['name'] == root_part_dev_base:
                dev_info = child
    if dev_info == None:
        # it cannot be we might have to repartition because at this
        # point *we* have partitoned.
        raise tc.error_e(
            "Can't find information for root device %s in FSinfo array"
            % root_part_dev_base,
            dict(fsinfo = target.pos.fsinfo))

    # what format does it currently have?
    current_fstype = dev_info.get('fstype', 'ext4')

    # What format does it have to have?
    #
    # Ok, here we need to note that we can't have multiple root
    # filesystems with the same UUID or LABEL, so the image can't rely
    # on UUIDs
    #
    img_fss = target.pos.metadata.get('filesystems', {})
    if '/' in img_fss:
        # a common origin is ok because the YAML schema forces both
        # fstype and mkfs_opts to be specified
        origin = "image's /.tcf.metadata.yaml"
        fsdata = img_fss.get('/', {})
    else:
        origin = "defaults @" + commonl.origin_get(0)
        fsdata = {}
    fstype = fsdata.get('fstype', 'ext4')
    mkfs_opts = fsdata.get('mkfs_opts', '-Fj')

    # do they match?
    if fstype != current_fstype:
        target.report_info(
            "POS: reformatting %s because current format is '%s' and "
            "'%s' is needed (per %s)"
            % (root_part_dev, current_fstype, fstype, origin))
        _mkfs(target, root_part_dev, fstype, mkfs_opts)
    else:
        target.report_info(
            "POS: no need to reformat %s because current format is '%s' and "
            "'%s' is needed (per %s)"
            % (root_part_dev, current_fstype, fstype, origin), dlevel = 1)

    for try_count in range(3):
        target.report_info("POS: mounting root partition %s onto /mnt "
                           "to image [%d/3]" % (root_part_dev, try_count))

        # don't let it fail or it will raise an exception, so we
        # print FAILED in that case to look for stuff; note the
        # double apostrophe trick so the regex finder doens't trip
        # on the command
        output = target.shell.run(
            "mount %s /mnt || echo FAI''LED" % root_part_dev,
            output = True)
        # What did we get?
        if 'FAILED' in output:
            if 'special device ' + root_part_dev \
               + ' does not exist.' in output:
                _disk_partition(target)
            else:
                # ok, this probably means probably the partitions are not
                # formatted; so let's just reformat and retry 
                _mkfs(target, root_part_dev, fstype, mkfs_opts)
        else:
            target.report_info("POS: mounted %s onto /mnt to image"
                               % root_part_dev)
            return root_part_dev	# it worked, we are done
        # fall through, retry
    else:
        raise tc.blocked_e(
            "POS: Tried to mount too many times and failed",
            dict(target = target))
示例#27
0
def boot_config_multiroot(target, boot_dev, image):
    """
    Configure the target to boot using the multiroot
    """
    boot_dev = target.kws['pos_boot_dev']
    # were we have mounted the root partition
    root_dir = "/mnt"

    linux_kernel_file, linux_initrd_file, linux_options = \
        _linux_boot_guess(target, image)
    if linux_kernel_file == None:
        raise tc.blocked_e("Cannot guess a Linux kernel to boot",
                           dict(target=target))
    # remove absolutization (some specs have it), as we need to copy from
    # mounted filesystems
    if os.path.isabs(linux_kernel_file):
        linux_kernel_file = linux_kernel_file[1:]
    if linux_initrd_file and os.path.isabs(linux_initrd_file):
        linux_initrd_file = linux_initrd_file[1:]

    if linux_options == None or linux_options == "":
        target.report_info("WARNING! can't figure out Linux cmdline "
                           "options, taking defaults")
        # below we'll add more stuff
        linux_options = "console=tty0 root=SOMEWHERE"

    # MULTIROOT: indicate which image has been flashed to this
    # partition
    # saved by pos_multiroot.mountfs
    root_part_dev = target.root_part_dev
    root_part_dev_base = os.path.basename(root_part_dev)
    target.property_set('pos_root_' + root_part_dev_base, image)
    # /boot EFI system partition is always /dev/DEVNAME1 (first
    # partition), we partition like that
    # FIXME: we shouldn't harcode this
    boot_part_dev = boot_dev + target.kws['p_prefix'] + "1"

    kws = dict(
        boot_dev=boot_dev,
        boot_part_dev=boot_part_dev,
        root_part_dev=root_part_dev,
        root_part_dev_base=root_part_dev_base,
        root_dir=root_dir,
        linux_kernel_file=linux_kernel_file,
        linux_kernel_file_basename=os.path.basename(linux_kernel_file),
        linux_initrd_file=linux_initrd_file,
        linux_options=linux_options,
    )
    if linux_initrd_file:
        kws['linux_initrd_file_basename'] = os.path.basename(linux_initrd_file)
    else:
        kws['linux_initrd_file_basename'] = None

    kws.update(target.kws)

    if linux_options:
        #
        # Maybe mess with the Linux boot options
        #
        target.report_info("linux cmdline options: %s" % linux_options)
        # FIXME: can this come from config?
        linux_options_replace = {
            # we want to use hard device name rather than LABELS/UUIDs, as
            # we have reformated and those will have changed
            "root": "/dev/%(root_part_dev_base)s" % kws,
            # we have created this in pos_multiroot and will only
            # replace it if the command line option is present.
            "resume": "/dev/disk/by-label/tcf-swap",
        }

        # FIXME: can this come from config?
        # We harcode a serial console on the device where we know the
        # framework is listening
        linux_options_append = \
            "console=%(linux_serial_console_default)s,115200n8" % kws

        linux_options_append += " " + target.rt.get('linux_options_append', "")

        linux_options += " " + linux_options_append

        for option, value in linux_options_replace.iteritems():
            regex = re.compile(r"\b" + option + r"=\S+")
            if regex.search(linux_options):
                linux_options = re.sub(
                    regex, option + "=" + linux_options_replace[option],
                    linux_options)
            else:
                linux_options += " " + option + "=" + value

        kws['linux_options'] = linux_options
        target.report_info("linux cmdline options (modified): %s" %
                           linux_options)

    # Now generate the UEFI system partition that will boot the
    # system; we always override it, so we don't need to decide if it
    # is corrupted or whatever; we'll mount it in /boot (which now is
    # the POS /boot)

    #
    # Mount the /boot fs
    #
    # Try to assume it is ok, try to repair it if not; rsync the
    # kernels in there, it is faster for repeated operation/
    #
    target.report_info("POS/EFI: checking %(boot_part_dev)s" % kws)
    output = target.shell.run("fsck.fat -aw /dev/%(boot_part_dev)s || true" %
                              kws,
                              output=True,
                              trim=True)

    # FIXME: parse the output to tell if there was a problem; when bad
    # but recovered, we'll see
    #
    # 0x41: Dirty bit is set. Fs was not properly unmounted and some data may be corrupt.
    #  Automatically removing dirty bit.
    # Performing changes.
    # /dev/sda1: 11 files, 4173/261372 clusters

    # When ok
    #
    ## $ sudo fsck.vfat -wa /dev/nvme0n1p1
    ## fsck.fat 4.1 (2017-01-24)
    ## /dev/sda1: 39 files, 2271/33259 clusters

    # When really hosed it won't print the device line, so we look for
    # that
    #
    ## $ fsck.vfat -wa /dev/trash
    ## fsck.fat 4.1 (2017-01-24)
    ## Logical sector size (49294 bytes) is not a multiple of the physical sector size.
    good_regex = re.compile(
        "^/dev/%(boot_part_dev)s: [0-9]+ files, "
        "[0-9]+/[0-9]+ clusters$" % kws, re.MULTILINE)
    if not good_regex.search(output):
        target.report_info(
            "POS/EFI: /dev/%(boot_part_dev)s: formatting EFI "
            "filesystem, fsck couldn't fix it" % kws, dict(output=output))
        target.shell.run("mkfs.fat -F32 /dev/%(boot_part_dev)s; sync" % kws)
    target.report_info("POS/EFI: /dev/%(boot_part_dev)s: mounting in /boot" %
                       kws)
    target.shell.run(" mount /dev/%(boot_part_dev)s /boot"
                     " && mkdir -p /boot/loader/entries " % kws)

    # Do we have enough space? if not, remove the oldest stuff that is
    # not the file we are looking for
    # This prints
    ## $ df --output=pcent /boot
    ## Use%
    ##   6%
    output = target.shell.run("df --output=pcent /boot", output=True)
    regex = re.compile(r"^\s*(?P<percent>[\.0-9]+)%$", re.MULTILINE)
    match = regex.search(output)
    if not match:
        raise tc.error_e("Can't determine the amount of free space in /boot",
                         dict(output=output))
    used_space = float(match.groupdict()['percent'])
    if used_space > 75:
        target.report_info(
            "POS/EFI: /dev/%(boot_part_dev)s: freeing up space" % kws)
        # List files in /boot, sort by last update (when we rsynced
        # them)
        ## 2018-10-29+08:48:48.0000000000	84590	/boot/EFI/BOOT/BOOTX64.EFI
        ## 2018-10-29+08:48:48.0000000000	84590	/boot/EFI/systemd/systemd-bootx64.efi
        ## 2019-05-14+13:25:06.0000000000	7192832	/boot/vmlinuz-4.12.14-110-default
        ## 2019-05-14+13:25:08.0000000000	9688340	/boot/initrd-4.12.14-110-default
        ## 2019-05-14+13:25:14.0000000000	224	/boot/loader/entries/tcf-boot.conf
        ## 2019-05-14+13:25:14.0000000000	54	/boot/loader/loader.conf
        output = target.shell.run(
            # that double \\ needed so the shell is the one using it
            # as a \t, not python converting to a sequence
            "find /boot/ -type f -printf '%T+\\t%s\\t%p\\n' | sort",
            output=True,
            trim=True)
        # delete the first half entries over 300k except those that
        # match the kernels we are installing
        to_remove = []
        _linux_initrd_file = kws.get("linux_initrd_file", "%%NONE%%")
        if linux_initrd_file == None:
            _linux_initrd_file = "%%NONE%%"
        for line in output.splitlines():
            _timestamp, size_s, path = line.split(None, 3)
            size = int(size_s)
            if size > 300 * 1024 \
               and not kws['linux_kernel_file_basename'] in path \
               and not _linux_initrd_file in path:
                to_remove.append(path)
        # get older half and wipe it--this means the first half, as we
        # sort from older to newer
        to_remove = to_remove[:len(to_remove) // 2]
        for path in to_remove:
            # we could do them in a single go, but we can exceed the
            # command line length -- lazy to optimize it
            target.shell.run("rm -f %s" % path)

    # Now copy all the files needed to boot to the root of the EFI
    # system partition mounted in /boot; remember they are in /mnt/,
    # our root partition
    target.shell.run(
        "time -p rsync --force --inplace /mnt/%(linux_kernel_file)s"
        " /boot/%(linux_kernel_file_basename)s" % kws)
    if kws.get("linux_initrd_file", None):
        target.shell.run(
            "time -p rsync --force --inplace /mnt/%(linux_initrd_file)s"
            " /boot/%(linux_initrd_file_basename)s" % kws)
    # we are the only one who cuts the cod here (yeah, direct Spanish
    # translation for the reader's entertainment), and if not wipe'm to
    # eternity; otherwise systemd will boot something prolly in
    # alphabetical order, not what we want
    target.shell.run("/usr/bin/rm -rf /boot/loader/entries/*.conf")
    # remember paths to the bootloader are relative to /boot
    # merge these two
    tcf_boot_conf = """\
cat <<EOF > /boot/loader/entries/tcf-boot.conf
title TCF-driven local boot
linux /%(linux_kernel_file_basename)s
options %(linux_options)s
"""
    if kws.get("linux_initrd_file", None):
        tcf_boot_conf += "initrd /%(linux_initrd_file_basename)s\n"
    tcf_boot_conf += "EOF\n"
    target.shell.run(tcf_boot_conf % kws)

    # Install new -- we wiped the /boot fs new anyway; if there are
    # multiple options already, bootctl shall be able to handle it.
    # Don't do variables in the any casewe will poke with them later
    # on anyway. Why? Because there is space for a race condition that
    # will leave us with the system booting off the localdisk vs the
    # network for PXE--see efibootmgr_setup()
    target.shell.run("bootctl update --no-variables"
                     " || bootctl install --no-variables;"
                     " sync")

    # Now mess with the EFIbootmgr
    # FIXME: make this a function and a configuration option (if the
    # target does efibootmgr)
    _efibootmgr_setup(target, boot_dev, 1)
    # umount only if things go well
    # Shall we try to unmount in case of error? nope, we are going to
    # have to redo the whole thing anyway, so do not touch it, in case
    # we are jumping in for manual debugging
    target.shell.run("umount /dev/%(boot_part_dev)s" % kws)
示例#28
0
def mount_fs(target, image, boot_dev):
    """
    Boots a root filesystem on /mnt

    The partition used as a root filesystem is picked up based on the
    image that is going to be installed; we look for one that has the
    most similar image already installed and pick that.

    :returns: name of the root partition device
    """
    # does the disk have a partition scheme we recognize?
    pos_partsizes = target.rt['pos_partsizes']
    # the name we'll give to the boot partition; see
    # _disk_partition(); if we can't find it, we assume the things
    # needs to be repartitioned. Note it includes the sizes, so if we
    # change the sizes in the config it reformats automatically.  #
    # TCF-multiroot-NN:NN:NN:NN
    target._boot_label_name = "TCF-multiroot-" + pos_partsizes
    pos_reinitialize_force = True
    boot_dev_base = os.path.basename(boot_dev)
    child = target.pos.fsinfo_get_child_by_partlabel(boot_dev_base,
                                                     target._boot_label_name)
    if child:
        pos_reinitialize_force = False
    else:
        target.report_info("POS: repartitioning due to different"
                           " partition schema")

    pos_reinitialize = target.property_get("pos_reinitialize", False)
    if pos_reinitialize:
        target.report_info("POS: repartitioning per pos_reinitialize "
                           "property")
    if pos_reinitialize or pos_reinitialize_force:
        # Need to reinit the partition table (we were told to by
        # setting pos_repartition to anything or we didn't recognize
        # the existing partitioning scheme)
        for tag in target.rt.keys():
            # remove pos_root_*, as they don't apply anymore
            if tag.startswith("pos_root_"):
                target.property_set(tag, None)
        _disk_partition(target)
        target.pos.fsinfo_read(target._boot_label_name)
        target.property_set('pos_reinitialize', None)

    root_part_dev = _rootfs_guess(target, image, boot_dev)
    # save for other functions called later
    target.root_part_dev = root_part_dev
    root_part_dev_base = os.path.basename(root_part_dev)
    image_prev = target.property_get("pos_root_" + root_part_dev_base,
                                     "nothing")
    target.report_info("POS: will use %s for root partition (had %s before)" %
                       (root_part_dev, image_prev))

    # fsinfo looks like described in target.pos.fsinfo_read()
    dev_info = None
    for blockdevice in target.pos.fsinfo.get('blockdevices', []):
        for child in blockdevice.get('children', []):
            if child['name'] == root_part_dev_base:
                dev_info = child
    if dev_info == None:
        # it cannot be we might have to repartition because at this
        # point *we* have partitioned.
        raise tc.error_e(
            "Can't find information for root device %s in FSinfo array" %
            root_part_dev_base, dict(fsinfo=target.pos.fsinfo))

    # what format does it currently have?
    current_fstype = dev_info.get('fstype', 'ext4')

    # What format does it have to have?
    #
    # Ok, here we need to note that we can't have multiple root
    # filesystems with the same UUID or LABEL, so the image can't rely
    # on UUIDs
    #
    img_fss = target.pos.metadata.get('filesystems', {})
    if '/' in img_fss:
        # a common origin is ok because the YAML schema forces both
        # fstype and mkfs_opts to be specified
        origin = "image's /.tcf.metadata.yaml"
        fsdata = img_fss.get('/', {})
    else:
        origin = "defaults @" + commonl.origin_get(0)
        fsdata = {}
    fstype = fsdata.get('fstype', 'ext4')
    mkfs_opts = fsdata.get('mkfs_opts', '-Fj')

    # do they match?
    if fstype != current_fstype:
        target.report_info(
            "POS: reformatting %s because current format is '%s' and "
            "'%s' is needed (per %s)" %
            (root_part_dev, current_fstype, fstype, origin))
        _mkfs(target, root_part_dev, fstype, mkfs_opts)
    else:
        target.report_info(
            "POS: no need to reformat %s because current format is '%s' and "
            "'%s' is needed (per %s)" %
            (root_part_dev, current_fstype, fstype, origin),
            dlevel=1)

    for try_count in range(3):
        target.report_info("POS: mounting root partition %s onto /mnt "
                           "to image [%d/3]" % (root_part_dev, try_count))

        # don't let it fail or it will raise an exception, so we
        # print FAILED in that case to look for stuff; note the
        # double apostrophe trick so the regex finder doens't trip
        # on the command
        output = target.shell.run("mount %s /mnt || echo FAI''LED" %
                                  root_part_dev,
                                  output=True)
        # What did we get?
        if 'FAILED' in output:
            if 'special device ' + root_part_dev \
               + ' does not exist.' in output:
                _disk_partition(target)
                target.pos.fsinfo_read(target._boot_label_name)
            else:
                # ok, this probably means probably the partitions are not
                # formatted; so let's just reformat and retry
                _mkfs(target, root_part_dev, fstype, mkfs_opts)
        else:
            target.report_info("POS: mounted %s onto /mnt to image" %
                               root_part_dev)
            return root_part_dev  # it worked, we are done
        # fall through, retry
    else:
        raise tc.blocked_e("POS: Tried to mount too many times and failed",
                           dict(target=target))
示例#29
0
    def up(self,
           tempt=None,
           user=None,
           login_regex=re.compile('login:'******'[Pp]assword:'),
           shell_setup=True,
           timeout=None,
           console=None,
           wait_for_early_shell_prompt=True):
        """Wait for the shell in a console to be ready

        Giving it ample time to boot, wait for a :data:`shell prompt
        <prompt_regex>` and set up the shell so that if an
        error happens, it will print an error message and raise a
        block exception. Optionally login as a user and password.

        Note this resets any shell prompt set by the script to what is
        assumed to be the original one after a power up.

        >>> target.shell.up(user = '******', password = '******')

        :param str tempt: (optional) string to send before waiting for
          the loging prompt (for example, to send a newline that
          activates the login)

        :param str user: (optional) if provided, it will wait for
          *login_regex* before trying to login with this user name.

        :param str password: (optional) if provided, and a password
          prompt is found, send this password.

        :param str login_regex: (optional) if provided (string
          or compiled regex) and *user* is provided, it will wait for
          this prompt before sending the username.

        :param str password_regex: (optional) if provided (string
          or compiled regex) and *password* is provided, it will wait for
          this prompt before sending the password.

        :param int delay_login: (optional) wait this many seconds
          before sending the user name after finding the login prompt.

        :param shell_setup: (optional, default) setup the shell
          up by disabling command line editing (makes it easier for
          the automation) and set up hooks that will raise an
          exception if a shell command fails.

          By default calls target.shell.setup(); if *False*, nothing
          will be called. Arguments are passed:

          - *console = CONSOLENAME*: console where to operate; can be
            *None* for the default console.

        :param int timeout: [optional] seconds to wait for the login
          prompt to appear; defaults to 60s plus whatever the target
          specifies in metadata *bios_boot_time*.

        :param str console: [optional] name of the console where to
          operate; if *None* it will update the current default
          console to whatever the server considers it shall be (the
          console called *default*).

          If a previous run set the default console to something else,
          setting it to *None* will update it to what the server
          considers shall be the default console (default console at
          boot).

        """
        assert tempt == None or isinstance(tempt, basestring)
        assert user == None or isinstance(user, basestring)
        assert login_regex == None or isinstance(
            login_regex, (basestring, re._pattern_type))
        assert delay_login >= 0
        assert password == None or isinstance(password, basestring)
        assert isinstance(password_regex, (basestring, re._pattern_type))
        assert isinstance(shell_setup, bool) or callable(shell_setup)
        assert timeout == None or timeout > 0
        assert console == None or isinstance(console, basestring)

        target = self.target
        testcase = target.testcase
        if timeout == None:
            timeout = 60 + int(target.kws.get("bios_boot_time", 0))
        # Set the original shell prompt
        self.prompt_regex = self.prompt_regex_default

        def _login(target):
            # If we have login info, login to get a shell prompt
            if login_regex != None:
                # we have already recevied this, so do not expect it
                target.expect(login_regex,
                              name="login prompt",
                              console=console)
            if delay_login:
                target.report_info("Delaying %ss before login in" %
                                   delay_login)
                time.sleep(delay_login)
            target.send(user)
            if password:
                target.expect(password_regex,
                              name="password prompt",
                              console=console)
                target.send(password, console=console)

        original_timeout = testcase.tls.expect_timeout
        try:
            if console == None:  # reset the default console
                target.console.default = None
                # this will yield the default console name, not None
                # as we set it above; bad API
                console = target.console.default
            testcase.tls.expect_timeout = timeout
            ts0 = time.time()
            ts = ts0
            inner_timeout = 3 * timeout / 20
            while ts - ts0 < 3 * timeout:
                # if this was an SSH console that was left
                # enabled, it will die and auto-disable when
                # the machine power cycles, so make sure it is enabled
                action = "n/a"
                try:
                    if console.startswith("ssh"):
                        target.console.disable()
                        action = "enable console %s" % console
                        target.console.setup(console,
                                             user=user,
                                             password=password)
                        target.console.enable(console=console)
                        ts = time.time()
                        target.report_info("shell-up: %s: success at +%.1fs" %
                                           (action, ts - ts0),
                                           dlevel=2)
                    if tempt:
                        action = "tempt console %s" % console
                        target.send(tempt, console=console)
                        ts = time.time()
                        target.report_info("shell-up: %s: success at +%.1fs" %
                                           (action, ts - ts0),
                                           dlevel=2)
                    if not console.startswith("ssh"):
                        if user:
                            action = "login in via console %s" % console
                            # _login uses this 'console' definition
                            _login(self.target)
                            ts = time.time()
                            target.report_info(
                                "shell-up: %s: success at +%.1fs" %
                                (action, ts - ts0),
                                dlevel=2)
                    if wait_for_early_shell_prompt:
                        action = "wait for shell prompt"
                        target.expect(self.prompt_regex,
                                      console=console,
                                      name="early shell prompt",
                                      timeout=inner_timeout)
                    break
                except (tc.error_e, tc.failed_e) as e:
                    ts = time.time()
                    target.report_info(
                        "shell-up: action '%s' failed at +%.1fs; retrying: %s"
                        % (action, ts - ts0, e),
                        dict(target=target, exception=e),
                        dlevel=2)
                    time.sleep(inner_timeout)
                    ts = time.time()
                    if console.startswith("ssh"):
                        target.console.disable(console=console)
                    continue
            else:
                raise tc.error_e(
                    "Waited too long (%ds) for shell to come up on"
                    " console '%s' (did not receive '%s')" %
                    (3 * timeout, console, self.prompt_regex.pattern))
        finally:
            testcase.tls.expect_timeout = original_timeout

        # same as target.console.select_preferred()
        if shell_setup == True:  # passed as a parameter
            target.shell.setup(console)
        elif callable(shell_setup):
            shell_setup(console)
        # False, so we don't call shell setup
        # don't set a timeout here, leave it to whatever it was defaulted
        return