Exemplo n.º 1
0
 def test_fuser_k_mount_ro(self, mocked_check_call):
     writeable_mounted_mtds = []
     calls = []
     # TODO set ProcessError side effect
     for i in range(3):
         mocked_check_call.reset_mock()
         system.fuser_k_mount_ro(writeable_mounted_mtds, self.logger)
         mocked_check_call.assert_has_calls(calls)
         writeable_mounted_mtds.append(
             ('/dev/mtdblock{}'.format(i), '/mnt/foo{}'.format(i)))
         calls.append(call(['fuser', '-km', '/mnt/foo{}'.format(i)]))
         calls.append(
             call([
                 'mount', '-o', 'remount,ro', '/dev/mtdblock{}'.format(i),
                 '/mnt/foo{}'.format(i)
             ]))
 def test_fuser_k_mount_ro(self, mocked_check_call):
     writeable_mounted_mtds = []
     calls = []
     # TODO set ProcessError side effect
     for i in range(3):
         mocked_check_call.reset_mock()
         system.fuser_k_mount_ro(writeable_mounted_mtds, self.logger)
         mocked_check_call.assert_has_calls(calls)
         writeable_mounted_mtds.append(
             ("/dev/mtdblock{}".format(i), "/mnt/foo{}".format(i)))
         calls.append(call(["fuser", "-km", "/mnt/foo{}".format(i)]))
         calls.append(
             call([
                 "mount",
                 "-o",
                 "remount,ro",
                 "/dev/mtdblock{}".format(i),
                 "/mnt/foo{}".format(i),
             ]))
Exemplo n.º 3
0
def _unmount_mnt_data():
    # If /mnt/data is mounted, try to unmount it as safely as possible
    m = re.search(
        "^(?P<device>[^ ]+) /mnt/data [^ ]+ [^ ]+ [0-9]+ [0-9]+$",
        open("/proc/mounts").read(),
        flags=re.MULTILINE,
    )

    if m:
        device = m.group("device")
        logger.info("/mnt/data is mounted, attempting to unmount ...")

        logger.info("Bind mount /mnt to /tmp/mnt")
        subprocess.check_output(["mkdir", "-p", "/tmp/mnt"])
        subprocess.check_output(["mount", "--bind", "/mnt", "/tmp/mnt"])

        logger.info("Copying /mnt/data contents to /tmp/mnt ...")
        subprocess.check_output(["cp", "-r", "/mnt/data", "/tmp/mnt"])

        # Sleep for a bit in case sshd is still referencing files in /mnt/data
        # (and we don't kill sessions with fuser -k)
        logger.info(
            "Waiting a bit so that sshd closes any file descriptors on /mnt/data ..."
        )
        time.sleep(3)

        logger.info("Remounting /mnt/data as RO ...")
        system.fuser_k_mount_ro([(device, "/mnt/data")], logger)

        logger.info("Unmounting /mnt/data ...")
        subprocess.check_output(["umount", "/mnt/data"])

        logger.info(
            "Ensuring sshd config (including host keys) is still valid")
        subprocess.check_output(["sshd", "-T"])

        logger.info("/mnt/data unmounted")
