|
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: Ben Howard <ben.howard@canonical.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 |
Disk Setup
|
|
Packit Service |
a04d08 |
----------
|
|
Packit Service |
a04d08 |
**Summary:** configure partitions and filesystems
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
This module is able to configure simple partition tables and filesystems.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
.. note::
|
|
Packit Service |
a04d08 |
for more detail about configuration options for disk setup, see the disk
|
|
Packit Service |
a04d08 |
setup example
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
For convenience, aliases can be specified for disks using the
|
|
Packit Service |
a04d08 |
``device_aliases`` config key, which takes a dictionary of alias: path
|
|
Packit Service |
a04d08 |
mappings. There are automatic aliases for ``swap`` and ``ephemeral<X>``, where
|
|
Packit Service |
a04d08 |
``swap`` will always refer to the active swap partition and ``ephemeral<X>``
|
|
Packit Service |
a04d08 |
will refer to the block device of the ephemeral image.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
Disk partitioning is done using the ``disk_setup`` directive. This config
|
|
Packit Service |
a04d08 |
directive accepts a dictionary where each key is either a path to a block
|
|
Packit Service |
a04d08 |
device or an alias specified in ``device_aliases``, and each value is the
|
|
Packit Service |
a04d08 |
configuration options for the device. The ``table_type`` option specifies the
|
|
Packit Service |
a04d08 |
partition table type, either ``mbr`` or ``gpt``. The ``layout`` option
|
|
Packit Service |
a04d08 |
specifies how partitions on the device are to be arranged. If ``layout`` is set
|
|
Packit Service |
a04d08 |
to ``true``, a single partition using all the space on the device will be
|
|
Packit Service |
a04d08 |
created. If set to ``false``, no partitions will be created. Partitions can be
|
|
Packit Service |
a04d08 |
specified by providing a list to ``layout``, where each entry in the list is
|
|
Packit Service |
a04d08 |
either a size or a list containing a size and the numerical value for a
|
|
Packit Service |
a04d08 |
partition type. The size for partitions is specified in **percentage** of disk
|
|
Packit Service |
a04d08 |
space, not in bytes (e.g. a size of 33 would take up 1/3 of the disk space).
|
|
Packit Service |
a04d08 |
The ``overwrite`` option controls whether this module tries to be safe about
|
|
Packit Service |
a04d08 |
writing partition talbes or not. If ``overwrite: false`` is set, the device
|
|
Packit Service |
a04d08 |
will be checked for a partition table and for a file system and if either is
|
|
Packit Service |
a04d08 |
found, the operation will be skipped. If ``overwrite: true`` is set, no checks
|
|
Packit Service |
a04d08 |
will be performed.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
.. note::
|
|
Packit Service |
a04d08 |
Using ``overwrite: true`` is dangerous and can lead to data loss, so double
|
|
Packit Service |
a04d08 |
check that the correct device has been specified if using this option.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
File system configuration is done using the ``fs_setup`` directive. This config
|
|
Packit Service |
a04d08 |
directive accepts a list of filesystem configs. The device to create the
|
|
Packit Service |
a04d08 |
filesystem on may be specified either as a path or as an alias in the format
|
|
Packit Service |
a04d08 |
``<alias name>.<y>`` where ``<y>`` denotes the partition number on the device.
|
|
Packit Service |
a04d08 |
The partition can also be specified by setting ``partition`` to the desired
|
|
Packit Service |
a04d08 |
partition number. The ``partition`` option may also be set to ``auto``, in
|
|
Packit Service |
a04d08 |
which this module will search for the existance of a filesystem matching the
|
|
Packit Service |
a04d08 |
``label``, ``type`` and ``device`` of the ``fs_setup`` entry and will skip
|
|
Packit Service |
a04d08 |
creating the filesystem if one is found. The ``partition`` option may also be
|
|
Packit Service |
a04d08 |
set to ``any``, in which case any file system that matches ``type`` and
|
|
Packit Service |
a04d08 |
``device`` will cause this module to skip filesystem creation for the
|
|
Packit Service |
a04d08 |
``fs_setup`` entry, regardless of ``label`` matching or not. To write a
|
|
Packit Service |
a04d08 |
filesystem directly to a device, use ``partition: none``. A label can be
|
|
Packit Service |
a04d08 |
specified for the filesystem using ``label``, and the filesystem type can be
|
|
Packit Service |
a04d08 |
specified using ``filesystem``.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
.. note::
|
|
Packit Service |
a04d08 |
If specifying device using the ``<device name>.<partition number>`` format,
|
|
Packit Service |
a04d08 |
the value of ``partition`` will be overwritten.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
.. note::
|
|
Packit Service |
a04d08 |
Using ``overwrite: true`` for filesystems is dangerous and can lead to data
|
|
Packit Service |
a04d08 |
loss, so double check the entry in ``fs_setup``.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
.. note::
|
|
Packit Service |
a04d08 |
``replace_fs`` is ignored unless ``partition`` is ``auto`` or ``any``.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
**Internal name:** ``cc_disk_setup``
|
|
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 |
device_aliases:
|
|
Packit Service |
a04d08 |
<alias name>: <device path>
|
|
Packit Service |
a04d08 |
disk_setup:
|
|
Packit Service |
a04d08 |
<alias name/path>:
|
|
Packit Service |
a04d08 |
table_type: <'mbr'/'gpt'>
|
|
Packit Service |
a04d08 |
layout:
|
|
Packit Service |
a04d08 |
- [33,82]
|
|
Packit Service |
a04d08 |
- 66
|
|
Packit Service |
a04d08 |
overwrite: <true/false>
|
|
Packit Service |
a04d08 |
fs_setup:
|
|
Packit Service |
a04d08 |
- label: <label>
|
|
Packit Service |
a04d08 |
filesystem: <filesystem type>
|
|
Packit Service |
a04d08 |
device: <device>
|
|
Packit Service |
a04d08 |
partition: <"auto"/"any"/"none"/<partition number>>
|
|
Packit Service |
a04d08 |
overwrite: <true/false>
|
|
Packit Service |
a04d08 |
replace_fs: <filesystem type>
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
from cloudinit.settings import PER_INSTANCE
|
|
Packit Service |
a04d08 |
from cloudinit import util
|
|
Packit Service |
751c4a |
from cloudinit import subp
|
|
Packit Service |
a04d08 |
import logging
|
|
Packit Service |
a04d08 |
import os
|
|
Packit Service |
a04d08 |
import shlex
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
frequency = PER_INSTANCE
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Define the commands to use
|
|
Packit Service |
751c4a |
UDEVADM_CMD = subp.which('udevadm')
|
|
Packit Service |
751c4a |
SFDISK_CMD = subp.which("sfdisk")
|
|
Packit Service |
751c4a |
SGDISK_CMD = subp.which("sgdisk")
|
|
Packit Service |
751c4a |
LSBLK_CMD = subp.which("lsblk")
|
|
Packit Service |
751c4a |
BLKID_CMD = subp.which("blkid")
|
|
Packit Service |
751c4a |
BLKDEV_CMD = subp.which("blockdev")
|
|
Packit Service |
751c4a |
WIPEFS_CMD = subp.which("wipefs")
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LANG_C_ENV = {'LANG': 'C'}
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG = logging.getLogger(__name__)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def handle(_name, cfg, cloud, log, _args):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
See doc/examples/cloud-config-disk-setup.txt for documentation on the
|
|
Packit Service |
a04d08 |
format.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
disk_setup = cfg.get("disk_setup")
|
|
Packit Service |
a04d08 |
if isinstance(disk_setup, dict):
|
|
Packit Service |
a04d08 |
update_disk_setup_devices(disk_setup, cloud.device_name_to_device)
|
|
Packit Service |
a04d08 |
log.debug("Partitioning disks: %s", str(disk_setup))
|
|
Packit Service |
a04d08 |
for disk, definition in disk_setup.items():
|
|
Packit Service |
a04d08 |
if not isinstance(definition, dict):
|
|
Packit Service |
a04d08 |
log.warning("Invalid disk definition for %s" % disk)
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
log.debug("Creating new partition table/disk")
|
|
Packit Service |
a04d08 |
util.log_time(logfunc=LOG.debug,
|
|
Packit Service |
a04d08 |
msg="Creating partition on %s" % disk,
|
|
Packit Service |
a04d08 |
func=mkpart, args=(disk, definition))
|
|
Packit Service |
a04d08 |
except Exception as e:
|
|
Packit Service |
a04d08 |
util.logexc(LOG, "Failed partitioning operation\n%s" % e)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
fs_setup = cfg.get("fs_setup")
|
|
Packit Service |
a04d08 |
if isinstance(fs_setup, list):
|
|
Packit Service |
a04d08 |
log.debug("setting up filesystems: %s", str(fs_setup))
|
|
Packit Service |
a04d08 |
update_fs_setup_devices(fs_setup, cloud.device_name_to_device)
|
|
Packit Service |
a04d08 |
for definition in fs_setup:
|
|
Packit Service |
a04d08 |
if not isinstance(definition, dict):
|
|
Packit Service |
a04d08 |
log.warning("Invalid file system definition: %s" % definition)
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
log.debug("Creating new filesystem.")
|
|
Packit Service |
a04d08 |
device = definition.get('device')
|
|
Packit Service |
a04d08 |
util.log_time(logfunc=LOG.debug,
|
|
Packit Service |
a04d08 |
msg="Creating fs for %s" % device,
|
|
Packit Service |
a04d08 |
func=mkfs, args=(definition,))
|
|
Packit Service |
a04d08 |
except Exception as e:
|
|
Packit Service |
a04d08 |
util.logexc(LOG, "Failed during filesystem operation\n%s" % e)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def update_disk_setup_devices(disk_setup, tformer):
|
|
Packit Service |
a04d08 |
# update 'disk_setup' dictionary anywhere were a device may occur
|
|
Packit Service |
a04d08 |
# update it with the response from 'tformer'
|
|
Packit Service |
751c4a |
for origname in list(disk_setup):
|
|
Packit Service |
a04d08 |
transformed = tformer(origname)
|
|
Packit Service |
a04d08 |
if transformed is None or transformed == origname:
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
if transformed in disk_setup:
|
|
Packit Service |
a04d08 |
LOG.info("Replacing %s in disk_setup for translation of %s",
|
|
Packit Service |
a04d08 |
origname, transformed)
|
|
Packit Service |
a04d08 |
del disk_setup[transformed]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
disk_setup[transformed] = disk_setup[origname]
|
|
Packit Service |
a04d08 |
disk_setup[transformed]['_origname'] = origname
|
|
Packit Service |
a04d08 |
del disk_setup[origname]
|
|
Packit Service |
a04d08 |
LOG.debug("updated disk_setup device entry '%s' to '%s'",
|
|
Packit Service |
a04d08 |
origname, transformed)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def update_fs_setup_devices(disk_setup, tformer):
|
|
Packit Service |
a04d08 |
# update 'fs_setup' dictionary anywhere were a device may occur
|
|
Packit Service |
a04d08 |
# update it with the response from 'tformer'
|
|
Packit Service |
a04d08 |
for definition in disk_setup:
|
|
Packit Service |
a04d08 |
if not isinstance(definition, dict):
|
|
Packit Service |
a04d08 |
LOG.warning("entry in disk_setup not a dict: %s", definition)
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
origname = definition.get('device')
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if origname is None:
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
(dev, part) = util.expand_dotted_devname(origname)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
tformed = tformer(dev)
|
|
Packit Service |
a04d08 |
if tformed is not None:
|
|
Packit Service |
a04d08 |
dev = tformed
|
|
Packit Service |
a04d08 |
LOG.debug("%s is mapped to disk=%s part=%s",
|
|
Packit Service |
a04d08 |
origname, tformed, part)
|
|
Packit Service |
a04d08 |
definition['_origname'] = origname
|
|
Packit Service |
a04d08 |
definition['device'] = tformed
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if part:
|
|
Packit Service |
a04d08 |
# In origname with <dev>.N, N overrides 'partition' key.
|
|
Packit Service |
a04d08 |
if 'partition' in definition:
|
|
Packit Service |
a04d08 |
LOG.warning("Partition '%s' from dotted device name '%s' "
|
|
Packit Service |
a04d08 |
"overrides 'partition' key in %s", part, origname,
|
|
Packit Service |
a04d08 |
definition)
|
|
Packit Service |
a04d08 |
definition['_partition'] = definition['partition']
|
|
Packit Service |
a04d08 |
definition['partition'] = part
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def value_splitter(values, start=None):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Returns the key/value pairs of output sent as string
|
|
Packit Service |
a04d08 |
like: FOO='BAR' HOME='127.0.0.1'
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
_values = shlex.split(values)
|
|
Packit Service |
a04d08 |
if start:
|
|
Packit Service |
a04d08 |
_values = _values[start:]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
for key, value in [x.split('=') for x in _values]:
|
|
Packit Service |
a04d08 |
yield key, value
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def enumerate_disk(device, nodeps=False):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Enumerate the elements of a child device.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
Parameters:
|
|
Packit Service |
a04d08 |
device: the kernel device name
|
|
Packit Service |
a04d08 |
nodeps <BOOL>: don't enumerate children devices
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
Return a dict describing the disk:
|
|
Packit Service |
a04d08 |
type: the entry type, i.e disk or part
|
|
Packit Service |
a04d08 |
fstype: the filesystem type, if it exists
|
|
Packit Service |
a04d08 |
label: file system label, if it exists
|
|
Packit Service |
a04d08 |
name: the device name, i.e. sda
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
lsblk_cmd = [LSBLK_CMD, '--pairs', '--output', 'NAME,TYPE,FSTYPE,LABEL',
|
|
Packit Service |
a04d08 |
device]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if nodeps:
|
|
Packit Service |
a04d08 |
lsblk_cmd.append('--nodeps')
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
info = None
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
751c4a |
info, _err = subp.subp(lsblk_cmd)
|
|
Packit Service |
a04d08 |
except Exception as e:
|
|
Packit Service |
751c4a |
raise Exception(
|
|
Packit Service |
751c4a |
"Failed during disk check for %s\n%s" % (device, e)
|
|
Packit Service |
751c4a |
) from e
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
parts = [x for x in (info.strip()).splitlines() if len(x.split()) > 0]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
for part in parts:
|
|
Packit Service |
a04d08 |
d = {
|
|
Packit Service |
a04d08 |
'name': None,
|
|
Packit Service |
a04d08 |
'type': None,
|
|
Packit Service |
a04d08 |
'fstype': None,
|
|
Packit Service |
a04d08 |
'label': None,
|
|
Packit Service |
a04d08 |
}
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
for key, value in value_splitter(part):
|
|
Packit Service |
a04d08 |
d[key.lower()] = value
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
yield d
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def device_type(device):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Return the device type of the device by calling lsblk.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
for d in enumerate_disk(device, nodeps=True):
|
|
Packit Service |
a04d08 |
if "type" in d:
|
|
Packit Service |
a04d08 |
return d["type"].lower()
|
|
Packit Service |
a04d08 |
return None
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def is_device_valid(name, partition=False):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Check if the device is a valid device.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
d_type = ""
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
d_type = device_type(name)
|
|
Packit Service |
a04d08 |
except Exception:
|
|
Packit Service |
a04d08 |
LOG.warning("Query against device %s failed", name)
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if partition and d_type == 'part':
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
elif not partition and d_type == 'disk':
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def check_fs(device):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Check if the device has a filesystem on it
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
Output of blkid is generally something like:
|
|
Packit Service |
a04d08 |
/dev/sda: LABEL="Backup500G" UUID="..." TYPE="ext4"
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
Return values are device, label, type, uuid
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
out, label, fs_type, uuid = None, None, None, None
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
blkid_cmd = [BLKID_CMD, '-c', '/dev/null', device]
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
751c4a |
out, _err = subp.subp(blkid_cmd, rcs=[0, 2])
|
|
Packit Service |
a04d08 |
except Exception as e:
|
|
Packit Service |
751c4a |
raise Exception(
|
|
Packit Service |
751c4a |
"Failed during disk check for %s\n%s" % (device, e)
|
|
Packit Service |
751c4a |
) from e
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if out:
|
|
Packit Service |
a04d08 |
if len(out.splitlines()) == 1:
|
|
Packit Service |
a04d08 |
for key, value in value_splitter(out, start=1):
|
|
Packit Service |
a04d08 |
if key.lower() == 'label':
|
|
Packit Service |
a04d08 |
label = value
|
|
Packit Service |
a04d08 |
elif key.lower() == 'type':
|
|
Packit Service |
a04d08 |
fs_type = value
|
|
Packit Service |
a04d08 |
elif key.lower() == 'uuid':
|
|
Packit Service |
a04d08 |
uuid = value
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
return label, fs_type, uuid
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def is_filesystem(device):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Returns true if the device has a file system.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
_, fs_type, _ = check_fs(device)
|
|
Packit Service |
a04d08 |
return fs_type
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def find_device_node(device, fs_type=None, label=None, valid_targets=None,
|
|
Packit Service |
a04d08 |
label_match=True, replace_fs=None):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Find a device that is either matches the spec, or the first
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
The return is value is (<device>, <bool>) where the device is the
|
|
Packit Service |
a04d08 |
device to use and the bool is whether the device matches the
|
|
Packit Service |
a04d08 |
fs_type and label.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
Note: This works with GPT partition tables!
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
# label of None is same as no label
|
|
Packit Service |
a04d08 |
if label is None:
|
|
Packit Service |
a04d08 |
label = ""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if not valid_targets:
|
|
Packit Service |
a04d08 |
valid_targets = ['disk', 'part']
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
raw_device_used = False
|
|
Packit Service |
a04d08 |
for d in enumerate_disk(device):
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if d['fstype'] == replace_fs and label_match is False:
|
|
Packit Service |
a04d08 |
# We found a device where we want to replace the FS
|
|
Packit Service |
a04d08 |
return ('/dev/%s' % d['name'], False)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if (d['fstype'] == fs_type and
|
|
Packit Service |
a04d08 |
((label_match and d['label'] == label) or not label_match)):
|
|
Packit Service |
a04d08 |
# If we find a matching device, we return that
|
|
Packit Service |
a04d08 |
return ('/dev/%s' % d['name'], True)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if d['type'] in valid_targets:
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if d['type'] != 'disk' or d['fstype']:
|
|
Packit Service |
a04d08 |
raw_device_used = True
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if d['type'] == 'disk':
|
|
Packit Service |
a04d08 |
# Skip the raw disk, its the default
|
|
Packit Service |
a04d08 |
pass
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
elif not d['fstype']:
|
|
Packit Service |
a04d08 |
return ('/dev/%s' % d['name'], False)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if not raw_device_used:
|
|
Packit Service |
a04d08 |
return (device, False)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG.warning("Failed to find device during available device search.")
|
|
Packit Service |
a04d08 |
return (None, False)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def is_disk_used(device):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Check if the device is currently used. Returns true if the device
|
|
Packit Service |
a04d08 |
has either a file system or a partition entry
|
|
Packit Service |
a04d08 |
is no filesystem found on the disk.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# If the child count is higher 1, then there are child nodes
|
|
Packit Service |
a04d08 |
# such as partition or device mapper nodes
|
|
Packit Service |
a04d08 |
if len(list(enumerate_disk(device))) > 1:
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# If we see a file system, then its used
|
|
Packit Service |
a04d08 |
_, check_fstype, _ = check_fs(device)
|
|
Packit Service |
a04d08 |
if check_fstype:
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def get_dyn_func(*args):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Call the appropriate function.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
The first value is the template for function name
|
|
Packit Service |
a04d08 |
The second value is the template replacement
|
|
Packit Service |
a04d08 |
The remain values are passed to the function
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
For example: get_dyn_func("foo_%s", 'bar', 1, 2, 3,)
|
|
Packit Service |
a04d08 |
would call "foo_bar" with args of 1, 2, 3
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
if len(args) < 2:
|
|
Packit Service |
a04d08 |
raise Exception("Unable to determine dynamic funcation name")
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
func_name = (args[0] % args[1])
|
|
Packit Service |
a04d08 |
func_args = args[2:]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
if func_args:
|
|
Packit Service |
a04d08 |
return globals()[func_name](*func_args)
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
return globals()[func_name]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
except KeyError as e:
|
|
Packit Service |
751c4a |
raise Exception("No such function %s to call!" % func_name) from e
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def get_hdd_size(device):
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
751c4a |
size_in_bytes, _ = subp.subp([BLKDEV_CMD, '--getsize64', device])
|
|
Packit Service |
751c4a |
sector_size, _ = subp.subp([BLKDEV_CMD, '--getss', device])
|
|
Packit Service |
a04d08 |
except Exception as e:
|
|
Packit Service |
751c4a |
raise Exception("Failed to get %s size\n%s" % (device, e)) from e
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
return int(size_in_bytes) / int(sector_size)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def check_partition_mbr_layout(device, layout):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Returns true if the partition layout matches the one on the disk
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
Layout should be a list of values. At this time, this only
|
|
Packit Service |
a04d08 |
verifies that the number of partitions and their labels is correct.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
read_parttbl(device)
|
|
Packit Service |
a04d08 |
prt_cmd = [SFDISK_CMD, "-l", device]
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
751c4a |
out, _err = subp.subp(prt_cmd, data="%s\n" % layout)
|
|
Packit Service |
a04d08 |
except Exception as e:
|
|
Packit Service |
751c4a |
raise Exception(
|
|
Packit Service |
751c4a |
"Error running partition command on %s\n%s" % (device, e)
|
|
Packit Service |
751c4a |
) from e
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
found_layout = []
|
|
Packit Service |
a04d08 |
for line in out.splitlines():
|
|
Packit Service |
a04d08 |
_line = line.split()
|
|
Packit Service |
a04d08 |
if len(_line) == 0:
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if device in _line[0]:
|
|
Packit Service |
a04d08 |
# We don't understand extended partitions yet
|
|
Packit Service |
a04d08 |
if _line[-1].lower() in ['extended', 'empty']:
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Find the partition types
|
|
Packit Service |
a04d08 |
type_label = None
|
|
Packit Service |
a04d08 |
for x in sorted(range(1, len(_line)), reverse=True):
|
|
Packit Service |
a04d08 |
if _line[x].isdigit() and _line[x] != '/':
|
|
Packit Service |
a04d08 |
type_label = _line[x]
|
|
Packit Service |
a04d08 |
break
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
found_layout.append(type_label)
|
|
Packit Service |
a04d08 |
return found_layout
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def check_partition_gpt_layout(device, layout):
|
|
Packit Service |
a04d08 |
prt_cmd = [SGDISK_CMD, '-p', device]
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
751c4a |
out, _err = subp.subp(prt_cmd, update_env=LANG_C_ENV)
|
|
Packit Service |
a04d08 |
except Exception as e:
|
|
Packit Service |
751c4a |
raise Exception(
|
|
Packit Service |
751c4a |
"Error running partition command on %s\n%s" % (device, e)
|
|
Packit Service |
751c4a |
) from e
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
out_lines = iter(out.splitlines())
|
|
Packit Service |
a04d08 |
# Skip header. Output looks like:
|
|
Packit Service |
a04d08 |
# ***************************************************************
|
|
Packit Service |
a04d08 |
# Found invalid GPT and valid MBR; converting MBR to GPT format
|
|
Packit Service |
a04d08 |
# in memory.
|
|
Packit Service |
a04d08 |
# ***************************************************************
|
|
Packit Service |
a04d08 |
#
|
|
Packit Service |
a04d08 |
# Disk /dev/vdb: 83886080 sectors, 40.0 GiB
|
|
Packit Service |
a04d08 |
# Logical sector size: 512 bytes
|
|
Packit Service |
a04d08 |
# Disk identifier (GUID): 8A7F11AD-3953-491B-8051-077E01C8E9A7
|
|
Packit Service |
a04d08 |
# Partition table holds up to 128 entries
|
|
Packit Service |
a04d08 |
# First usable sector is 34, last usable sector is 83886046
|
|
Packit Service |
a04d08 |
# Partitions will be aligned on 2048-sector boundaries
|
|
Packit Service |
a04d08 |
# Total free space is 83476413 sectors (39.8 GiB)
|
|
Packit Service |
a04d08 |
#
|
|
Packit Service |
a04d08 |
# Number Start (sector) End (sector) Size Code Name
|
|
Packit Service |
a04d08 |
# 1 2048 206847 100.0 MiB 0700 Microsoft basic data
|
|
Packit Service |
a04d08 |
for line in out_lines:
|
|
Packit Service |
a04d08 |
if line.strip().startswith('Number'):
|
|
Packit Service |
a04d08 |
break
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
codes = [line.strip().split()[5] for line in out_lines]
|
|
Packit Service |
a04d08 |
cleaned = []
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# user would expect a code '83' to be Linux, but sgdisk outputs 8300.
|
|
Packit Service |
a04d08 |
for code in codes:
|
|
Packit Service |
a04d08 |
if len(code) == 4 and code.endswith("00"):
|
|
Packit Service |
a04d08 |
code = code[0:2]
|
|
Packit Service |
a04d08 |
cleaned.append(code)
|
|
Packit Service |
a04d08 |
return cleaned
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def check_partition_layout(table_type, device, layout):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
See if the partition lay out matches.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
This is future a future proofing function. In order
|
|
Packit Service |
a04d08 |
to add support for other disk layout schemes, add a
|
|
Packit Service |
a04d08 |
function called check_partition_%s_layout
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
found_layout = get_dyn_func(
|
|
Packit Service |
a04d08 |
"check_partition_%s_layout", table_type, device, layout)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG.debug("called check_partition_%s_layout(%s, %s), returned: %s",
|
|
Packit Service |
a04d08 |
table_type, device, layout, found_layout)
|
|
Packit Service |
a04d08 |
if isinstance(layout, bool):
|
|
Packit Service |
a04d08 |
# if we are using auto partitioning, or "True" be happy
|
|
Packit Service |
a04d08 |
# if a single partition exists.
|
|
Packit Service |
a04d08 |
if layout and len(found_layout) >= 1:
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
elif len(found_layout) == len(layout):
|
|
Packit Service |
a04d08 |
# This just makes sure that the number of requested
|
|
Packit Service |
a04d08 |
# partitions and the type labels are right
|
|
Packit Service |
a04d08 |
layout_types = [str(x[1]) if isinstance(x, (tuple, list)) else None
|
|
Packit Service |
a04d08 |
for x in layout]
|
|
Packit Service |
a04d08 |
LOG.debug("Layout types=%s. Found types=%s",
|
|
Packit Service |
a04d08 |
layout_types, found_layout)
|
|
Packit Service |
a04d08 |
for itype, ftype in zip(layout_types, found_layout):
|
|
Packit Service |
a04d08 |
if itype is not None and str(ftype) != str(itype):
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def get_partition_mbr_layout(size, layout):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Calculate the layout of the partition table. Partition sizes
|
|
Packit Service |
a04d08 |
are defined as percentage values or a tuple of percentage and
|
|
Packit Service |
a04d08 |
partition type.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
For example:
|
|
Packit Service |
a04d08 |
[ 33, [66: 82] ]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
Defines the first partition to be a size of 1/3 the disk,
|
|
Packit Service |
a04d08 |
while the remaining 2/3's will be of type Linux Swap.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if not isinstance(layout, list) and isinstance(layout, bool):
|
|
Packit Service |
a04d08 |
# Create a single partition
|
|
Packit Service |
a04d08 |
return "0,"
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if ((len(layout) == 0 and isinstance(layout, list)) or
|
|
Packit Service |
a04d08 |
not isinstance(layout, list)):
|
|
Packit Service |
a04d08 |
raise Exception("Partition layout is invalid")
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
last_part_num = len(layout)
|
|
Packit Service |
a04d08 |
if last_part_num > 4:
|
|
Packit Service |
a04d08 |
raise Exception("Only simply partitioning is allowed.")
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
part_definition = []
|
|
Packit Service |
a04d08 |
part_num = 0
|
|
Packit Service |
a04d08 |
for part in layout:
|
|
Packit Service |
a04d08 |
part_type = 83 # Default to Linux
|
|
Packit Service |
a04d08 |
percent = part
|
|
Packit Service |
a04d08 |
part_num += 1
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if isinstance(part, list):
|
|
Packit Service |
a04d08 |
if len(part) != 2:
|
|
Packit Service |
a04d08 |
raise Exception("Partition was incorrectly defined: %s" % part)
|
|
Packit Service |
a04d08 |
percent, part_type = part
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
part_size = int(float(size) * (float(percent) / 100))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if part_num == last_part_num:
|
|
Packit Service |
a04d08 |
part_definition.append(",,%s" % part_type)
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
part_definition.append(",%s,%s" % (part_size, part_type))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
sfdisk_definition = "\n".join(part_definition)
|
|
Packit Service |
a04d08 |
if len(part_definition) > 4:
|
|
Packit Service |
a04d08 |
raise Exception("Calculated partition definition is too big\n%s" %
|
|
Packit Service |
a04d08 |
sfdisk_definition)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
return sfdisk_definition
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def get_partition_gpt_layout(size, layout):
|
|
Packit Service |
a04d08 |
if isinstance(layout, bool):
|
|
Packit Service |
a04d08 |
return [(None, [0, 0])]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
partition_specs = []
|
|
Packit Service |
a04d08 |
for partition in layout:
|
|
Packit Service |
a04d08 |
if isinstance(partition, list):
|
|
Packit Service |
a04d08 |
if len(partition) != 2:
|
|
Packit Service |
a04d08 |
raise Exception(
|
|
Packit Service |
a04d08 |
"Partition was incorrectly defined: %s" % partition)
|
|
Packit Service |
a04d08 |
percent, partition_type = partition
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
percent = partition
|
|
Packit Service |
a04d08 |
partition_type = None
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
part_size = int(float(size) * (float(percent) / 100))
|
|
Packit Service |
a04d08 |
partition_specs.append((partition_type, [0, '+{}'.format(part_size)]))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# The last partition should use up all remaining space
|
|
Packit Service |
a04d08 |
partition_specs[-1][-1][-1] = 0
|
|
Packit Service |
a04d08 |
return partition_specs
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def purge_disk_ptable(device):
|
|
Packit Service |
a04d08 |
# wipe the first and last megabyte of a disk (or file)
|
|
Packit Service |
a04d08 |
# gpt stores partition table both at front and at end.
|
|
Packit Service |
a04d08 |
null = '\0'
|
|
Packit Service |
a04d08 |
start_len = 1024 * 1024
|
|
Packit Service |
a04d08 |
end_len = 1024 * 1024
|
|
Packit Service |
a04d08 |
with open(device, "rb+") as fp:
|
|
Packit Service |
a04d08 |
fp.write(null * (start_len))
|
|
Packit Service |
a04d08 |
fp.seek(-end_len, os.SEEK_END)
|
|
Packit Service |
a04d08 |
fp.write(null * end_len)
|
|
Packit Service |
a04d08 |
fp.flush()
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
read_parttbl(device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def purge_disk(device):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Remove parition table entries
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# wipe any file systems first
|
|
Packit Service |
a04d08 |
for d in enumerate_disk(device):
|
|
Packit Service |
a04d08 |
if d['type'] not in ["disk", "crypt"]:
|
|
Packit Service |
a04d08 |
wipefs_cmd = [WIPEFS_CMD, "--all", "/dev/%s" % d['name']]
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
LOG.info("Purging filesystem on /dev/%s", d['name'])
|
|
Packit Service |
751c4a |
subp.subp(wipefs_cmd)
|
|
Packit Service |
751c4a |
except Exception as e:
|
|
Packit Service |
751c4a |
raise Exception(
|
|
Packit Service |
751c4a |
"Failed FS purge of /dev/%s" % d['name']
|
|
Packit Service |
751c4a |
) from e
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
purge_disk_ptable(device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def get_partition_layout(table_type, size, layout):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Call the appropriate function for creating the table
|
|
Packit Service |
a04d08 |
definition. Returns the table definition
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
This is a future proofing function. To add support for
|
|
Packit Service |
a04d08 |
other layouts, simply add a "get_partition_%s_layout"
|
|
Packit Service |
a04d08 |
function.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
return get_dyn_func("get_partition_%s_layout", table_type, size, layout)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def read_parttbl(device):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Use partprobe instead of 'udevadm'. Partprobe is the only
|
|
Packit Service |
a04d08 |
reliable way to probe the partition table.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
blkdev_cmd = [BLKDEV_CMD, '--rereadpt', device]
|
|
Packit Service |
a04d08 |
util.udevadm_settle()
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
751c4a |
subp.subp(blkdev_cmd)
|
|
Packit Service |
a04d08 |
except Exception as e:
|
|
Packit Service |
a04d08 |
util.logexc(LOG, "Failed reading the partition table %s" % e)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
util.udevadm_settle()
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def exec_mkpart_mbr(device, layout):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Break out of mbr partition to allow for future partition
|
|
Packit Service |
a04d08 |
types, i.e. gpt
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
# Create the partitions
|
|
Packit Service |
a04d08 |
prt_cmd = [SFDISK_CMD, "--Linux", "--unit=S", "--force", device]
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
751c4a |
subp.subp(prt_cmd, data="%s\n" % layout)
|
|
Packit Service |
a04d08 |
except Exception as e:
|
|
Packit Service |
751c4a |
raise Exception(
|
|
Packit Service |
751c4a |
"Failed to partition device %s\n%s" % (device, e)
|
|
Packit Service |
751c4a |
) from e
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
read_parttbl(device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def exec_mkpart_gpt(device, layout):
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
751c4a |
subp.subp([SGDISK_CMD, '-Z', device])
|
|
Packit Service |
a04d08 |
for index, (partition_type, (start, end)) in enumerate(layout):
|
|
Packit Service |
a04d08 |
index += 1
|
|
Packit Service |
751c4a |
subp.subp([SGDISK_CMD,
|
|
Packit Service |
a04d08 |
'-n', '{}:{}:{}'.format(index, start, end), device])
|
|
Packit Service |
a04d08 |
if partition_type is not None:
|
|
Packit Service |
a04d08 |
# convert to a 4 char (or more) string right padded with 0
|
|
Packit Service |
a04d08 |
# 82 -> 8200. 'Linux' -> 'Linux'
|
|
Packit Service |
a04d08 |
pinput = str(partition_type).ljust(4, "0")
|
|
Packit Service |
751c4a |
subp.subp(
|
|
Packit Service |
a04d08 |
[SGDISK_CMD, '-t', '{}:{}'.format(index, pinput), device])
|
|
Packit Service |
a04d08 |
except Exception:
|
|
Packit Service |
a04d08 |
LOG.warning("Failed to partition device %s", device)
|
|
Packit Service |
a04d08 |
raise
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
read_parttbl(device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def exec_mkpart(table_type, device, layout):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Fetches the function for creating the table type.
|
|
Packit Service |
a04d08 |
This allows to dynamically find which function to call.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
Paramaters:
|
|
Packit Service |
a04d08 |
table_type: type of partition table to use
|
|
Packit Service |
a04d08 |
device: the device to work on
|
|
Packit Service |
a04d08 |
layout: layout definition specific to partition table
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
return get_dyn_func("exec_mkpart_%s", table_type, device, layout)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def assert_and_settle_device(device):
|
|
Packit Service |
a04d08 |
"""Assert that device exists and settle so it is fully recognized."""
|
|
Packit Service |
a04d08 |
if not os.path.exists(device):
|
|
Packit Service |
a04d08 |
util.udevadm_settle()
|
|
Packit Service |
a04d08 |
if not os.path.exists(device):
|
|
Packit Service |
a04d08 |
raise RuntimeError("Device %s did not exist and was not created "
|
|
Packit Service |
a04d08 |
"with a udevadm settle." % device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Whether or not the device existed above, it is possible that udev
|
|
Packit Service |
a04d08 |
# events that would populate udev database (for reading by lsdname) have
|
|
Packit Service |
a04d08 |
# not yet finished. So settle again.
|
|
Packit Service |
a04d08 |
util.udevadm_settle()
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def mkpart(device, definition):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Creates the partition table.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
Parameters:
|
|
Packit Service |
a04d08 |
definition: dictionary describing how to create the partition.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
The following are supported values in the dict:
|
|
Packit Service |
a04d08 |
overwrite: Should the partition table be created regardless
|
|
Packit Service |
a04d08 |
of any pre-exisiting data?
|
|
Packit Service |
a04d08 |
layout: the layout of the partition table
|
|
Packit Service |
a04d08 |
table_type: Which partition table to use, defaults to MBR
|
|
Packit Service |
a04d08 |
device: the device to work on.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
# ensure that we get a real device rather than a symbolic link
|
|
Packit Service |
a04d08 |
assert_and_settle_device(device)
|
|
Packit Service |
a04d08 |
device = os.path.realpath(device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG.debug("Checking values for %s definition", device)
|
|
Packit Service |
a04d08 |
overwrite = definition.get('overwrite', False)
|
|
Packit Service |
a04d08 |
layout = definition.get('layout', False)
|
|
Packit Service |
a04d08 |
table_type = definition.get('table_type', 'mbr')
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Check if the default device is a partition or not
|
|
Packit Service |
a04d08 |
LOG.debug("Checking against default devices")
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if (isinstance(layout, bool) and not layout) or not layout:
|
|
Packit Service |
a04d08 |
LOG.debug("Device is not to be partitioned, skipping")
|
|
Packit Service |
a04d08 |
return # Device is not to be partitioned
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# This prevents you from overwriting the device
|
|
Packit Service |
a04d08 |
LOG.debug("Checking if device %s is a valid device", device)
|
|
Packit Service |
a04d08 |
if not is_device_valid(device):
|
|
Packit Service |
a04d08 |
raise Exception(
|
|
Packit Service |
a04d08 |
'Device {device} is not a disk device!'.format(device=device))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Remove the partition table entries
|
|
Packit Service |
a04d08 |
if isinstance(layout, str) and layout.lower() == "remove":
|
|
Packit Service |
a04d08 |
LOG.debug("Instructed to remove partition table entries")
|
|
Packit Service |
a04d08 |
purge_disk(device)
|
|
Packit Service |
a04d08 |
return
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG.debug("Checking if device layout matches")
|
|
Packit Service |
a04d08 |
if check_partition_layout(table_type, device, layout):
|
|
Packit Service |
a04d08 |
LOG.debug("Device partitioning layout matches")
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG.debug("Checking if device is safe to partition")
|
|
Packit Service |
a04d08 |
if not overwrite and (is_disk_used(device) or is_filesystem(device)):
|
|
Packit Service |
a04d08 |
LOG.debug("Skipping partitioning on configured device %s", device)
|
|
Packit Service |
a04d08 |
return
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG.debug("Checking for device size of %s", device)
|
|
Packit Service |
a04d08 |
device_size = get_hdd_size(device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG.debug("Calculating partition layout")
|
|
Packit Service |
a04d08 |
part_definition = get_partition_layout(table_type, device_size, layout)
|
|
Packit Service |
a04d08 |
LOG.debug(" Layout is: %s", part_definition)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG.debug("Creating partition table on %s", device)
|
|
Packit Service |
a04d08 |
exec_mkpart(table_type, device, part_definition)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG.debug("Partition table created for %s", device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def lookup_force_flag(fs):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
A force flag might be -F or -F, this look it up
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
flags = {
|
|
Packit Service |
a04d08 |
'ext': '-F',
|
|
Packit Service |
a04d08 |
'btrfs': '-f',
|
|
Packit Service |
a04d08 |
'xfs': '-f',
|
|
Packit Service |
a04d08 |
'reiserfs': '-f',
|
|
Packit Service |
751c4a |
'swap': '-f',
|
|
Packit Service |
a04d08 |
}
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if 'ext' in fs.lower():
|
|
Packit Service |
a04d08 |
fs = 'ext'
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if fs.lower() in flags:
|
|
Packit Service |
a04d08 |
return flags[fs]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG.warning("Force flag for %s is unknown.", fs)
|
|
Packit Service |
a04d08 |
return ''
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def mkfs(fs_cfg):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Create a file system on the device.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
label: defines the label to use on the device
|
|
Packit Service |
a04d08 |
fs_cfg: defines how the filesystem is to look
|
|
Packit Service |
a04d08 |
The following values are required generally:
|
|
Packit Service |
a04d08 |
device: which device or cloud defined default_device
|
|
Packit Service |
a04d08 |
filesystem: which file system type
|
|
Packit Service |
a04d08 |
overwrite: indiscriminately create the file system
|
|
Packit Service |
a04d08 |
partition: when device does not define a partition,
|
|
Packit Service |
a04d08 |
setting this to a number will mean
|
|
Packit Service |
a04d08 |
device + partition. When set to 'auto', the
|
|
Packit Service |
a04d08 |
first free device or the first device which
|
|
Packit Service |
a04d08 |
matches both label and type will be used.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
'any' means the first filesystem that matches
|
|
Packit Service |
a04d08 |
on the device.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
When 'cmd' is provided then no other parameter is required.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
label = fs_cfg.get('label')
|
|
Packit Service |
a04d08 |
device = fs_cfg.get('device')
|
|
Packit Service |
a04d08 |
partition = str(fs_cfg.get('partition', 'any'))
|
|
Packit Service |
a04d08 |
fs_type = fs_cfg.get('filesystem')
|
|
Packit Service |
a04d08 |
fs_cmd = fs_cfg.get('cmd', [])
|
|
Packit Service |
a04d08 |
fs_opts = fs_cfg.get('extra_opts', [])
|
|
Packit Service |
a04d08 |
fs_replace = fs_cfg.get('replace_fs', False)
|
|
Packit Service |
a04d08 |
overwrite = fs_cfg.get('overwrite', False)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# ensure that we get a real device rather than a symbolic link
|
|
Packit Service |
a04d08 |
assert_and_settle_device(device)
|
|
Packit Service |
a04d08 |
device = os.path.realpath(device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# This allows you to define the default ephemeral or swap
|
|
Packit Service |
a04d08 |
LOG.debug("Checking %s against default devices", device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if not partition or partition.isdigit():
|
|
Packit Service |
a04d08 |
# Handle manual definition of partition
|
|
Packit Service |
a04d08 |
if partition.isdigit():
|
|
Packit Service |
a04d08 |
device = "%s%s" % (device, partition)
|
|
Packit Service |
a04d08 |
LOG.debug("Manual request of partition %s for %s",
|
|
Packit Service |
a04d08 |
partition, device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Check to see if the fs already exists
|
|
Packit Service |
a04d08 |
LOG.debug("Checking device %s", device)
|
|
Packit Service |
a04d08 |
check_label, check_fstype, _ = check_fs(device)
|
|
Packit Service |
a04d08 |
LOG.debug("Device '%s' has check_label='%s' check_fstype=%s",
|
|
Packit Service |
a04d08 |
device, check_label, check_fstype)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if check_label == label and check_fstype == fs_type:
|
|
Packit Service |
a04d08 |
LOG.debug("Existing file system found at %s", device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if not overwrite:
|
|
Packit Service |
a04d08 |
LOG.debug("Device %s has required file system", device)
|
|
Packit Service |
a04d08 |
return
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
LOG.warning("Destroying filesystem on %s", device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
LOG.debug("Device %s is cleared for formating", device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
elif partition and str(partition).lower() in ('auto', 'any'):
|
|
Packit Service |
a04d08 |
# For auto devices, we match if the filesystem does exist
|
|
Packit Service |
a04d08 |
odevice = device
|
|
Packit Service |
a04d08 |
LOG.debug("Identifying device to create %s filesytem on", label)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# any mean pick the first match on the device with matching fs_type
|
|
Packit Service |
a04d08 |
label_match = True
|
|
Packit Service |
a04d08 |
if partition.lower() == 'any':
|
|
Packit Service |
a04d08 |
label_match = False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
device, reuse = find_device_node(device, fs_type=fs_type, label=label,
|
|
Packit Service |
a04d08 |
label_match=label_match,
|
|
Packit Service |
a04d08 |
replace_fs=fs_replace)
|
|
Packit Service |
a04d08 |
LOG.debug("Automatic device for %s identified as %s", odevice, device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if reuse:
|
|
Packit Service |
a04d08 |
LOG.debug("Found filesystem match, skipping formating.")
|
|
Packit Service |
a04d08 |
return
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if not reuse and fs_replace and device:
|
|
Packit Service |
a04d08 |
LOG.debug("Replacing file system on %s as instructed.", device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if not device:
|
|
Packit Service |
a04d08 |
LOG.debug("No device aviable that matches request. "
|
|
Packit Service |
a04d08 |
"Skipping fs creation for %s", fs_cfg)
|
|
Packit Service |
a04d08 |
return
|
|
Packit Service |
a04d08 |
elif not partition or str(partition).lower() == 'none':
|
|
Packit Service |
a04d08 |
LOG.debug("Using the raw device to place filesystem %s on", label)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
LOG.debug("Error in device identification handling.")
|
|
Packit Service |
a04d08 |
return
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG.debug("File system type '%s' with label '%s' will be created on %s",
|
|
Packit Service |
a04d08 |
fs_type, label, device)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Make sure the device is defined
|
|
Packit Service |
a04d08 |
if not device:
|
|
Packit Service |
a04d08 |
LOG.warning("Device is not known: %s", device)
|
|
Packit Service |
a04d08 |
return
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Check that we can create the FS
|
|
Packit Service |
a04d08 |
if not (fs_type or fs_cmd):
|
|
Packit Service |
a04d08 |
raise Exception(
|
|
Packit Service |
a04d08 |
"No way to create filesystem '{label}'. fs_type or fs_cmd "
|
|
Packit Service |
a04d08 |
"must be set.".format(label=label))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Create the commands
|
|
Packit Service |
a04d08 |
shell = False
|
|
Packit Service |
a04d08 |
if fs_cmd:
|
|
Packit Service |
a04d08 |
fs_cmd = fs_cfg['cmd'] % {
|
|
Packit Service |
a04d08 |
'label': label,
|
|
Packit Service |
a04d08 |
'filesystem': fs_type,
|
|
Packit Service |
a04d08 |
'device': device,
|
|
Packit Service |
a04d08 |
}
|
|
Packit Service |
a04d08 |
shell = True
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if overwrite:
|
|
Packit Service |
a04d08 |
LOG.warning(
|
|
Packit Service |
a04d08 |
"fs_setup:overwrite ignored because cmd was specified: %s",
|
|
Packit Service |
a04d08 |
fs_cmd)
|
|
Packit Service |
a04d08 |
if fs_opts:
|
|
Packit Service |
a04d08 |
LOG.warning(
|
|
Packit Service |
a04d08 |
"fs_setup:extra_opts ignored because cmd was specified: %s",
|
|
Packit Service |
a04d08 |
fs_cmd)
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
# Find the mkfs command
|
|
Packit Service |
751c4a |
mkfs_cmd = subp.which("mkfs.%s" % fs_type)
|
|
Packit Service |
a04d08 |
if not mkfs_cmd:
|
|
Packit Service |
751c4a |
mkfs_cmd = subp.which("mk%s" % fs_type)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if not mkfs_cmd:
|
|
Packit Service |
a04d08 |
LOG.warning("Cannot create fstype '%s'. No mkfs.%s command",
|
|
Packit Service |
a04d08 |
fs_type, fs_type)
|
|
Packit Service |
a04d08 |
return
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
fs_cmd = [mkfs_cmd, device]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if label:
|
|
Packit Service |
a04d08 |
fs_cmd.extend(["-L", label])
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# File systems that support the -F flag
|
|
Packit Service |
a04d08 |
if overwrite or device_type(device) == "disk":
|
|
Packit Service |
751c4a |
force_flag = lookup_force_flag(fs_type)
|
|
Packit Service |
751c4a |
if force_flag:
|
|
Packit Service |
751c4a |
fs_cmd.append(force_flag)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Add the extends FS options
|
|
Packit Service |
a04d08 |
if fs_opts:
|
|
Packit Service |
a04d08 |
fs_cmd.extend(fs_opts)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG.debug("Creating file system %s on %s", label, device)
|
|
Packit Service |
a04d08 |
LOG.debug(" Using cmd: %s", str(fs_cmd))
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
751c4a |
subp.subp(fs_cmd, shell=shell)
|
|
Packit Service |
a04d08 |
except Exception as e:
|
|
Packit Service |
751c4a |
raise Exception("Failed to exec of '%s':\n%s" % (fs_cmd, e)) from e
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# vi: ts=4 expandtab
|