Exemplo n.º 1
0
def maybe_get_writable_device_path(devpath, info, log):
    """Return updated devpath if the devpath is a writable block device.

    @param devpath: Requested path to the root device we want to resize.
    @param info: String representing information about the requested device.
    @param log: Logger to which logs will be added upon error.

    @returns devpath or updated devpath per kernel commandline if the device
        path is a writable block device, returns None otherwise.
    """
    container = util.is_container()

    # Ensure the path is a block device.
    if (devpath == "/dev/root" and not os.path.exists(devpath)
            and not container):
        devpath = util.rootdev_from_cmdline(util.get_cmdline())
        if devpath is None:
            log.warn("Unable to find device '/dev/root'")
            return None
        log.debug("Converted /dev/root to '%s' per kernel cmdline", devpath)

    if devpath == 'overlayroot':
        log.debug("Not attempting to resize devpath '%s': %s", devpath, info)
        return None

    # FreeBSD zpool can also just use gpt/<label>
    # with that in mind we can not do an os.stat on "gpt/whatever"
    # therefore return the devpath already here.
    if devpath.startswith('gpt/'):
        log.debug('We have a gpt label - just go ahead')
        return devpath

    try:
        statret = os.stat(devpath)
    except OSError as exc:
        if container and exc.errno == errno.ENOENT:
            log.debug(
                "Device '%s' did not exist in container. "
                "cannot resize: %s", devpath, info)
        elif exc.errno == errno.ENOENT:
            log.warn("Device '%s' did not exist. cannot resize: %s", devpath,
                     info)
        else:
            raise exc
        return None

    if not stat.S_ISBLK(statret.st_mode) and not stat.S_ISCHR(statret.st_mode):
        if container:
            log.debug("device '%s' not a block device in container."
                      " cannot resize: %s" % (devpath, info))
        else:
            log.warn("device '%s' not a block device. cannot resize: %s" %
                     (devpath, info))
        return None
    return devpath  # The writable block devpath
Exemplo n.º 2
0
def maybe_install_squashfuse(cloud):
    """Install squashfuse if we are in a container."""
    if not util.is_container():
        return
    try:
        cloud.distro.update_package_sources()
    except Exception:
        util.logexc(LOG, "Package update failed")
        raise
    try:
        cloud.distro.install_packages(['squashfuse'])
    except Exception:
        util.logexc(LOG, "Failed to install squashfuse")
        raise
Exemplo n.º 3
0
def maybe_install_squashfuse(cloud):
    """Install squashfuse if we are in a container."""
    if not util.is_container():
        return
    try:
        cloud.distro.update_package_sources()
    except Exception:
        util.logexc(LOG, "Package update failed")
        raise
    try:
        cloud.distro.install_packages(['squashfuse'])
    except Exception:
        util.logexc(LOG, "Failed to install squashfuse")
        raise
Exemplo n.º 4
0
def gather_timestamps_using_systemd():
    """
    Gather timestamps that corresponds to kernel begin initialization,
    kernel finish initialization. and cloud-init systemd unit activation

    :return: the three timestamps
    """
    kernel_start = float(time.time()) - float(util.uptime())
    try:
        delta_k_end = SystemctlReader(
            "UserspaceTimestampMonotonic"
        ).parse_epoch_as_float()
        delta_ci_s = SystemctlReader(
            "InactiveExitTimestampMonotonic", "cloud-init-local"
        ).parse_epoch_as_float()
        base_time = kernel_start
        status = SUCCESS_CODE
        # lxc based containers do not set their monotonic zero point to be when
        # the container starts, instead keep using host boot as zero point
        # time.CLOCK_MONOTONIC_RAW is only available in python 3.3
        if util.is_container():
            # clock.monotonic also uses host boot as zero point
            if sys.version_info >= (3, 3):
                base_time = float(time.time()) - float(time.monotonic())
                # TODO: lxcfs automatically truncates /proc/uptime to seconds
                # in containers when https://github.com/lxc/lxcfs/issues/292
                # is fixed, util.uptime() should be used instead of stat on
                try:
                    file_stat = os.stat("/proc/1/cmdline")
                    kernel_start = file_stat.st_atime
                except OSError as err:
                    raise RuntimeError(
                        "Could not determine container boot "
                        "time from /proc/1/cmdline. ({})".format(err)
                    ) from err
                status = CONTAINER_CODE
            else:
                status = FAIL_CODE
        kernel_end = base_time + delta_k_end
        cloudinit_sysd = base_time + delta_ci_s

    except Exception as e:
        # Except ALL exceptions as Systemctl reader can throw many different
        # errors, but any failure in systemctl means that timestamps cannot be
        # obtained
        print(e)
        return TIMESTAMP_UNKNOWN
    return status, kernel_start, kernel_end, cloudinit_sysd
