summaryrefslogtreecommitdiff
path: root/cloudinit/config/cc_disk_setup.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/config/cc_disk_setup.py')
-rw-r--r--cloudinit/config/cc_disk_setup.py146
1 files changed, 98 insertions, 48 deletions
diff --git a/cloudinit/config/cc_disk_setup.py b/cloudinit/config/cc_disk_setup.py
index f49386e3..c2b83aea 100644
--- a/cloudinit/config/cc_disk_setup.py
+++ b/cloudinit/config/cc_disk_setup.py
@@ -68,6 +68,9 @@ specified using ``filesystem``.
Using ``overwrite: true`` for filesystems is dangerous and can lead to data
loss, so double check the entry in ``fs_setup``.
+.. note::
+ ``replace_fs`` is ignored unless ``partition`` is ``auto`` or ``any``.
+
**Internal name:** ``cc_disk_setup``
**Module frequency:** per instance
@@ -127,7 +130,7 @@ def handle(_name, cfg, cloud, log, _args):
log.debug("Partitioning disks: %s", str(disk_setup))
for disk, definition in disk_setup.items():
if not isinstance(definition, dict):
- log.warn("Invalid disk definition for %s" % disk)
+ log.warning("Invalid disk definition for %s" % disk)
continue
try:
@@ -144,7 +147,7 @@ def handle(_name, cfg, cloud, log, _args):
update_fs_setup_devices(fs_setup, cloud.device_name_to_device)
for definition in fs_setup:
if not isinstance(definition, dict):
- log.warn("Invalid file system definition: %s" % definition)
+ log.warning("Invalid file system definition: %s" % definition)
continue
try:
@@ -199,8 +202,13 @@ def update_fs_setup_devices(disk_setup, tformer):
definition['_origname'] = origname
definition['device'] = tformed
- if part and 'partition' in definition:
- definition['_partition'] = definition['partition']
+ if part:
+ # In origname with <dev>.N, N overrides 'partition' key.
+ if 'partition' in definition:
+ LOG.warning("Partition '%s' from dotted device name '%s' "
+ "overrides 'partition' key in %s", part, origname,
+ definition)
+ definition['_partition'] = definition['partition']
definition['partition'] = part
@@ -423,7 +431,7 @@ def get_dyn_func(*args):
raise Exception("No such function %s to call!" % func_name)
-def get_mbr_hdd_size(device):
+def get_hdd_size(device):
try:
size_in_bytes, _ = util.subp([BLKDEV_CMD, '--getsize64', device])
sector_size, _ = util.subp([BLKDEV_CMD, '--getss', device])
@@ -433,22 +441,6 @@ def get_mbr_hdd_size(device):
return int(size_in_bytes) / int(sector_size)
-def get_gpt_hdd_size(device):
- out, _ = util.subp([SGDISK_CMD, '-p', device], update_env=LANG_C_ENV)
- for line in out.splitlines():
- if line.startswith("Disk"):
- return line.split()[2]
- raise Exception("Failed to get %s size from sgdisk" % (device))
-
-
-def get_hdd_size(table_type, device):
- """
- Returns the hard disk size.
- This works with any disk type, including GPT.
- """
- return get_dyn_func("get_%s_hdd_size", table_type, device)
-
-
def check_partition_mbr_layout(device, layout):
"""
Returns true if the partition layout matches the one on the disk
@@ -496,12 +488,35 @@ def check_partition_gpt_layout(device, layout):
device, e))
out_lines = iter(out.splitlines())
- # Skip header
+ # Skip header. Output looks like:
+ # ***************************************************************
+ # Found invalid GPT and valid MBR; converting MBR to GPT format
+ # in memory.
+ # ***************************************************************
+ #
+ # Disk /dev/vdb: 83886080 sectors, 40.0 GiB
+ # Logical sector size: 512 bytes
+ # Disk identifier (GUID): 8A7F11AD-3953-491B-8051-077E01C8E9A7
+ # Partition table holds up to 128 entries
+ # First usable sector is 34, last usable sector is 83886046
+ # Partitions will be aligned on 2048-sector boundaries
+ # Total free space is 83476413 sectors (39.8 GiB)
+ #
+ # Number Start (sector) End (sector) Size Code Name
+ # 1 2048 206847 100.0 MiB 0700 Microsoft basic data
for line in out_lines:
if line.strip().startswith('Number'):
break
- return [line.strip().split()[-1] for line in out_lines]
+ codes = [line.strip().split()[5] for line in out_lines]
+ cleaned = []
+
+ # user would expect a code '83' to be Linux, but sgdisk outputs 8300.
+ for code in codes:
+ if len(code) == 4 and code.endswith("00"):
+ code = code[0:2]
+ cleaned.append(code)
+ return cleaned
def check_partition_layout(table_type, device, layout):
@@ -515,6 +530,8 @@ def check_partition_layout(table_type, device, layout):
found_layout = get_dyn_func(
"check_partition_%s_layout", table_type, device, layout)
+ LOG.debug("called check_partition_%s_layout(%s, %s), returned: %s",
+ table_type, device, layout, found_layout)
if isinstance(layout, bool):
# if we are using auto partitioning, or "True" be happy
# if a single partition exists.
@@ -522,18 +539,17 @@ def check_partition_layout(table_type, device, layout):
return True
return False
- else:
- if len(found_layout) != len(layout):
- return False
- else:
- # This just makes sure that the number of requested
- # partitions and the type labels are right
- for x in range(1, len(layout) + 1):
- if isinstance(layout[x - 1], tuple):
- _, part_type = layout[x]
- if int(found_layout[x]) != int(part_type):
- return False
- return True
+ elif len(found_layout) == len(layout):
+ # This just makes sure that the number of requested
+ # partitions and the type labels are right
+ layout_types = [str(x[1]) if isinstance(x, (tuple, list)) else None
+ for x in layout]
+ LOG.debug("Layout types=%s. Found types=%s",
+ layout_types, found_layout)
+ for itype, ftype in zip(layout_types, found_layout):
+ if itype is not None and str(ftype) != str(itype):
+ return False
+ return True
return False
@@ -664,14 +680,14 @@ def read_parttbl(device):
reliable way to probe the partition table.
"""
blkdev_cmd = [BLKDEV_CMD, '--rereadpt', device]
- udev_cmd = [UDEVADM_CMD, 'settle']
+ udevadm_settle()
try:
- util.subp(udev_cmd)
util.subp(blkdev_cmd)
- util.subp(udev_cmd)
except Exception as e:
util.logexc(LOG, "Failed reading the partition table %s" % e)
+ udevadm_settle()
+
def exec_mkpart_mbr(device, layout):
"""
@@ -696,9 +712,11 @@ def exec_mkpart_gpt(device, layout):
util.subp([SGDISK_CMD,
'-n', '{}:{}:{}'.format(index, start, end), device])
if partition_type is not None:
+ # convert to a 4 char (or more) string right padded with 0
+ # 82 -> 8200. 'Linux' -> 'Linux'
+ pinput = str(partition_type).ljust(4, "0")
util.subp(
- [SGDISK_CMD,
- '-t', '{}:{}'.format(index, partition_type), device])
+ [SGDISK_CMD, '-t', '{}:{}'.format(index, pinput), device])
except Exception:
LOG.warning("Failed to partition device %s", device)
raise
@@ -719,6 +737,24 @@ def exec_mkpart(table_type, device, layout):
return get_dyn_func("exec_mkpart_%s", table_type, device, layout)
+def udevadm_settle():
+ util.subp(['udevadm', 'settle'])
+
+
+def assert_and_settle_device(device):
+ """Assert that device exists and settle so it is fully recognized."""
+ if not os.path.exists(device):
+ udevadm_settle()
+ if not os.path.exists(device):
+ raise RuntimeError("Device %s did not exist and was not created "
+ "with a udevamd settle." % device)
+
+ # Whether or not the device existed above, it is possible that udev
+ # events that would populate udev database (for reading by lsdname) have
+ # not yet finished. So settle again.
+ udevadm_settle()
+
+
def mkpart(device, definition):
"""
Creates the partition table.
@@ -734,6 +770,7 @@ def mkpart(device, definition):
device: the device to work on.
"""
# ensure that we get a real device rather than a symbolic link
+ assert_and_settle_device(device)
device = os.path.realpath(device)
LOG.debug("Checking values for %s definition", device)
@@ -769,8 +806,8 @@ def mkpart(device, definition):
LOG.debug("Skipping partitioning on configured device %s", device)
return
- LOG.debug("Checking for device size")
- device_size = get_hdd_size(table_type, device)
+ LOG.debug("Checking for device size of %s", device)
+ device_size = get_hdd_size(device)
LOG.debug("Calculating partition layout")
part_definition = get_partition_layout(table_type, device_size, layout)
@@ -834,6 +871,7 @@ def mkfs(fs_cfg):
overwrite = fs_cfg.get('overwrite', False)
# ensure that we get a real device rather than a symbolic link
+ assert_and_settle_device(device)
device = os.path.realpath(device)
# This allows you to define the default ephemeral or swap
@@ -849,7 +887,8 @@ def mkfs(fs_cfg):
# Check to see if the fs already exists
LOG.debug("Checking device %s", device)
check_label, check_fstype, _ = check_fs(device)
- LOG.debug("Device %s has %s %s", device, check_label, check_fstype)
+ LOG.debug("Device '%s' has check_label='%s' check_fstype=%s",
+ device, check_label, check_fstype)
if check_label == label and check_fstype == fs_type:
LOG.debug("Existing file system found at %s", device)
@@ -910,12 +949,23 @@ def mkfs(fs_cfg):
"must be set.", label)
# Create the commands
+ shell = False
if fs_cmd:
fs_cmd = fs_cfg['cmd'] % {
'label': label,
'filesystem': fs_type,
'device': device,
}
+ shell = True
+
+ if overwrite:
+ LOG.warning(
+ "fs_setup:overwrite ignored because cmd was specified: %s",
+ fs_cmd)
+ if fs_opts:
+ LOG.warning(
+ "fs_setup:extra_opts ignored because cmd was specified: %s",
+ fs_cmd)
else:
# Find the mkfs command
mkfs_cmd = util.which("mkfs.%s" % fs_type)
@@ -936,14 +986,14 @@ def mkfs(fs_cfg):
if overwrite or device_type(device) == "disk":
fs_cmd.append(lookup_force_flag(fs_type))
- # Add the extends FS options
- if fs_opts:
- fs_cmd.extend(fs_opts)
+ # Add the extends FS options
+ if fs_opts:
+ fs_cmd.extend(fs_opts)
LOG.debug("Creating file system %s on %s", label, device)
- LOG.debug(" Using cmd: %s", " ".join(fs_cmd))
+ LOG.debug(" Using cmd: %s", str(fs_cmd))
try:
- util.subp(fs_cmd)
+ util.subp(fs_cmd, shell=shell)
except Exception as e:
raise Exception("Failed to exec of '%s':\n%s" % (fs_cmd, e))