Blame cloudinit/config/cc_mounts.py

Packit Service a04d08
# Copyright (C) 2009-2010 Canonical Ltd.
Packit Service a04d08
# Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
Packit Service a04d08
#
Packit Service a04d08
# Author: Scott Moser <scott.moser@canonical.com>
Packit Service a04d08
# Author: Juerg Haefliger <juerg.haefliger@hp.com>
Packit Service a04d08
#
Packit Service a04d08
# This file is part of cloud-init. See LICENSE file for license information.
Packit Service a04d08
Packit Service a04d08
"""
Packit Service a04d08
Mounts
Packit Service a04d08
------
Packit Service a04d08
**Summary:** configure mount points and swap files
Packit Service a04d08
Packit Service a04d08
This module can add or remove mountpoints from ``/etc/fstab`` as well as
Packit Service a04d08
configure swap. The ``mounts`` config key takes a list of fstab entries to add.
Packit Service a04d08
Each entry is specified as a list of ``[ fs_spec, fs_file, fs_vfstype,
Packit Service a04d08
fs_mntops, fs-freq, fs_passno ]``. For more information on these options,
Packit Service a04d08
consult the manual for ``/etc/fstab``. When specifying the ``fs_spec``, if the
Packit Service a04d08
device name starts with one of ``xvd``, ``sd``, ``hd``, or ``vd``, the leading
Packit Service a04d08
``/dev`` may be omitted.
Packit Service a04d08
Packit Service a04d08
In order to remove a previously listed mount, an entry can be added to the
Packit Service a04d08
mounts list containing ``fs_spec`` for the device to be removed but no
Packit Service a04d08
mountpoint (i.e. ``[ sda1 ]`` or ``[ sda1, null ]``).
Packit Service a04d08
Packit Service a04d08
The ``mount_default_fields`` config key allows default options to be specified
Packit Service a04d08
for the values in a ``mounts`` entry that are not specified, aside from the
Packit Service a04d08
``fs_spec`` and the ``fs_file``. If specified, this must be a list containing 6
Packit Service a04d08
values. It defaults to::
Packit Service a04d08
Packit Service a04d08
    mount_default_fields: [none, none, "auto", "defaults,nobootwait", "0", "2"]
Packit Service a04d08
Packit Service a04d08
On a systemd booted system that default is the mostly equivalent::
Packit Service a04d08
Packit Service a04d08
    mount_default_fields: [none, none, "auto",
Packit Service a04d08
       "defaults,nofail,x-systemd.requires=cloud-init.service", "0", "2"]
Packit Service a04d08
Packit Service a04d08
Note that `nobootwait` is an upstart specific boot option that somewhat
Packit Service a04d08
equates to the more standard `nofail`.
Packit Service a04d08
Packit Service a04d08
Swap files can be configured by setting the path to the swap file to create
Packit Service a04d08
with ``filename``, the size of the swap file with ``size`` maximum size of
Packit Service a04d08
the swap file if using an ``size: auto`` with ``maxsize``. By default no
Packit Service a04d08
swap file is created.
Packit Service a04d08
Packit Service a04d08
**Internal name:** ``cc_mounts``
Packit Service a04d08
Packit Service a04d08
**Module frequency:** per instance
Packit Service a04d08
Packit Service a04d08
**Supported distros:** all
Packit Service a04d08
Packit Service a04d08
**Config keys**::
Packit Service a04d08
Packit Service a04d08
    mounts:
Packit Service a04d08
        - [ /dev/ephemeral0, /mnt, auto, "defaults,noexec" ]
Packit Service a04d08
        - [ sdc, /opt/data ]
Packit Service a04d08
        - [ xvdh, /opt/data, "auto", "defaults,nofail", "0", "0" ]
Packit Service a04d08
    mount_default_fields: [None, None, "auto", "defaults,nofail", "0", "2"]
Packit Service a04d08
    swap:
Packit Service a04d08
        filename: <file>
Packit Service a04d08
        size: <"auto"/size in bytes>
Packit Service a04d08
        maxsize: <size in bytes>
Packit Service a04d08
"""
Packit Service a04d08
Packit Service a04d08
from string import whitespace
Packit Service a04d08
Packit Service a04d08
import logging
Packit Service a04d08
import os
Packit Service a04d08
import re
Packit Service a04d08
Packit Service a04d08
from cloudinit import type_utils
Packit Service a04d08
from cloudinit import subp
Packit Service a04d08
from cloudinit import util
Packit Service a04d08
Packit Service a04d08
# Shortname matches 'sda', 'sda1', 'xvda', 'hda', 'sdb', xvdb, vda, vdd1, sr0
Packit Service a04d08
DEVICE_NAME_FILTER = r"^([x]{0,1}[shv]d[a-z][0-9]*|sr[0-9]+)$"
Packit Service a04d08
DEVICE_NAME_RE = re.compile(DEVICE_NAME_FILTER)
Packit Service a04d08
# Name matches 'server:/path'
Packit Service a04d08
NETWORK_NAME_FILTER = r"^.+:.*"
Packit Service a04d08
NETWORK_NAME_RE = re.compile(NETWORK_NAME_FILTER)
Packit Service a04d08
WS = re.compile("[%s]+" % (whitespace))
Packit Service a04d08
FSTAB_PATH = "/etc/fstab"
Packit Service a04d08
MNT_COMMENT = "comment=cloudconfig"
Packit Service a04d08
Packit Service a04d08
LOG = logging.getLogger(__name__)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def is_meta_device_name(name):
Packit Service a04d08
    # return true if this is a metadata service name