Exemplo n.º 5
0
def devent2dev(devent):
    if devent.startswith("/dev/"):
        return devent
    else:
        result = util.get_mount_info(devent)
        if not result:
            raise ValueError("Could not determine device of '%s' % dev_ent")
        dev = result[0]

    container = util.is_container()

    # Ensure the path is a block device.
    if (dev == "/dev/root" and not os.path.exists(dev) and not container):
        dev = util.rootdev_from_cmdline(util.get_cmdline())
        if dev is None:
            raise ValueError("Unable to find device '/dev/root'")
    return dev
Exemplo n.º 6
0
def read_dmi_data(key):
    """
    Wrapper for reading DMI data.

    If running in a container return None.  This is because DMI data is
    assumed to be not useful in a container as it does not represent the
    container but rather the host.

    This will do the following (returning the first that produces a
    result):
        1) Use a mapping to translate `key` from dmidecode naming to
           sysfs naming and look in /sys/class/dmi/... for a value.
        2) Use `key` as a sysfs key directly and look in /sys/class/dmi/...
        3) Fall-back to passing `key` to `dmidecode --string`.

    If all of the above fail to find a value, None will be returned.
    """

    if is_container():
        return None

    if is_FreeBSD():
        return _read_kenv(key)

    syspath_value = _read_dmi_syspath(key)
    if syspath_value is not None:
        return syspath_value

    def is_x86(arch):
        return (arch == 'x86_64' or (arch[0] == 'i' and arch[2:] == '86'))

    # running dmidecode can be problematic on some arches (LP: #1243287)
    uname_arch = os.uname()[4]
    if not (is_x86(uname_arch) or uname_arch in ('aarch64', 'amd64')):
        LOG.debug("dmidata is not supported on %s", uname_arch)
        return None

    dmidecode_path = subp.which('dmidecode')
    if dmidecode_path:
        return _call_dmidecode(key, dmidecode_path)

    LOG.debug("did not find either path %s or dmidecode command",
              DMI_SYS_PATH)
    return None
Exemplo n.º 7
0
def devent2dev(devent):
    if devent.startswith("/dev/"):
        return devent
    else:
        result = util.get_mount_info(devent)
        if not result:
            raise ValueError("Could not determine device of '%s' % dev_ent")
        dev = result[0]

    container = util.is_container()

    # Ensure the path is a block device.
    if dev == "/dev/root" and not container:
        dev = util.rootdev_from_cmdline(util.get_cmdline())
        if dev is None:
            if os.path.exists(dev):
                # if /dev/root exists, but we failed to convert
                # that to a "real" /dev/ path device, then return it.
                return dev
            raise ValueError("Unable to find device '/dev/root'")
    return dev
Exemplo n.º 8
0
def devent2dev(devent):
    if devent.startswith("/dev/"):
        return devent
    else:
        result = util.get_mount_info(devent)
        if not result:
            raise ValueError("Could not determine device of '%s' % dev_ent")
        dev = result[0]

    container = util.is_container()

    # Ensure the path is a block device.
    if (dev == "/dev/root" and not container):
        dev = util.rootdev_from_cmdline(util.get_cmdline())
        if dev is None:
            if os.path.exists(dev):
                # if /dev/root exists, but we failed to convert
                # that to a "real" /dev/ path device, then return it.
                return dev
            raise ValueError("Unable to find device '/dev/root'")
    return dev