def improve_system(logger):
    # type: (object) -> None
    if not system.is_openbmc():
        logger.error("{} must be run from an OpenBMC system.".format(sys.argv[0]))
        sys.exit(1)

    description = textwrap.dedent(
        """\
        Leave the OpenBMC system better than we found it, by performing one or
        more of the following actions: fetching an image file, validating the
        checksums of the partitions inside an image file, copying the image
        file to flash, validating the checksums of the partitions on flash,
        changing kernel parameters, rebooting.

        The current logic is reboot-happy. If you want to validate the
        checksums of the partitions on flash without rebooting, use
        --dry-run."""
    )
    (checksums, args) = system.get_checksums_args(description)

    # Don't let things like dropped SSH connections interrupt.
    [
        signal.signal(s, signal.SIG_IGN)
        for s in [signal.SIGHUP, signal.SIGINT, signal.SIGTERM]
    ]

    (full_flash_mtds, all_mtds) = system.get_mtds()

    [total_memory_kb, free_memory_kb] = system.get_mem_info()
    logger.info(
        "{} KiB total memory, {} KiB free memory.".format(
            total_memory_kb, free_memory_kb
        )
    )

    reboot_threshold_pct = system.get_healthd_reboot_threshold()
    reboot_threshold_kb = ((100 - reboot_threshold_pct) / 100) * total_memory_kb

    # As of May 2019, sample image files are 17 to 23 MiB. Make sure there
    # is space for them and a few flashing related processes.
    max_openbmc_img_size = 23
    openbmc_img_size_kb = max_openbmc_img_size * 1024
    # in the case of no reboot treshold, use a default limit
    default_threshold = 60 * 1024

    # for low memory remediation - ensure downloading BMC image will NOT trigger
    # healthd reboot
    min_memory_needed = max(
        default_threshold, openbmc_img_size_kb + reboot_threshold_kb
    )
    logger.info(
        "Healthd reboot threshold at {}%  ({} KiB)".format(
            reboot_threshold_pct, reboot_threshold_kb
        )
    )
    logger.info("Minimum memory needed for update is {} KiB".format(min_memory_needed))
    if free_memory_kb < min_memory_needed:
        logger.info(
            "Free memory ({} KiB) < minimum required memory ({} KiB), reboot needed".format(
                free_memory_kb, min_memory_needed
            )
        )
        if full_flash_mtds != []:
            [
                system.get_valid_partitions([full_flash], checksums, logger)
                for full_flash in full_flash_mtds
            ]
        else:
            system.get_valid_partitions(all_mtds, checksums, logger)
        system.reboot(args.dry_run, "remediating low free memory", logger)

    if full_flash_mtds == []:
        partitions = system.get_valid_partitions(all_mtds, checksums, logger)
        mtdparts = "mtdparts={}:{},-@0(flash0)".format(
            "spi0.0", ",".join(map(str, partitions))
        )
        system.append_to_kernel_parameters(args.dry_run, mtdparts, logger)
        system.reboot(args.dry_run, "changing kernel parameters", logger)

    reason = "potentially applying a latent update"
    if args.image:
        image_file_name = args.image
        if args.image.startswith("https"):
            try:
                cmd = ["curl", "-k", "-s", "-o", "/tmp/flash", args.image]
                system.run_verbosely(cmd, logger)
                image_file_name = "/tmp/flash"
            except Exception:
                # Python2 may throw OSError here, whereas Python3 may
                # throw FileNotFoundError - common case is Exception.
                args.image = args.image.replace("https", "http")

        if args.image.startswith("http:"):
            cmd = ["wget", "-q", "-O", "/tmp/flash", args.image]
            system.run_verbosely(cmd, logger)
            # --continue might be cool but it doesn't seem to work.
            image_file_name = "/tmp/flash"

        image_file = virtualcat.ImageFile(image_file_name)
        system.get_valid_partitions([image_file], checksums, logger)

        # If /mnt/data is smaller in the new image, it must be made read-only
        # to prevent the tail of the FIT image from being corrupted.
        # Determining shrinkage isn't trivial (data0 is omitted from image
        # image files for one thing) and details may change over time, so just
        # remount every MTD read-only. Reboot is expected to restart the
        # killed processes.
        system.fuser_k_mount_ro(system.get_writeable_mounted_mtds(), logger)

        # Don't let healthd reboot mid-flash (S166329).
        system.remove_healthd_reboot(logger)

        attempts = 0 if args.dry_run else 3
        for mtd in full_flash_mtds:
            system.flash(attempts, image_file, mtd, logger)
        # One could in theory pre-emptively set mtdparts for images that
        # will need it, but the mtdparts generator hasn't been tested on dual
        # flash and potentially other systems. To avoid over-optimizing for
        # what should be a temporary condition, just reboot and reuse the
        # existing no full flash logic.
        reason = "applying an update"

    [
        system.get_valid_partitions([full_flash], checksums, logger)
        for full_flash in full_flash_mtds
    ]
    system.reboot(args.dry_run, reason, logger)