Packit Service a04d08
    if name in ["ami", "root", "swap"]:
Packit Service a04d08
        return True
Packit Service a04d08
    # names 'ephemeral0' or 'ephemeral1'
Packit Service a04d08
    # 'ebs[0-9]' appears when '--block-device-mapping sdf=snap-d4d90bbc'
Packit Service a04d08
    for enumname in ("ephemeral", "ebs"):
Packit Service a04d08
        if name.startswith(enumname) and name.find(":") == -1:
Packit Service a04d08
            return True
Packit Service a04d08
    return False
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def is_network_device(name):
Packit Service a04d08
    # return true if this is a network device
Packit Service a04d08
    if NETWORK_NAME_RE.match(name):
Packit Service a04d08
        return True
Packit Service a04d08
    return False
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def _get_nth_partition_for_device(device_path, partition_number):
Packit Service a04d08
    potential_suffixes = [str(partition_number), 'p%s' % (partition_number,),
Packit Service a04d08
                          '-part%s' % (partition_number,)]
Packit Service a04d08
    for suffix in potential_suffixes:
Packit Service a04d08
        potential_partition_device = '%s%s' % (device_path, suffix)
Packit Service a04d08
        if os.path.exists(potential_partition_device):
Packit Service a04d08
            return potential_partition_device
Packit Service a04d08
    return None
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def _is_block_device(device_path, partition_path=None):
Packit Service a04d08
    device_name = os.path.realpath(device_path).split('/')[-1]
Packit Service a04d08
    sys_path = os.path.join('/sys/block/', device_name)
Packit Service a04d08
    if partition_path is not None:
Packit Service a04d08
        sys_path = os.path.join(
Packit Service a04d08
            sys_path, os.path.realpath(partition_path).split('/')[-1])
Packit Service a04d08
    return os.path.exists(sys_path)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def sanitize_devname(startname, transformer, log):
Packit Service a04d08
    log.debug("Attempting to determine the real name of %s", startname)
Packit Service a04d08
Packit Service a04d08
    # workaround, allow user to specify 'ephemeral'
Packit Service a04d08
    # rather than more ec2 correct 'ephemeral0'
Packit Service a04d08
    devname = startname
Packit Service a04d08
    if devname == "ephemeral":
Packit Service a04d08
        devname = "ephemeral0"
Packit Service a04d08
        log.debug("Adjusted mount option from ephemeral to ephemeral0")
Packit Service a04d08
Packit Service a04d08
    if is_network_device(startname):
Packit Service a04d08
        return startname
Packit Service a04d08
Packit Service a04d08
    device_path, partition_number = util.expand_dotted_devname(devname)
Packit Service a04d08
Packit Service a04d08
    if is_meta_device_name(device_path):
Packit Service a04d08
        orig = device_path
Packit Service a04d08
        device_path = transformer(device_path)
Packit Service a04d08
        if not device_path:
Packit Service a04d08
            return None
Packit Service a04d08
        if not device_path.startswith("/"):
Packit Service a04d08
            device_path = "/dev/%s" % (device_path,)
Packit Service a04d08
        log.debug("Mapped metadata name %s to %s", orig, device_path)
Packit Service a04d08
    else:
Packit Service a04d08
        if DEVICE_NAME_RE.match(startname):
Packit Service a04d08
            device_path = "/dev/%s" % (device_path,)
Packit Service a04d08
Packit Service a04d08
    partition_path = None
Packit Service a04d08
    if partition_number is None:
Packit Service a04d08
        partition_path = _get_nth_partition_for_device(device_path, 1)
Packit Service a04d08
    else:
Packit Service a04d08
        partition_path = _get_nth_partition_for_device(device_path,
Packit Service a04d08
                                                       partition_number)
Packit Service a04d08
        if partition_path is None:
Packit Service a04d08
            return None
Packit Service a04d08
Packit Service a04d08
    if _is_block_device(device_path, partition_path):
Packit Service a04d08
        if partition_path is not None:
Packit Service a04d08
            return partition_path
Packit Service a04d08
        return device_path
Packit Service a04d08
    return None
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def suggested_swapsize(memsize=None, maxsize=None, fsys=None):
Packit Service a04d08
    # make a suggestion on the size of swap for this system.
Packit Service a04d08
    if memsize is None:
Packit Service a04d08
        memsize = util.read_meminfo()['total']
Packit Service a04d08
Packit Service a04d08
    GB = 2 ** 30
Packit Service a04d08
    sugg_max = 8 * GB
Packit Service a04d08
Packit Service a04d08
    info = {'avail': 'na', 'max_in': maxsize, 'mem': memsize}
Packit Service a04d08
Packit Service a04d08
    if fsys is None and maxsize is None:
Packit Service a04d08
        # set max to 8GB default if no filesystem given
Packit Service a04d08
        maxsize = sugg_max
Packit Service a04d08
    elif fsys:
Packit Service a04d08
        statvfs = os.statvfs(fsys)
Packit Service a04d08
        avail = statvfs.f_frsize * statvfs.f_bfree
Packit Service a04d08
        info['avail'] = avail
Packit Service a04d08
Packit Service a04d08
        if maxsize is None:
Packit Service a04d08
            # set to 25% of filesystem space
Packit Service a04d08
            maxsize = min(int(avail / 4), sugg_max)
Packit Service a04d08
        elif maxsize > ((avail * .9)):
Packit Service a04d08
            # set to 90% of available disk space
Packit Service a04d08
            maxsize = int(avail * .9)
Packit Service a04d08
    elif maxsize is None:
Packit Service a04d08
        maxsize = sugg_max
Packit Service a04d08
Packit Service a04d08
    info['max'] = maxsize
Packit Service a04d08
Packit Service a04d08
    formulas = [
Packit Service a04d08
        # < 1G: swap = double memory
Packit Service a04d08
        (1 * GB, lambda x: x * 2),
Packit Service a04d08
        # < 2G: swap = 2G
Packit Service a04d08
        (2 * GB, lambda x: 2 * GB),
Packit Service a04d08
        # < 4G: swap = memory
Packit Service a04d08
        (4 * GB, lambda x: x),
Packit Service a04d08
        # < 16G: 4G
Packit Service a04d08
        (16 * GB, lambda x: 4 * GB),
Packit Service a04d08
        # < 64G: 1/2 M up to max
Packit Service a04d08
        (64 * GB, lambda x: x / 2),
Packit Service a04d08
    ]
Packit Service a04d08
Packit Service a04d08
    size = None
Packit Service a04d08
    for top, func in formulas:
Packit Service a04d08
        if memsize <= top:
Packit Service a04d08
            size = min(func(memsize), maxsize)
Packit Service a04d08
            # if less than 1/2 memory and not much, return 0
Packit Service a04d08
            if size < (memsize / 2) and size < 4 * GB:
Packit Service a04d08
                size = 0
Packit Service a04d08
                break
Packit Service a04d08
            break
Packit Service a04d08
Packit Service a04d08
    if size is not None:
Packit Service a04d08
        size = maxsize
Packit Service a04d08
Packit Service a04d08
    info['size'] = size
Packit Service a04d08
Packit Service a04d08
    MB = 2 ** 20
Packit Service a04d08
    pinfo = {}
Packit Service a04d08
    for k, v in info.items():
Packit Service a04d08
        if isinstance(v, int):
Packit Service a04d08
            pinfo[k] = "%s MB" % (v / MB)
Packit Service a04d08
        else:
Packit Service a04d08
            pinfo[k] = v
Packit Service a04d08
Packit Service a04d08
    LOG.debug("suggest %s swap for %s memory with '%s'"
Packit Service a04d08
              " disk given max=%s [max=%s]'", pinfo['size'], pinfo['mem'],
Packit Service a04d08
              pinfo['avail'], pinfo['max_in'], pinfo['max'])
Packit Service a04d08
    return size
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def create_swapfile(fname: str, size: str) -> None:
Packit Service a04d08
    """Size is in MiB."""
Packit Service a04d08
Packit Service a04d08
    errmsg = "Failed to create swapfile '%s' of size %sMB via %s: %s"
Packit Service a04d08
Packit Service a04d08
    def create_swap(fname, size, method):
Packit Service a04d08
        LOG.debug("Creating swapfile in '%s' on fstype '%s' using '%s'",
Packit Service a04d08
                  fname, fstype, method)
Packit Service a04d08
Packit Service a04d08
        if method == "fallocate":
Packit Service a04d08
            cmd = ['fallocate', '-l', '%sM' % size, fname]
Packit Service a04d08
        elif method == "dd":
Packit Service a04d08
            cmd = ['dd', 'if=/dev/zero', 'of=%s' % fname, 'bs=1M',
Packit Service a04d08
                   'count=%s' % size]
Packit Service a04d08
Packit Service a04d08
        try:
Packit Service a04d08
            subp.subp(cmd, capture=True)
Packit Service a04d08
        except subp.ProcessExecutionError as e:
Packit Service a04d08
            LOG.warning(errmsg, fname, size, method, e)
Packit Service a04d08
            util.del_file(fname)
Packit Service a04d08
Packit Service a04d08
    swap_dir = os.path.dirname(fname)
Packit Service a04d08
    util.ensure_dir(swap_dir)
Packit Service a04d08
Packit Service a04d08
    fstype = util.get_mount_info(swap_dir)[1]
Packit Service a04d08
Packit Service a04d08
    if (fstype == "xfs" and
Packit Service a04d08
            util.kernel_version() < (4, 18)) or fstype == "btrfs":
Packit Service a04d08
        create_swap(fname, size, "dd")
Packit Service a04d08
    else:
Packit Service a04d08
        try:
Packit Service a04d08
            create_swap(fname, size, "fallocate")
Packit Service a04d08
        except subp.ProcessExecutionError as e:
Packit Service a04d08
            LOG.warning(errmsg, fname, size, "dd", e)
Packit Service a04d08
            LOG.warning("Will attempt with dd.")
Packit Service a04d08
            create_swap(fname, size, "dd")
Packit Service a04d08
Packit Service a04d08
    if os.path.exists(fname):
Packit Service a04d08
        util.chmod(fname, 0o600)
Packit Service a04d08
    try:
Packit Service a04d08
        subp.subp(['mkswap', fname])
Packit Service a04d08
    except subp.ProcessExecutionError:
Packit Service a04d08
        util.del_file(fname)
Packit Service a04d08
        raise
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def setup_swapfile(fname, size=None, maxsize=None):
Packit Service a04d08
    """
Packit Service a04d08
    fname: full path string of filename to setup
Packit Service a04d08
    size: the size to create. set to "auto" for recommended
Packit Service a04d08
    maxsize: the maximum size
Packit Service a04d08
    """
Packit Service a04d08
    swap_dir = os.path.dirname(fname)
Packit Service a04d08
    if str(size).lower() == "auto":
Packit Service a04d08
        try:
Packit Service a04d08
            memsize = util.read_meminfo()['total']
Packit Service a04d08
        except IOError:
Packit Service a04d08
            LOG.debug("Not creating swap: failed to read meminfo")
Packit Service a04d08
            return
Packit Service a04d08
Packit Service a04d08
        util.ensure_dir(swap_dir)
Packit Service a04d08
        size = suggested_swapsize(fsys=swap_dir, maxsize=maxsize,
Packit Service a04d08
                                  memsize=memsize)
Packit Service a04d08
Packit Service a04d08
    mibsize = str(int(size / (2 ** 20)))
Packit Service a04d08
    if not size:
Packit Service a04d08
        LOG.debug("Not creating swap: suggested size was 0")
Packit Service a04d08
        return
Packit Service a04d08
Packit Service a04d08
    util.log_time(LOG.debug, msg="Setting up swap file", func=create_swapfile,
Packit Service a04d08
                  args=[fname, mibsize])
Packit Service a04d08
Packit Service a04d08
    return fname
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def handle_swapcfg(swapcfg):
Packit Service a04d08
    """handle the swap config, calling setup_swap if necessary.
Packit Service a04d08
       return None or (filename, size)
Packit Service a04d08
    """
Packit Service a04d08
    if not isinstance(swapcfg, dict):
Packit Service a04d08
        LOG.warning("input for swap config was not a dict.")
Packit Service a04d08
        return None
Packit Service a04d08
Packit Service a04d08
    fname = swapcfg.get('filename', '/swap.img')
Packit Service a04d08
    size = swapcfg.get('size', 0)
Packit Service a04d08
    maxsize = swapcfg.get('maxsize', None)
Packit Service a04d08
Packit Service a04d08
    if not (size and fname):
Packit Service a04d08
        LOG.debug("no need to setup swap")
Packit Service a04d08
        return
Packit Service a04d08
Packit Service a04d08
    if os.path.exists(fname):
Packit Service a04d08
        if not os.path.exists("/proc/swaps"):
Packit Service a04d08
            LOG.debug("swap file %s exists, but no /proc/swaps exists, "
Packit Service a04d08
                      "being safe", fname)
Packit Service a04d08
            return fname
Packit Service a04d08
        try:
Packit Service a04d08
            for line in util.load_file("/proc/swaps").splitlines():
Packit Service a04d08
                if line.startswith(fname + " "):
Packit Service a04d08
                    LOG.debug("swap file %s already in use", fname)
Packit Service a04d08
                    return fname
Packit Service a04d08
            LOG.debug("swap file %s exists, but not in /proc/swaps", fname)
Packit Service a04d08
        except Exception:
Packit Service a04d08
            LOG.warning("swap file %s exists. Error reading /proc/swaps",
Packit Service a04d08
                        fname)
Packit Service a04d08
            return fname
Packit Service a04d08
Packit Service a04d08
    try:
Packit Service a04d08
        if isinstance(size, str) and size != "auto":
Packit Service a04d08
            size = util.human2bytes(size)
Packit Service a04d08
        if isinstance(maxsize, str):
Packit Service a04d08
            maxsize = util.human2bytes(maxsize)
Packit Service a04d08
        return setup_swapfile(fname=fname, size=size, maxsize=maxsize)
Packit Service a04d08
Packit Service a04d08
    except Exception as e:
Packit Service a04d08
        LOG.warning("failed to setup swap: %s", e)
Packit Service a04d08
Packit Service a04d08
    return None
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def handle(_name, cfg, cloud, log, _args):
Packit Service a04d08
    # fs_spec, fs_file, fs_vfstype, fs_mntops, fs-freq, fs_passno
Packit Service a04d08
    def_mnt_opts = "defaults,nobootwait"
Packit Service a04d08
    uses_systemd = cloud.distro.uses_systemd()
Packit Service a04d08
    if uses_systemd:
Packit Service a04d08
        def_mnt_opts = "defaults,nofail,x-systemd.requires=cloud-init.service"
Packit Service a04d08
Packit Service a04d08
    defvals = [None, None, "auto", def_mnt_opts, "0", "2"]
Packit Service a04d08
    defvals = cfg.get("mount_default_fields", defvals)
Packit Service a04d08
Packit Service a04d08
    # these are our default set of mounts
Packit Service a04d08
    defmnts = [["ephemeral0", "/mnt", "auto", defvals[3], "0", "2"],
Packit Service a04d08
               ["swap", "none", "swap", "sw", "0", "0"]]
Packit Service a04d08
Packit Service a04d08
    cfgmnt = []
Packit Service a04d08
    if "mounts" in cfg:
Packit Service a04d08
        cfgmnt = cfg["mounts"]
Packit Service a04d08
Packit Service a04d08
    LOG.debug("mounts configuration is %s", cfgmnt)
Packit Service a04d08
Packit Service a04d08
    fstab_lines = []
Packit Service a04d08
    fstab_devs = {}
Packit Service a04d08
    fstab_removed = []
Packit Service a04d08
Packit Service a04d08
    if os.path.exists(FSTAB_PATH):
Packit Service a04d08
        for line in util.load_file(FSTAB_PATH).splitlines():
Packit Service a04d08
            if MNT_COMMENT in line:
Packit Service a04d08
                fstab_removed.append(line)
Packit Service a04d08
                continue
Packit Service a04d08
Packit Service a04d08
            try:
Packit Service a04d08
                toks = WS.split(line)
Packit Service a04d08
            except Exception:
Packit Service a04d08
                pass
Packit Service a04d08
            fstab_devs[toks[0]] = line
Packit Service a04d08
            fstab_lines.append(line)
Packit Service a04d08
Packit Service a04d08
    for i in range(len(cfgmnt)):
Packit Service a04d08
        # skip something that wasn't a list
Packit Service a04d08
        if not isinstance(cfgmnt[i], list):
Packit Service a04d08
            log.warning("Mount option %s not a list, got a %s instead",
Packit Service a04d08
                        (i + 1), type_utils.obj_name(cfgmnt[i]))
Packit Service a04d08
            continue
Packit Service a04d08
Packit Service a04d08
        start = str(cfgmnt[i][0])
Packit Service a04d08
        sanitized = sanitize_devname(start, cloud.device_name_to_device, log)
Packit Service a04d08
        if sanitized != start:
Packit Service a04d08
            log.debug("changed %s => %s" % (start, sanitized))
Packit Service a04d08
Packit Service a04d08
        if sanitized is None:
Packit Service a04d08
            log.debug("Ignoring nonexistent named mount %s", start)
Packit Service a04d08
            continue
Packit Service a04d08
        elif sanitized in fstab_devs:
Packit Service a04d08
            log.info("Device %s already defined in fstab: %s",
Packit Service a04d08
                     sanitized, fstab_devs[sanitized])
Packit Service a04d08
            continue
Packit Service a04d08
Packit Service a04d08
        cfgmnt[i][0] = sanitized
Packit Service a04d08
Packit Service a04d08
        # in case the user did not quote a field (likely fs-freq, fs_passno)
Packit Service a04d08
        # but do not convert None to 'None' (LP: #898365)
Packit Service a04d08
        for j in range(len(cfgmnt[i])):
Packit Service a04d08
            if cfgmnt[i][j] is None:
Packit Service a04d08
                continue
Packit Service a04d08
            else:
Packit Service a04d08
                cfgmnt[i][j] = str(cfgmnt[i][j])
Packit Service a04d08
Packit Service a04d08
    for i in range(len(cfgmnt)):
Packit Service a04d08
        # fill in values with defaults from defvals above
Packit Service a04d08
        for j in range(len(defvals)):
Packit Service a04d08
            if len(cfgmnt[i]) <= j:
Packit Service a04d08
                cfgmnt[i].append(defvals[j])
Packit Service a04d08
            elif cfgmnt[i][j] is None:
Packit Service a04d08
                cfgmnt[i][j] = defvals[j]
Packit Service a04d08
Packit Service a04d08
        # if the second entry in the list is 'None' this
Packit Service a04d08
        # clears all previous entries of that same 'fs_spec'
Packit Service a04d08
        # (fs_spec is the first field in /etc/fstab, ie, that device)
Packit Service a04d08
        if cfgmnt[i][1] is None:
Packit Service a04d08
            for j in range(i):
Packit Service a04d08
                if cfgmnt[j][0] == cfgmnt[i][0]:
Packit Service a04d08
                    cfgmnt[j][1] = None
Packit Service a04d08
Packit Service a04d08
    # for each of the "default" mounts, add them only if no other
Packit Service a04d08
    # entry has the same device name
Packit Service a04d08
    for defmnt in defmnts:
Packit Service a04d08
        start = defmnt[0]
Packit Service a04d08
        sanitized = sanitize_devname(start, cloud.device_name_to_device, log)
Packit Service a04d08
        if sanitized != start:
Packit Service a04d08
            log.debug("changed default device %s => %s" % (start, sanitized))
Packit Service a04d08
Packit Service a04d08
        if sanitized is None:
Packit Service a04d08
            log.debug("Ignoring nonexistent default named mount %s", start)
Packit Service a04d08
            continue
Packit Service a04d08
        elif sanitized in fstab_devs:
Packit Service a04d08
            log.debug("Device %s already defined in fstab: %s",
Packit Service a04d08
                      sanitized, fstab_devs[sanitized])
Packit Service a04d08
            continue
Packit Service a04d08
Packit Service a04d08
        defmnt[0] = sanitized
Packit Service a04d08
Packit Service a04d08
        cfgmnt_has = False
Packit Service a04d08
        for cfgm in cfgmnt:
Packit Service a04d08
            if cfgm[0] == defmnt[0]:
Packit Service a04d08
                cfgmnt_has = True
Packit Service a04d08
                break
Packit Service a04d08
Packit Service a04d08
        if cfgmnt_has:
Packit Service a04d08
            log.debug(("Not including %s, already"
Packit Service a04d08
                       " previously included"), start)
Packit Service a04d08
            continue
Packit Service a04d08
        cfgmnt.append(defmnt)
Packit Service a04d08
Packit Service a04d08
    # now, each entry in the cfgmnt list has all fstab values
Packit Service a04d08
    # if the second field is None (not the string, the value) we skip it
Packit Service a04d08
    actlist = []
Packit Service a04d08
    for x in cfgmnt:
Packit Service a04d08
        if x[1] is None:
Packit Service a04d08
            log.debug("Skipping nonexistent device named %s", x[0])
Packit Service a04d08
        else:
Packit Service a04d08
            actlist.append(x)
Packit Service a04d08
Packit Service a04d08
    swapret = handle_swapcfg(cfg.get('swap', {}))
Packit Service a04d08
    if swapret:
Packit Service a04d08
        actlist.append([swapret, "none", "swap", "sw", "0", "0"])