Exemplo n.º 9
0
def handle(name, cfg, _cloud, log, args):
    if len(args) != 0:
        resize_root = args[0]
    else:
        resize_root = util.get_cfg_option_str(cfg, "resize_rootfs", True)

    if not util.translate_bool(resize_root, addons=[NOBLOCK]):
        log.debug("Skipping module named %s, resizing disabled", name)
        return

    # TODO(harlowja) is the directory ok to be used??
    resize_root_d = util.get_cfg_option_str(cfg, "resize_rootfs_tmp", "/run")
    util.ensure_dir(resize_root_d)

    # TODO(harlowja): allow what is to be resized to be configurable??
    resize_what = "/"
    result = util.get_mount_info(resize_what, log)
    if not result:
        log.warn("Could not determine filesystem type of %s", resize_what)
        return

    (devpth, fs_type, mount_point) = result

    info = "dev=%s mnt_point=%s path=%s" % (devpth, mount_point, resize_what)
    log.debug("resize_info: %s" % info)

    container = util.is_container()

    # Ensure the path is a block device.
    if (devpth == "/dev/root" and not os.path.exists(devpth)
            and not container):
        devpth = util.rootdev_from_cmdline(util.get_cmdline())
        if devpth is None:
            log.warn("Unable to find device '/dev/root'")
            return
        log.debug("Converted /dev/root to '%s' per kernel cmdline", devpth)

    try:
        statret = os.stat(devpth)
    except OSError as exc:
        if container and exc.errno == errno.ENOENT:
            log.debug(
                "Device '%s' did not exist in container. "
                "cannot resize: %s", devpth, info)
        elif exc.errno == errno.ENOENT:
            log.warn("Device '%s' did not exist. cannot resize: %s", devpth,
                     info)
        else:
            raise exc
        return

    if not os.access(devpth, os.W_OK):
        if container:
            log.debug("'%s' not writable in container. cannot resize: %s",
                      devpth, info)
        else:
            log.warn("'%s' not writable. cannot resize: %s", devpth, info)
        return

    if not stat.S_ISBLK(statret.st_mode) and not stat.S_ISCHR(statret.st_mode):
        if container:
            log.debug("device '%s' not a block device in container."
                      " cannot resize: %s" % (devpth, info))
        else:
            log.warn("device '%s' not a block device. cannot resize: %s" %
                     (devpth, info))
        return

    resizer = None
    if can_skip_resize(fs_type, resize_what, devpth):
        log.debug("Skip resize filesystem type %s for %s", fs_type,
                  resize_what)
        return

    fstype_lc = fs_type.lower()
    for (pfix, root_cmd) in RESIZE_FS_PREFIXES_CMDS:
        if fstype_lc.startswith(pfix):
            resizer = root_cmd
            break

    if not resizer:
        log.warn("Not resizing unknown filesystem type %s for %s", fs_type,
                 resize_what)
        return

    resize_cmd = resizer(resize_what, devpth)
    log.debug("Resizing %s (%s) using %s", resize_what, fs_type,
              ' '.join(resize_cmd))

    if resize_root == NOBLOCK:
        # Fork to a child that will run
        # the resize command
        util.fork_cb(util.log_time,
                     logfunc=log.debug,
                     msg="backgrounded Resizing",
                     func=do_resize,
                     args=(resize_cmd, log))
    else:
        util.log_time(logfunc=log.debug,
                      msg="Resizing",
                      func=do_resize,
                      args=(resize_cmd, log))

    action = 'Resized'
    if resize_root == NOBLOCK:
        action = 'Resizing (via forking)'
    log.debug("%s root filesystem (type=%s, val=%s)", action, fs_type,
              resize_root)