Exemplo n.º 5
0
def improve_system(logger):
    # type: (object) -> None
    if not system.is_openbmc():
        logger.error('{} must be run from an OpenBMC system.'.format(
            sys.argv[0]))
        sys.exit(1)

    description = textwrap.dedent('''\
        Leave the OpenBMC system better than we found it, by performing one or
        more of the following actions: fetching an image file, validating the
        checksums of the partitions inside an image file, copying the image
        file to flash, validating the checksums of the partitions on flash,
        changing kernel parameters, rebooting.

        The current logic is reboot-happy. If you want to validate the
        checksums of the partitions on flash without rebooting, use
        --dry-run.''')
    (checksums, args) = system.get_checksums_args(description)

    # Don't let things like dropped SSH connections interrupt.
    [
        signal.signal(s, signal.SIG_IGN)
        for s in [signal.SIGHUP, signal.SIGINT, signal.SIGTERM]
    ]

    (full_flash_mtds, all_mtds) = system.get_mtds()

    free_memory = system.free_kibibytes()
    logger.info('{} KiB free memory.'.format(free_memory))
    # As of August 2017, sample image files are 15 to 22 MiB. Make sure there
    # is space for them and a few flashing related processes.
    if system.free_kibibytes() < 60 * 1024:
        if full_flash_mtds != []:
            [
                system.get_valid_partitions([full_flash], checksums, logger)
                for full_flash in full_flash_mtds
            ]
        else:
            system.get_valid_partitions(all_mtds, checksums, logger)
        system.reboot(args.dry_run, 'remediating low free memory', logger)

    if full_flash_mtds == []:
        partitions = system.get_valid_partitions(all_mtds, checksums, logger)
        mtdparts = 'mtdparts={}:{},-@0(flash0)'.format(
            'spi0.0', ','.join(map(str, partitions)))
        system.append_to_kernel_parameters(args.dry_run, mtdparts, logger)
        system.reboot(args.dry_run, 'changing kernel parameters', logger)

    reason = 'potentially applying a latent update'
    if args.image:
        image_file_name = args.image
        if args.image.startswith('http'):
            # --continue might be cool but it doesn't seem to work.
            system.run_verbosely(
                ['wget', '-q', '-O', '/tmp/flash', args.image], logger)
            image_file_name = '/tmp/flash'

        image_file = virtualcat.ImageFile(image_file_name)
        system.get_valid_partitions([image_file], checksums, logger)

        # If /mnt/data is smaller in the new image, it must be made read-only
        # to prevent the tail of the FIT image from being corrupted.
        # Determining shrinkage isn't trivial (data0 is omitted from image
        # image files for one thing) and details may change over time, so just
        # remount every MTD read-only. Reboot is expected to restart the
        # killed processes.
        system.fuser_k_mount_ro(system.get_writeable_mounted_mtds(), logger)

        # Don't let healthd reboot mid-flash (S166329).
        system.remove_healthd_reboot(logger)

        attempts = 0 if args.dry_run else 3
        for mtd in full_flash_mtds:
            system.flash(attempts, image_file, mtd, logger)
        # One could in theory pre-emptively set mtdparts for images that
        # will need it, but the mtdparts generator hasn't been tested on dual
        # flash and potentially other systems. To avoid over-optimizing for
        # what should be a temporary condition, just reboot and reuse the
        # existing no full flash logic.
        reason = 'applying an update'

    [
        system.get_valid_partitions([full_flash], checksums, logger)
        for full_flash in full_flash_mtds
    ]
    system.reboot(args.dry_run, reason, logger)