Packit Service a04d08
Packit Service a04d08
    if len(actlist) == 0:
Packit Service a04d08
        log.debug("No modifications to fstab needed")
Packit Service a04d08
        return
Packit Service a04d08
Packit Service a04d08
    cc_lines = []
Packit Service a04d08
    needswap = False
Packit Service a04d08
    need_mount_all = False
Packit Service a04d08
    dirs = []
Packit Service a04d08
    for line in actlist:
Packit Service a04d08
        # write 'comment' in the fs_mntops, entry,  claiming this
Packit Service a04d08
        line[3] = "%s,%s" % (line[3], MNT_COMMENT)
Packit Service a04d08
        if line[2] == "swap":
Packit Service a04d08
            needswap = True
Packit Service a04d08
        if line[1].startswith("/"):
Packit Service a04d08
            dirs.append(line[1])
Packit Service a04d08
        cc_lines.append('\t'.join(line))
Packit Service a04d08
Packit Service a04d08
    mount_points = [v['mountpoint'] for k, v in util.mounts().items()
Packit Service a04d08
                    if 'mountpoint' in v]
Packit Service a04d08
    for d in dirs:
Packit Service a04d08
        try:
Packit Service a04d08
            util.ensure_dir(d)
Packit Service a04d08
        except Exception:
Packit Service a04d08
            util.logexc(log, "Failed to make '%s' config-mount", d)
Packit Service a04d08
        # dirs is list of directories on which a volume should be mounted.
Packit Service a04d08
        # If any of them does not already show up in the list of current
Packit Service a04d08
        # mount points, we will definitely need to do mount -a.
Packit Service a04d08
        if not need_mount_all and d not in mount_points:
Packit Service a04d08
            need_mount_all = True
Packit Service a04d08
Packit Service a04d08
    sadds = [WS.sub(" ", n) for n in cc_lines]
Packit Service a04d08
    sdrops = [WS.sub(" ", n) for n in fstab_removed]
Packit Service a04d08
Packit Service a04d08
    sops = (["- " + drop for drop in sdrops if drop not in sadds] +
Packit Service a04d08
            ["+ " + add for add in sadds if add not in sdrops])
Packit Service a04d08
Packit Service a04d08
    fstab_lines.extend(cc_lines)
Packit Service a04d08
    contents = "%s\n" % ('\n'.join(fstab_lines))
Packit Service a04d08
    util.write_file(FSTAB_PATH, contents)
Packit Service a04d08
Packit Service a04d08
    activate_cmds = []
Packit Service a04d08
    if needswap:
Packit Service a04d08
        activate_cmds.append(["swapon", "-a"])
Packit Service a04d08
Packit Service a04d08
    if len(sops) == 0:
Packit Service a04d08
        log.debug("No changes to /etc/fstab made.")
Packit Service a04d08
    else:
Packit Service a04d08
        log.debug("Changes to fstab: %s", sops)
Packit Service a04d08
        need_mount_all = True
Packit Service a04d08
Packit Service a04d08
    if need_mount_all:
Packit Service a04d08
        activate_cmds.append(["mount", "-a"])
Packit Service a04d08
        if uses_systemd:
Packit Service a04d08
            activate_cmds.append(["systemctl", "daemon-reload"])
Packit Service a04d08
Packit Service a04d08
    fmt = "Activating swap and mounts with: %s"
Packit Service a04d08
    for cmd in activate_cmds:
Packit Service a04d08
        fmt = "Activate mounts: %s:" + ' '.join(cmd)
Packit Service a04d08
        try:
Packit Service a04d08
            subp.subp(cmd)
Packit Service a04d08
            log.debug(fmt, "PASS")
Packit Service a04d08
        except subp.ProcessExecutionError:
Packit Service a04d08
            log.warning(fmt, "FAIL")
Packit Service a04d08
            util.logexc(log, fmt, "FAIL")
Packit Service a04d08
Packit Service a04d08
# vi: ts=4 expandtab