diff options
author | Ben Howard <ben.howard@canonical.com> | 2013-10-02 15:05:15 -0600 |
---|---|---|
committer | Ben Howard <ben.howard@canonical.com> | 2013-10-02 15:05:15 -0600 |
commit | ea88f3e1208ba501b50d1f41187b83cf11f15785 (patch) | |
tree | a1c1ea966d8ed38670968d809dba88750cc4cd96 /cloudinit/config | |
parent | ed8cb662f72e4e3e03ee0489593b7d114e5618cf (diff) | |
download | vyos-cloud-init-ea88f3e1208ba501b50d1f41187b83cf11f15785.tar.gz vyos-cloud-init-ea88f3e1208ba501b50d1f41187b83cf11f15785.zip |
Added ability to define disks via 'ephemeralX.Y'.
Modified cc_mounts to identify whether ephermalX is partitioned.
Changed datasources for Azure and SmartOS to use 'ephemeralX.Y' format.
Added disk remove functionally
Diffstat (limited to 'cloudinit/config')
-rw-r--r-- | cloudinit/config/cc_disk_setup.py | 129 | ||||
-rw-r--r-- | cloudinit/config/cc_mounts.py | 37 |
2 files changed, 144 insertions, 22 deletions
diff --git a/cloudinit/config/cc_disk_setup.py b/cloudinit/config/cc_disk_setup.py index ade9c2ad..faf424ba 100644 --- a/cloudinit/config/cc_disk_setup.py +++ b/cloudinit/config/cc_disk_setup.py @@ -16,8 +16,8 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -from cloudinit.settings import PER_INSTANCE from cloudinit import util +from cloudinit.settings import PER_INSTANCE import logging import shlex @@ -29,13 +29,13 @@ SFDISK_CMD = util.which("sfdisk") LSBLK_CMD = util.which("lsblk") BLKID_CMD = util.which("blkid") BLKDEV_CMD = util.which("blockdev") +WIPEFS_CMD = util.which("wipefs") LOG = logging.getLogger(__name__) def handle(_name, cfg, cloud, log, _args): """ - Call util.prep_disk for disk_setup cloud-config. See doc/examples/cloud-config_disk-setup.txt for documentation on the format. """ @@ -203,23 +203,11 @@ def is_filesystem(device): return fs_type -def find_device_node(device, fs_type=None, label=None, valid_targets=None, - label_match=True): +def enumerate_disk(device): """ - Find a device that is either matches the spec, or the first - - The return is value is (<device>, <bool>) where the device is the - device to use and the bool is whether the device matches the - fs_type and label. - - Note: This works with GPT partition tables! + Enumerate the elements of a child device. Return a dict of name, + type, fstype, and label """ - # label of None is same as no label - if label is None: - label = "" - - if not valid_targets: - valid_targets = ['disk', 'part'] lsblk_cmd = [LSBLK_CMD, '--pairs', '--out', 'NAME,TYPE,FSTYPE,LABEL', device] @@ -229,7 +217,6 @@ def find_device_node(device, fs_type=None, label=None, valid_targets=None, except Exception as e: raise Exception("Failed during disk check for %s\n%s" % (device, e)) - raw_device_used = False parts = [x for x in (info.strip()).splitlines() if len(x.split()) > 0] for part in parts: @@ -242,6 +229,35 @@ def find_device_node(device, fs_type=None, label=None, valid_targets=None, for key, value in value_splitter(part): d[key.lower()] = value + LOG.info(d) + yield d + + +def find_device_node(device, fs_type=None, label=None, valid_targets=None, + label_match=True, replace_fs=None): + """ + Find a device that is either matches the spec, or the first + + The return is value is (<device>, <bool>) where the device is the + device to use and the bool is whether the device matches the + fs_type and label. + + Note: This works with GPT partition tables! + """ + # label of None is same as no label + if label is None: + label = "" + + if not valid_targets: + valid_targets = ['disk', 'part'] + + raw_device_used = False + for d in enumerate_disk(device): + + if d['fstype'] == replace_fs and label_match == False: + # We found a device where we want to replace the FS + return ('/dev/%s' % d['name'], False) + if (d['fstype'] == fs_type and ((label_match and d['label'] == label) or not label_match)): # If we find a matching device, we return that @@ -454,6 +470,42 @@ def get_partition_mbr_layout(size, layout): return sfdisk_definition +def purge_disk(device): + """ + Remove parition table entries + """ + + # wipe any file systems first + for d in enumerate_disk(device): + LOG.info(d) + if d['type'] not in ["disk", "crypt"]: + wipefs_cmd = [WIPEFS_CMD, "--all", "/dev/%s" % d['name']] + try: + LOG.info("Purging filesystem on /dev/%s" % d['name']) + util.subp(wipefs_cmd) + LOG.info("Purged filesystem on /dev/%s" % d['name']) + except Exception as e: + raise Exception("Failed FS purge of /dev/%s" % d['name']) + + dd_cmd = util.which("dd") + last_seek = int(get_hdd_size(device) / 1024) - 2 + first_mb = [dd_cmd, "if=/dev/zero", "of=%s" % device, "bs=1M", "count=1"] + last_mb = [dd_cmd, "if=/dev/zero", "of=%s" % device, "bs=1M", "seek=%s" % last_seek] + try: + util.subp(first_mb) + LOG.info("Purged MBR/Partition table from %s" % device) + util.subp(last_mb, rcs=[0,1]) + LOG.info("Purged any chance of GPT table from %s" % device) + + # Wipe it for good measure + wipefs_cmd = [WIPEFS_CMD, "--all", device] + util.subp(wipefs_cmd) + except Exception as e: + LOG.critical(e) + raise Exception("Failed to remove MBR/Part from %s" % device) + + read_parttbl(device) + def get_partition_layout(table_type, size, layout): """ @@ -542,6 +594,12 @@ def mkpart(device, definition): if not is_device_valid(device): raise Exception("Device %s is not a disk device!", device) + # Remove the partition table entries + if isinstance(layout, str) and layout.lower() == "remove": + LOG.debug("Instructed to remove partition table entries") + purge_disk(device) + return + LOG.debug("Checking if device layout matches") if check_partition_layout(table_type, device, layout): LOG.debug("Device partitioning layout matches") @@ -565,6 +623,26 @@ def mkpart(device, definition): LOG.debug("Partition table created for %s", device) +def lookup_force_flag(fs): + """ + A force flag might be -F or -F, this look it up + """ + flags = {'ext': '-F', + 'btrfs': '-f', + 'xfs': '-f', + 'reiserfs': '-f', + } + + if 'ext' in fs.lower(): + fs = 'ext' + + if fs.lower() in flags: + return flags[fs] + + LOG.warn("Force flag for %s is unknown." % fs) + return '' + + def mkfs(fs_cfg): """ Create a file system on the device. @@ -592,6 +670,7 @@ def mkfs(fs_cfg): fs_type = fs_cfg.get('filesystem') fs_cmd = fs_cfg.get('cmd', []) fs_opts = fs_cfg.get('extra_opts', []) + fs_replace = fs_cfg.get('replace_fs', False) overwrite = fs_cfg.get('overwrite', False) # This allows you to define the default ephemeral or swap @@ -632,17 +711,23 @@ def mkfs(fs_cfg): label_match = False device, reuse = find_device_node(device, fs_type=fs_type, label=label, - label_match=label_match) + label_match=label_match, + replace_fs=fs_replace) LOG.debug("Automatic device for %s identified as %s", odevice, device) if reuse: LOG.debug("Found filesystem match, skipping formating.") return + if not reuse and fs_replace and device: + LOG.debug("Replacing file system on %s as instructed." % device) + if not device: LOG.debug("No device aviable that matches request. " "Skipping fs creation for %s", fs_cfg) return + elif not partition or str(partition).lower() == 'none': + LOG.debug("Using the raw device to place filesystem %s on" % label) else: LOG.debug("Error in device identification handling.") @@ -682,12 +767,16 @@ def mkfs(fs_cfg): if label: fs_cmd.extend(["-L", label]) + # File systems that support the -F flag + if not fs_cmd and (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) LOG.debug("Creating file system %s on %s", label, device) - LOG.debug(" Using cmd: %s", "".join(fs_cmd)) + LOG.debug(" Using cmd: %s", " ".join(fs_cmd)) try: util.subp(fs_cmd) except Exception as e: diff --git a/cloudinit/config/cc_mounts.py b/cloudinit/config/cc_mounts.py index 390ba711..f4c2e3d8 100644 --- a/cloudinit/config/cc_mounts.py +++ b/cloudinit/config/cc_mounts.py @@ -20,6 +20,7 @@ from string import whitespace # pylint: disable=W0402 +import os.path import re from cloudinit import type_utils @@ -75,7 +76,9 @@ def handle(_name, cfg, cloud, log, _args): "name from ephemeral to ephemeral0"), (i + 1)) if is_mdname(startname): - newname = cloud.device_name_to_device(startname) + candidate_name = cloud.device_name_to_device(startname) + newname = disk_or_part(candidate_name) + if not newname: log.debug("Ignoring nonexistant named mount %s", startname) cfgmnt[i][1] = None @@ -119,7 +122,8 @@ def handle(_name, cfg, cloud, log, _args): # entry has the same device name for defmnt in defmnts: startname = defmnt[0] - devname = cloud.device_name_to_device(startname) + candidate_name = cloud.device_name_to_device(startname) + devname = disk_or_part(candidate_name) if devname is None: log.debug("Ignoring nonexistant named default mount %s", startname) continue @@ -198,3 +202,32 @@ def handle(_name, cfg, cloud, log, _args): util.subp(("mount", "-a")) except: util.logexc(log, "Activating mounts via 'mount -a' failed") + + +def disk_or_part(device): + """ + Find where the file system is on the disk, either on + the disk itself or on the first partition. We don't go + any deeper than partition 1 though. + """ + + if not device: + return None + + short_name = device.split('/')[-1] + sys_path = "/sys/block/%s" % short_name + + if not os.path.exists(sys_path): + LOG.warn("Device %s does not exist in sysfs" % device) + return None + + sys_long_path = sys_path + "/" + short_name + "%s" + valid_mappings = [ sys_long_path % "1", + sys_long_path % "p1", + sys_path ] + + for cdisk in valid_mappings: + if not os.path.exists(cdisk): + continue + return "/dev/%s" % cdisk.split('/')[-1] + return None |