Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 3 additions & 80 deletions cloudinit/config/cc_growpart.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,7 @@
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
Expand All @@ -95,58 +93,6 @@ 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":
Expand Down Expand Up @@ -262,18 +208,13 @@ def get_size(filename):
os.close(fd)


def device_part_info(devpath, is_lvm):
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)

# 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

Expand Down Expand Up @@ -303,7 +244,7 @@ def device_part_info(devpath, is_lvm):

# 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):
Expand Down Expand Up @@ -353,9 +294,8 @@ 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, is_lvm)
(disk, ptnum) = device_part_info(blockdev)
except (TypeError, ValueError) as e:
info.append((devent, RESIZE.SKIPPED,
"device_part_info(%s) failed: %s" % (blockdev, e),))
Expand All @@ -376,23 +316,6 @@ 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


Expand Down
2 changes: 0 additions & 2 deletions doc/examples/cloud-config-growpart.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
#
# devices:
# a list of things to resize.
# if the devices are under LVM, the list should be a single entry,
# cloud-init will then extend the single entry, otherwise it will fail.
# items can be filesystem paths or devices (in /dev)
# examples:
# devices: [/, /dev/vdb1]
Expand Down
56 changes: 1 addition & 55 deletions tests/unittests/test_handler/test_handler_growpart.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,53 +172,6 @@ def setUp(self):
self.name = "growpart"
self.log = logging.getLogger("TestResize")

def test_lvm_resize(self):
# LVM resize should work only if a single device is configured. More
# than one device should fail.
lvm_pass = ["/dev/XXdm-0"]
lvm_fail = ["/dev/XXdm-1", "/dev/YYdm-1"]
devstat_ret = Bunch(st_mode=25008, st_ino=6078, st_dev=5,
st_nlink=1, st_uid=0, st_gid=6, st_size=0,
st_atime=0, st_mtime=0, st_ctime=0)
real_stat = os.stat
resize_calls = []

class myresizer(object):
def resize(self, diskdev, partnum, partdev):
resize_calls.append((diskdev, partnum, partdev))
if partdev == "/dev/XXdm-0":
return (1024, 2048)
return (1024, 1024) # old size, new size

def mystat(path):
if path in lvm_pass or path in lvm_fail:
return devstat_ret
return real_stat(path)

try:
opinfo = cc_growpart.device_part_info
cc_growpart.device_part_info = simple_device_part_info_lvm
os.stat = mystat

resized = cc_growpart.resize_devices(myresizer(), lvm_pass)
not_resized = cc_growpart.resize_devices(myresizer(), lvm_fail)

def find(name, res):
for f in res:
if f[0] == name:
return f
return None

self.assertEqual(cc_growpart.RESIZE.CHANGED,
find("/dev/XXdm-0", resized)[1])
self.assertEqual(cc_growpart.RESIZE.NOCHANGE,
find("/dev/XXdm-1", not_resized)[1])
self.assertEqual(cc_growpart.RESIZE.NOCHANGE,
find("/dev/YYdm-1", not_resized)[1])
finally:
cc_growpart.device_part_info = opinfo
os.stat = real_stat

def test_simple_devices(self):
# test simple device list
# this patches out devent2dev, os.stat, and device_part_info
Expand Down Expand Up @@ -274,14 +227,7 @@ def find(name, res):
os.stat = real_stat


def simple_device_part_info_lvm(devpath, is_lvm):
# simple stupid return (/dev/vda, 1) for /dev/vda
ret = re.search("([^0-9]*)([0-9]*)$", devpath)
x = (ret.group(1), ret.group(2))
return x


def simple_device_part_info(devpath, is_lvm):
def simple_device_part_info(devpath):
# simple stupid return (/dev/vda, 1) for /dev/vda
ret = re.search("([^0-9]*)([0-9]*)$", devpath)
x = (ret.group(1), ret.group(2))
Expand Down