Exemplo n.º 10
0
def handle(name, cfg, _cloud, log, args):
    if len(args) != 0:
        resize_root = args[0]
    else:
        resize_root = util.get_cfg_option_str(cfg, "resize_rootfs", True)

    if not util.translate_bool(resize_root, addons=[NOBLOCK]):
        log.debug("Skipping module named %s, resizing disabled", name)
        return

    # TODO(harlowja) is the directory ok to be used??
    resize_root_d = util.get_cfg_option_str(cfg, "resize_rootfs_tmp", "/run")
    util.ensure_dir(resize_root_d)

    # TODO(harlowja): allow what is to be resized to be configurable??
    resize_what = "/"
    result = util.get_mount_info(resize_what, log)
    if not result:
        log.warn("Could not determine filesystem type of %s", resize_what)
        return

    (devpth, fs_type, mount_point) = result

    # Ensure the path is a block device.
    info = "dev=%s mnt_point=%s path=%s" % (devpth, mount_point, resize_what)
    log.debug("resize_info: %s" % info)

    container = util.is_container()

    if (devpth == "/dev/root" and not os.path.exists(devpth) and
        not container):
        devpth = rootdev_from_cmdline(util.get_cmdline())
        if devpth is None:
            log.warn("Unable to find device '/dev/root'")
            return
        log.debug("Converted /dev/root to '%s' per kernel cmdline", devpth)

    try:
        statret = os.stat(devpth)
    except OSError as exc:
        if container and exc.errno == errno.ENOENT:
            log.debug("Device '%s' did not exist in container. "
                      "cannot resize: %s" % (devpth, info))
        elif exc.errno == errno.ENOENT:
            log.warn("Device '%s' did not exist. cannot resize: %s" %
                     (devpth, info))
        else:
            raise exc
        return

    if not stat.S_ISBLK(statret.st_mode) and not stat.S_ISCHR(statret.st_mode):
        if container:
            log.debug("device '%s' not a block device in container."
                      " cannot resize: %s" % (devpth, info))
        else:
            log.warn("device '%s' not a block device. cannot resize: %s" %
                     (devpth, info))
        return

    resizer = None
    fstype_lc = fs_type.lower()
    for (pfix, root_cmd) in RESIZE_FS_PREFIXES_CMDS:
        if fstype_lc.startswith(pfix):
            resizer = root_cmd
            break

    if not resizer:
        log.warn("Not resizing unknown filesystem type %s for %s",
                 fs_type, resize_what)
        return

    resize_cmd = resizer(resize_what, devpth)
    log.debug("Resizing %s (%s) using %s", resize_what, fs_type,
              ' '.join(resize_cmd))

    if resize_root == NOBLOCK:
        # Fork to a child that will run
        # the resize command
        util.fork_cb(
            util.log_time(logfunc=log.debug, msg="backgrounded Resizing",
                func=do_resize, args=(resize_cmd, log)))
    else:
        util.log_time(logfunc=log.debug, msg="Resizing",
            func=do_resize, args=(resize_cmd, log))

    action = 'Resized'
    if resize_root == NOBLOCK:
        action = 'Resizing (via forking)'
    log.debug("%s root filesystem (type=%s, val=%s)", action, fs_type,
              resize_root)
Exemplo n.º 11
0
def maybe_get_writable_device_path(devpath, info, log):
    """Return updated devpath if the devpath is a writable block device.

    @param devpath: Requested path to the root device we want to resize.
    @param info: String representing information about the requested device.
    @param log: Logger to which logs will be added upon error.

    @returns devpath or updated devpath per kernel commandline if the device
        path is a writable block device, returns None otherwise.
    """
    container = util.is_container()

    # Ensure the path is a block device.
    if (devpath == "/dev/root" and not os.path.exists(devpath) and
            not container):
        devpath = util.rootdev_from_cmdline(util.get_cmdline())
        if devpath is None:
            log.warn("Unable to find device '/dev/root'")
            return None
        log.debug("Converted /dev/root to '%s' per kernel cmdline", devpath)

    if devpath == 'overlayroot':
        log.debug("Not attempting to resize devpath '%s': %s", devpath, info)
        return None

    # FreeBSD zpool can also just use gpt/<label>
    # with that in mind we can not do an os.stat on "gpt/whatever"
    # therefore return the devpath already here.
    if devpath.startswith('gpt/'):
        log.debug('We have a gpt label - just go ahead')
        return devpath
    # Alternatively, our device could simply be a name as returned by gpart,
    # such as da0p3
    if not devpath.startswith('/dev/') and not os.path.exists(devpath):
        fulldevpath = '/dev/' + devpath.lstrip('/')
        log.debug("'%s' doesn't appear to be a valid device path. Trying '%s'",
                  devpath, fulldevpath)
        devpath = fulldevpath

    try:
        statret = os.stat(devpath)
    except OSError as exc:
        if container and exc.errno == errno.ENOENT:
            log.debug("Device '%s' did not exist in container. "
                      "cannot resize: %s", devpath, info)
        elif exc.errno == errno.ENOENT:
            log.warn("Device '%s' did not exist. cannot resize: %s",
                     devpath, info)
        else:
            raise exc
        return None

    if not stat.S_ISBLK(statret.st_mode) and not stat.S_ISCHR(statret.st_mode):
        if container:
            log.debug("device '%s' not a block device in container."
                      " cannot resize: %s" % (devpath, info))
        else:
            log.warn("device '%s' not a block device. cannot resize: %s" %
                     (devpath, info))
        return None
    return devpath  # The writable block devpath
