summaryrefslogtreecommitdiff
path: root/cloudinit/config
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/config')
-rw-r--r--cloudinit/config/cc_growpart.py83
1 files changed, 80 insertions, 3 deletions
diff --git a/cloudinit/config/cc_growpart.py b/cloudinit/config/cc_growpart.py
index 9f338ad1..6399bfb7 100644
--- a/cloudinit/config/cc_growpart.py
+++ b/cloudinit/config/cc_growpart.py
@@ -68,7 +68,9 @@ import os
import os.path
import re
import stat
+import platform
+from functools import lru_cache
from cloudinit import log as logging
from cloudinit.settings import PER_ALWAYS
from cloudinit import subp
@@ -93,6 +95,58 @@ class RESIZE(object):
LOG = logging.getLogger(__name__)
+@lru_cache()
+def is_lvm_lv(devpath):
+ if util.is_Linux():
+ # all lvm lvs will have a realpath as a 'dm-*' name.
+ rpath = os.path.realpath(devpath)
+ if not os.path.basename(rpath).startswith("dm-"):
+ return False
+ out, _ = subp.subp("udevadm", "info", devpath)
+ # lvs should have DM_LV_NAME=<lvmuuid> and also DM_VG_NAME
+ return 'DM_LV_NAME=' in out
+ else:
+ LOG.info("Not an LVM Logical Volume partition")
+ return False
+
+
+@lru_cache()
+def get_pvs_for_lv(devpath):
+ myenv = {'LANG': 'C'}
+
+ if not util.is_Linux():
+ LOG.info("No support for LVM on %s", platform.system())
+ return None
+ if not subp.which('lvm'):
+ LOG.info("No 'lvm' command present")
+ return None
+
+ try:
+ (out, _err) = subp.subp(["lvm", "lvs", devpath, "--options=vgname",
+ "--noheadings"], update_env=myenv)
+ vgname = out.strip()
+ except subp.ProcessExecutionError as e:
+ if e.exit_code != 0:
+ util.logexc(LOG, "Failed: can't get Volume Group information "
+ "from %s", devpath)
+ raise ResizeFailedException(e) from e
+
+ try:
+ (out, _err) = subp.subp(["lvm", "vgs", vgname, "--options=pvname",
+ "--noheadings"], update_env=myenv)
+ pvs = [p.strip() for p in out.splitlines()]
+ if len(pvs) > 1:
+ LOG.info("Do not know how to resize multiple Physical"
+ " Volumes")
+ else:
+ return pvs[0]
+ except subp.ProcessExecutionError as e:
+ if e.exit_code != 0:
+ util.logexc(LOG, "Failed: can't get Physical Volume "
+ "information from Volume Group %s", vgname)
+ raise ResizeFailedException(e) from e
+
+
def resizer_factory(mode):
resize_class = None
if mode == "auto":
@@ -208,13 +262,18 @@ def get_size(filename):
os.close(fd)
-def device_part_info(devpath):
+def device_part_info(devpath, is_lvm):
# 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)
+ # first check if this is an LVM and get its PVs
+ lvm_rpath = get_pvs_for_lv(devpath)
+ if is_lvm and lvm_rpath:
+ rpath = lvm_rpath
+
bname = os.path.basename(rpath)
syspath = "/sys/class/block/%s" % bname
@@ -244,7 +303,7 @@ def device_part_info(devpath):
# 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)
+ return diskdevpath, ptnum
def devent2dev(devent):
@@ -294,8 +353,9 @@ def resize_devices(resizer, devices):
"device '%s' not a block device" % blockdev,))
continue
+ is_lvm = is_lvm_lv(blockdev)
try:
- (disk, ptnum) = device_part_info(blockdev)
+ disk, ptnum = device_part_info(blockdev, is_lvm)
except (TypeError, ValueError) as e:
info.append((devent, RESIZE.SKIPPED,
"device_part_info(%s) failed: %s" % (blockdev, e),))
@@ -316,6 +376,23 @@ def resize_devices(resizer, devices):
"failed to resize: disk=%s, ptnum=%s: %s" %
(disk, ptnum, e),))
+ if is_lvm and isinstance(resizer, ResizeGrowPart):
+ try:
+ if len(devices) == 1:
+ (_out, _err) = subp.subp(
+ ["lvm", "lvextend", "--extents=100%FREE", blockdev],
+ update_env={'LANG': 'C'})
+ info.append((devent, RESIZE.CHANGED,
+ "Logical Volume %s extended" % devices[0],))
+ else:
+ LOG.info("Exactly one device should be configured to be "
+ "resized when using LVM. More than one configured"
+ ": %s", devices)
+ except (subp.ProcessExecutionError, ValueError) as e:
+ info.append((devent, RESIZE.NOCHANGE,
+ "Logical Volume %s resize failed: %s" %
+ (blockdev, e),))
+
return info