diff options
Diffstat (limited to 'cloudinit/config/cc_growpart.py')
-rw-r--r-- | cloudinit/config/cc_growpart.py | 300 |
1 files changed, 0 insertions, 300 deletions
diff --git a/cloudinit/config/cc_growpart.py b/cloudinit/config/cc_growpart.py deleted file mode 100644 index 40560f11..00000000 --- a/cloudinit/config/cc_growpart.py +++ /dev/null @@ -1,300 +0,0 @@ -# vi: ts=4 expandtab -# -# Copyright (C) 2011 Canonical Ltd. -# Copyright (C) 2013 Hewlett-Packard Development Company, L.P. -# -# Author: Scott Moser <scott.moser@canonical.com> -# Author: Juerg Haefliger <juerg.haefliger@hp.com> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 3, as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -import os -import os.path -import re -import stat - -from cloudinit import log as logging -from cloudinit.settings import PER_ALWAYS -from cloudinit import util - -frequency = PER_ALWAYS - -DEFAULT_CONFIG = { - 'mode': 'auto', - 'devices': ['/'], - 'ignore_growroot_disabled': False, -} - - -class RESIZE(object): - SKIPPED = "SKIPPED" - CHANGED = "CHANGED" - NOCHANGE = "NOCHANGE" - FAILED = "FAILED" - - -LOG = logging.getLogger(__name__) - - -def resizer_factory(mode): - resize_class = None - if mode == "auto": - for (_name, resizer) in RESIZERS: - cur = resizer() - if cur.available(): - resize_class = cur - break - - if not resize_class: - raise ValueError("No resizers available") - - else: - mmap = {} - for (k, v) in RESIZERS: - mmap[k] = v - - if mode not in mmap: - raise TypeError("unknown resize mode %s" % mode) - - mclass = mmap[mode]() - if mclass.available(): - resize_class = mclass - - if not resize_class: - raise ValueError("mode %s not available" % mode) - - return resize_class - - -class ResizeFailedException(Exception): - pass - - -class ResizeGrowPart(object): - def available(self): - myenv = os.environ.copy() - myenv['LANG'] = 'C' - - try: - (out, _err) = util.subp(["growpart", "--help"], env=myenv) - if re.search(r"--update\s+", out, re.DOTALL): - return True - - except util.ProcessExecutionError: - pass - return False - - def resize(self, diskdev, partnum, partdev): - before = get_size(partdev) - try: - util.subp(["growpart", '--dry-run', diskdev, partnum]) - except util.ProcessExecutionError as e: - if e.exit_code != 1: - util.logexc(LOG, "Failed growpart --dry-run for (%s, %s)", - diskdev, partnum) - raise ResizeFailedException(e) - return (before, before) - - try: - util.subp(["growpart", diskdev, partnum]) - except util.ProcessExecutionError as e: - util.logexc(LOG, "Failed: growpart %s %s", diskdev, partnum) - raise ResizeFailedException(e) - - return (before, get_size(partdev)) - - -class ResizeGpart(object): - def available(self): - if not util.which('gpart'): - return False - return True - - def resize(self, diskdev, partnum, partdev): - """ - GPT disks store metadata at the beginning (primary) and at the - end (secondary) of the disk. When launching an image with a - larger disk compared to the original image, the secondary copy - is lost. Thus, the metadata will be marked CORRUPT, and need to - be recovered. - """ - try: - util.subp(["gpart", "recover", diskdev]) - except util.ProcessExecutionError as e: - if e.exit_code != 0: - util.logexc(LOG, "Failed: gpart recover %s", diskdev) - raise ResizeFailedException(e) - - before = get_size(partdev) - try: - util.subp(["gpart", "resize", "-i", partnum, diskdev]) - except util.ProcessExecutionError as e: - util.logexc(LOG, "Failed: gpart resize -i %s %s", partnum, diskdev) - raise ResizeFailedException(e) - - # Since growing the FS requires a reboot, make sure we reboot - # first when this module has finished. - open('/var/run/reboot-required', 'a').close() - - return (before, get_size(partdev)) - - -def get_size(filename): - fd = os.open(filename, os.O_RDONLY) - try: - return os.lseek(fd, 0, os.SEEK_END) - finally: - os.close(fd) - - -def device_part_info(devpath): - # convert an entry in /dev/ to parent disk and partition number - - # input of /dev/vdb or /dev/disk/by-label/foo - # rpath is hopefully a real-ish path in /dev (vda, sdb..) - rpath = os.path.realpath(devpath) - - bname = os.path.basename(rpath) - syspath = "/sys/class/block/%s" % bname - - # FreeBSD doesn't know of sysfs so just get everything we need from - # the device, like /dev/vtbd0p2. - if util.system_info()["platform"].startswith('FreeBSD'): - m = re.search('^(/dev/.+)p([0-9])$', devpath) - return (m.group(1), m.group(2)) - - if not os.path.exists(syspath): - raise ValueError("%s had no syspath (%s)" % (devpath, syspath)) - - ptpath = os.path.join(syspath, "partition") - if not os.path.exists(ptpath): - raise TypeError("%s not a partition" % devpath) - - ptnum = util.load_file(ptpath).rstrip() - - # for a partition, real syspath is something like: - # /sys/devices/pci0000:00/0000:00:04.0/virtio1/block/vda/vda1 - rsyspath = os.path.realpath(syspath) - disksyspath = os.path.dirname(rsyspath) - - diskmajmin = util.load_file(os.path.join(disksyspath, "dev")).rstrip() - diskdevpath = os.path.realpath("/dev/block/%s" % diskmajmin) - - # diskdevpath has something like 253:0 - # and udev has put links in /dev/block/253:0 to the device name in /dev/ - return (diskdevpath, ptnum) - - -def devent2dev(devent): - if devent.startswith("/dev/"): - return devent - else: - result = util.get_mount_info(devent) - if not result: - raise ValueError("Could not determine device of '%s' % dev_ent") - return result[0] - - -def resize_devices(resizer, devices): - # returns a tuple of tuples containing (entry-in-devices, action, message) - info = [] - for devent in devices: - try: - blockdev = devent2dev(devent) - except ValueError as e: - info.append((devent, RESIZE.SKIPPED, - "unable to convert to device: %s" % e,)) - continue - - try: - statret = os.stat(blockdev) - except OSError as e: - info.append((devent, RESIZE.SKIPPED, - "stat of '%s' failed: %s" % (blockdev, e),)) - continue - - if (not stat.S_ISBLK(statret.st_mode) and - not stat.S_ISCHR(statret.st_mode)): - info.append((devent, RESIZE.SKIPPED, - "device '%s' not a block device" % blockdev,)) - continue - - try: - (disk, ptnum) = device_part_info(blockdev) - except (TypeError, ValueError) as e: - info.append((devent, RESIZE.SKIPPED, - "device_part_info(%s) failed: %s" % (blockdev, e),)) - continue - - try: - (old, new) = resizer.resize(disk, ptnum, blockdev) - if old == new: - info.append((devent, RESIZE.NOCHANGE, - "no change necessary (%s, %s)" % (disk, ptnum),)) - else: - info.append((devent, RESIZE.CHANGED, - "changed (%s, %s) from %s to %s" % - (disk, ptnum, old, new),)) - - except ResizeFailedException as e: - info.append((devent, RESIZE.FAILED, - "failed to resize: disk=%s, ptnum=%s: %s" % - (disk, ptnum, e),)) - - return info - - -def handle(_name, cfg, _cloud, log, _args): - if 'growpart' not in cfg: - log.debug("No 'growpart' entry in cfg. Using default: %s" % - DEFAULT_CONFIG) - cfg['growpart'] = DEFAULT_CONFIG - - mycfg = cfg.get('growpart') - if not isinstance(mycfg, dict): - log.warn("'growpart' in config was not a dict") - return - - mode = mycfg.get('mode', "auto") - if util.is_false(mode): - log.debug("growpart disabled: mode=%s" % mode) - return - - if util.is_false(mycfg.get('ignore_growroot_disabled', False)): - if os.path.isfile("/etc/growroot-disabled"): - log.debug("growpart disabled: /etc/growroot-disabled exists") - log.debug("use ignore_growroot_disabled to ignore") - return - - devices = util.get_cfg_option_list(mycfg, "devices", ["/"]) - if not len(devices): - log.debug("growpart: empty device list") - return - - try: - resizer = resizer_factory(mode) - except (ValueError, TypeError) as e: - log.debug("growpart unable to find resizer for '%s': %s" % (mode, e)) - if mode != "auto": - raise e - return - - resized = util.log_time(logfunc=log.debug, msg="resize_devices", - func=resize_devices, args=(resizer, devices)) - for (entry, action, msg) in resized: - if action == RESIZE.CHANGED: - log.info("'%s' resized: %s" % (entry, msg)) - else: - log.debug("'%s' %s: %s" % (entry, action, msg)) - -RESIZERS = (('growpart', ResizeGrowPart), ('gpart', ResizeGpart)) |