|
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
|