Exemplo n.º 12
0
def handle(_name, cfg, _cloud, log, args):
    if len(args) != 0:
        resize_root = False
        if str(args[0]).lower() in ['true', '1', 'on', 'yes']:
            resize_root = True
    else:
        resize_root = util.get_cfg_option_str(cfg, "resize_rootfs", True)

    if str(resize_root).lower() in ['false', '0']:
        return

    # we use mktemp rather than mkstemp because early in boot nothing
    # else should be able to race us for this, and we need to mknod.
    devpth = tempfile.mktemp(prefix="cloudinit.resizefs.", dir="/run")

    try:
        st_dev = os.stat("/").st_dev
        dev = os.makedev(os.major(st_dev), os.minor(st_dev))
        os.mknod(devpth, 0400 | stat.S_IFBLK, dev)
    except:
        if util.is_container():
            log.debug("inside container, ignoring mknod failure in resizefs")
            return
        log.warn("Failed to make device node to resize /")
        raise

    cmd = ['blkid', '-c', '/dev/null', '-sTYPE', '-ovalue', devpth]
    try:
        (fstype, _err) = util.subp(cmd)
    except subprocess.CalledProcessError as e:
        log.warn("Failed to get filesystem type of maj=%s, min=%s via: %s" %
            (os.major(st_dev), os.minor(st_dev), cmd))
        log.warn("output=%s\nerror=%s\n", e.output[0], e.output[1])
        os.unlink(devpth)
        raise

    if str(fstype).startswith("ext"):
        resize_cmd = ['resize2fs', devpth]
    elif fstype == "xfs":
        resize_cmd = ['xfs_growfs', devpth]
    else:
        os.unlink(devpth)
        log.debug("not resizing unknown filesystem %s" % fstype)
        return

    if resize_root == "noblock":
        fid = os.fork()
        if fid == 0:
            try:
                do_resize(resize_cmd, devpth, log)
                os._exit(0)  # pylint: disable=W0212
            except Exception as exc:
                sys.stderr.write("Failed: %s" % exc)
                os._exit(1)  # pylint: disable=W0212
    else:
        do_resize(resize_cmd, devpth, log)

    log.debug("resizing root filesystem (type=%s, maj=%i, min=%i, val=%s)" %
        (str(fstype).rstrip("\n"), os.major(st_dev), os.minor(st_dev),
         resize_root))

    return
Exemplo n.º 13
0
def handle(_name, cfg, _cloud, log, args):
    if len(args) != 0:
        resize_root = False
        if str(args[0]).lower() in ['true', '1', 'on', 'yes']:
            resize_root = True
    else:
        resize_root = util.get_cfg_option_str(cfg, "resize_rootfs", True)

    if str(resize_root).lower() in ['false', '0']:
        return

    # we use mktemp rather than mkstemp because early in boot nothing
    # else should be able to race us for this, and we need to mknod.
    devpth = tempfile.mktemp(prefix="cloudinit.resizefs.", dir="/run")

    try:
        st_dev = os.stat("/").st_dev
        dev = os.makedev(os.major(st_dev), os.minor(st_dev))
        os.mknod(devpth, 0400 | stat.S_IFBLK, dev)
    except:
        if util.is_container():
            log.debug("inside container, ignoring mknod failure in resizefs")
            return
        log.warn("Failed to make device node to resize /")
        raise

    cmd = ['blkid', '-c', '/dev/null', '-sTYPE', '-ovalue', devpth]
    try:
        (fstype, _err) = util.subp(cmd)
    except subprocess.CalledProcessError as e:
        log.warn("Failed to get filesystem type of maj=%s, min=%s via: %s" %
                 (os.major(st_dev), os.minor(st_dev), cmd))
        log.warn("output=%s\nerror=%s\n", e.output[0], e.output[1])
        os.unlink(devpth)
        raise

    if str(fstype).startswith("ext"):
        resize_cmd = ['resize2fs', devpth]
    elif fstype == "xfs":
        resize_cmd = ['xfs_growfs', devpth]
    else:
        os.unlink(devpth)
        log.debug("not resizing unknown filesystem %s" % fstype)
        return

    if resize_root == "noblock":
        fid = os.fork()
        if fid == 0:
            try:
                do_resize(resize_cmd, devpth, log)
                os._exit(0)  # pylint: disable=W0212
            except Exception as exc:
                sys.stderr.write("Failed: %s" % exc)
                os._exit(1)  # pylint: disable=W0212
    else:
        do_resize(resize_cmd, devpth, log)

    log.debug("resizing root filesystem (type=%s, maj=%i, min=%i, val=%s)" %
              (str(fstype).rstrip("\n"), os.major(st_dev), os.minor(st_dev),
               resize_root))

    return