cloud-init/cloudinit/config/cc_resizefs.py
Scott Moser d09712ac24 add util.log_time helper
The reason for this is that more and more things I was wanting to be able to
see how long they took.  This puts that time logic into a single place.  It
also supports (by default) reading from /proc/uptime as the timing mechanism.

While that is almost certainly slower than time.time(), it does give 
millisecond granularity and is not affected by 'ntpdate' having
run in between the two events.
2013-07-30 14:23:10 -04:00

144 lines
4.6 KiB
Python

# vi: ts=4 expandtab
#
# Copyright (C) 2011 Canonical Ltd.
# Copyright (C) 2012 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 errno
import os
import stat
from cloudinit.settings import PER_ALWAYS
from cloudinit import util
frequency = PER_ALWAYS
def _resize_btrfs(mount_point, devpth): # pylint: disable=W0613
return ('btrfs', 'filesystem', 'resize', 'max', mount_point)
def _resize_ext(mount_point, devpth): # pylint: disable=W0613
return ('resize2fs', devpth)
def _resize_xfs(mount_point, devpth): # pylint: disable=W0613
return ('xfs_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.
RESIZE_FS_PREFIXES_CMDS = [
('btrfs', _resize_btrfs),
('ext', _resize_ext),
('xfs', _resize_xfs),
]
NOBLOCK = "noblock"
def handle(name, cfg, _cloud, log, args):
if len(args) != 0:
resize_root = args[0]
else:
resize_root = util.get_cfg_option_str(cfg, "resize_rootfs", True)
if not util.translate_bool(resize_root, addons=[NOBLOCK]):
log.debug("Skipping module named %s, resizing disabled", name)
return
# TODO(harlowja) is the directory ok to be used??
resize_root_d = util.get_cfg_option_str(cfg, "resize_rootfs_tmp", "/run")
util.ensure_dir(resize_root_d)
# TODO(harlowja): allow what is to be resized to be configurable??
resize_what = "/"
result = util.get_mount_info(resize_what, log)
if not result:
log.warn("Could not determine filesystem type of %s", resize_what)
return
(devpth, fs_type, mount_point) = result
# Ensure the path is a block device.
info = "dev=%s mnt_point=%s path=%s" % (devpth, mount_point, resize_what)
log.debug("resize_info: %s" % info)
try:
statret = os.stat(devpth)
except OSError as exc:
if util.is_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:
log.warn("Device '%s' did not exist. cannot resize: %s" %
(devpth, info))
else:
raise exc
return
if not stat.S_ISBLK(statret.st_mode):
if util.is_container():
log.debug("device '%s' not a block device in container."
" cannot resize: %s" % (devpth, info))
else:
log.warn("device '%s' not a block device. cannot resize: %s" %
(devpth, info))
return
resizer = None
fstype_lc = fs_type.lower()
for (pfix, root_cmd) in RESIZE_FS_PREFIXES_CMDS:
if fstype_lc.startswith(pfix):
resizer = root_cmd
break
if not resizer:
log.warn("Not resizing unknown filesystem type %s for %s",
fs_type, resize_what)
return
resize_cmd = resizer(resize_what, devpth)
log.debug("Resizing %s (%s) using %s", resize_what, fs_type,
' '.join(resize_cmd))
if resize_root == NOBLOCK:
# Fork to a child that will run
# the resize command
util.fork_cb(
util.log_time(logfunc=log.debug, msg="backgrounded Resizing",
func=do_resize, args=(resize_cmd, log)))
else:
util.log_time(logfunc=log.debug, msg="Resizing",
func=do_resize, args=(resize_cmd, log))
action = 'Resized'
if resize_root == NOBLOCK:
action = 'Resizing (via forking)'
log.debug("%s root filesystem (type=%s, val=%s)", action, fs_type,
resize_root)
def do_resize(resize_cmd, log):
try:
util.subp(resize_cmd)
except util.ProcessExecutionError:
util.logexc(log, "Failed to resize filesystem (cmd=%s)", resize_cmd)
raise
# TODO(harlowja): Should we add a fsck check after this to make
# sure we didn't corrupt anything?