summaryrefslogtreecommitdiff
path: root/cloudinit/config
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/config')
-rw-r--r--cloudinit/config/cc_debug.py4
-rw-r--r--cloudinit/config/cc_growpart.py46
-rw-r--r--cloudinit/config/cc_power_state_change.py29
-rw-r--r--cloudinit/config/cc_resizefs.py40
4 files changed, 105 insertions, 14 deletions
diff --git a/cloudinit/config/cc_debug.py b/cloudinit/config/cc_debug.py
index cfd31fa1..7219b0f8 100644
--- a/cloudinit/config/cc_debug.py
+++ b/cloudinit/config/cc_debug.py
@@ -14,10 +14,10 @@
# 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 StringIO import StringIO
-from cloudinit import util
from cloudinit import type_utils
+from cloudinit import util
import copy
+from StringIO import StringIO
def _make_header(text):
diff --git a/cloudinit/config/cc_growpart.py b/cloudinit/config/cc_growpart.py
index 6bddf847..f52c41f0 100644
--- a/cloudinit/config/cc_growpart.py
+++ b/cloudinit/config/cc_growpart.py
@@ -114,6 +114,41 @@ class ResizeGrowPart(object):
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:
@@ -132,6 +167,12 @@ def device_part_info(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))
@@ -182,7 +223,8 @@ def resize_devices(resizer, devices):
"stat of '%s' failed: %s" % (blockdev, e),))
continue
- if not stat.S_ISBLK(statret.st_mode):
+ 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
@@ -255,4 +297,4 @@ def handle(_name, cfg, _cloud, log, _args):
else:
log.debug("'%s' %s: %s" % (entry, action, msg))
-RESIZERS = (('growpart', ResizeGrowPart),)
+RESIZERS = (('growpart', ResizeGrowPart), ('gpart', ResizeGpart))
diff --git a/cloudinit/config/cc_power_state_change.py b/cloudinit/config/cc_power_state_change.py
index e3150808..561c5abd 100644
--- a/cloudinit/config/cc_power_state_change.py
+++ b/cloudinit/config/cc_power_state_change.py
@@ -22,6 +22,7 @@ from cloudinit import util
import errno
import os
import re
+import signal
import subprocess
import time
@@ -30,6 +31,24 @@ frequency = PER_INSTANCE
EXIT_FAIL = 254
+def givecmdline(pid):
+ # Returns the cmdline for the given process id. In Linux we can use procfs
+ # for this but on BSD there is /usr/bin/procstat.
+ try:
+ # Example output from procstat -c 1
+ # PID COMM ARGS
+ # 1 init /bin/init --
+ if util.system_info()["platform"].startswith('FreeBSD'):
+ (output, _err) = util.subp(['procstat', '-c', str(pid)])
+ line = output.splitlines()[1]
+ m = re.search('\d+ (\w|\.|-)+\s+(/\w.+)', line)
+ return m.group(2)
+ else:
+ return util.load_file("/proc/%s/cmdline" % pid)
+ except IOError:
+ return None
+
+
def handle(_name, cfg, _cloud, log, _args):
try:
@@ -42,8 +61,8 @@ def handle(_name, cfg, _cloud, log, _args):
return
mypid = os.getpid()
- cmdline = util.load_file("/proc/%s/cmdline" % mypid)
+ cmdline = givecmdline(mypid)
if not cmdline:
log.warn("power_state: failed to get cmdline of current process")
return
@@ -119,8 +138,6 @@ def run_after_pid_gone(pid, pidcmdline, timeout, log, func, args):
msg = None
end_time = time.time() + timeout
- cmdline_f = "/proc/%s/cmdline" % pid
-
def fatal(msg):
if log:
log.warn(msg)
@@ -134,16 +151,14 @@ def run_after_pid_gone(pid, pidcmdline, timeout, log, func, args):
break
try:
- cmdline = ""
- with open(cmdline_f) as fp:
- cmdline = fp.read()
+ cmdline = givecmdline(pid)
if cmdline != pidcmdline:
msg = "cmdline changed for %s [now: %s]" % (pid, cmdline)
break
except IOError as ioerr:
if ioerr.errno in known_errnos:
- msg = "pidfile '%s' gone [%d]" % (cmdline_f, ioerr.errno)
+ msg = "pidfile gone [%d]" % ioerr.errno
else:
fatal("IOError during wait: %s" % ioerr)
break
diff --git a/cloudinit/config/cc_resizefs.py b/cloudinit/config/cc_resizefs.py
index 56040fdd..be406034 100644
--- a/cloudinit/config/cc_resizefs.py
+++ b/cloudinit/config/cc_resizefs.py
@@ -39,6 +39,10 @@ def _resize_ext(mount_point, devpth): # pylint: disable=W0613
def _resize_xfs(mount_point, devpth): # pylint: disable=W0613
return ('xfs_growfs', devpth)
+
+def _resize_ufs(mount_point, devpth): # pylint: disable=W0613
+ return ('growfs', devpth)
+
# Do not use a dictionary as these commands should be able to be used
# for multiple filesystem types if possible, e.g. one command for
# ext2, ext3 and ext4.
@@ -46,11 +50,31 @@ RESIZE_FS_PREFIXES_CMDS = [
('btrfs', _resize_btrfs),
('ext', _resize_ext),
('xfs', _resize_xfs),
+ ('ufs', _resize_ufs),
]
NOBLOCK = "noblock"
+def rootdev_from_cmdline(cmdline):
+ found = None
+ for tok in cmdline.split():
+ if tok.startswith("root="):
+ found = tok[5:]
+ break
+ if found is None:
+ return None
+
+ if found.startswith("/dev/"):
+ return found
+ if found.startswith("LABEL="):
+ return "/dev/disk/by-label/" + found[len("LABEL="):]
+ if found.startswith("UUID="):
+ return "/dev/disk/by-uuid/" + found[len("UUID="):]
+
+ return "/dev/" + found
+
+
def handle(name, cfg, _cloud, log, args):
if len(args) != 0:
resize_root = args[0]
@@ -78,10 +102,20 @@ def handle(name, cfg, _cloud, log, args):
info = "dev=%s mnt_point=%s path=%s" % (devpth, mount_point, resize_what)
log.debug("resize_info: %s" % info)
+ container = util.is_container()
+
+ if (devpth == "/dev/root" and not os.path.exists(devpth) and
+ not container):
+ devpth = rootdev_from_cmdline(util.get_cmdline())
+ if devpth is None:
+ log.warn("Unable to find device '/dev/root'")
+ return
+ log.debug("Converted /dev/root to '%s' per kernel cmdline", devpth)
+
try:
statret = os.stat(devpth)
except OSError as exc:
- if util.is_container() and exc.errno == errno.ENOENT:
+ if container and exc.errno == errno.ENOENT:
log.debug("Device '%s' did not exist in container. "
"cannot resize: %s" % (devpth, info))
elif exc.errno == errno.ENOENT:
@@ -91,8 +125,8 @@ def handle(name, cfg, _cloud, log, args):
raise exc
return
- if not stat.S_ISBLK(statret.st_mode):
- if util.is_container():
+ if not stat.S_ISBLK(statret.st_mode) and not stat.S_ISCHR(statret.st_mode):
+ if container:
log.debug("device '%s' not a block device in container."
" cannot resize: %s" % (devpth, info))
else: