From 71928e8c70900843dce4aa3ae84fcd278e4b887a Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Tue, 13 Nov 2012 08:57:57 -0500 Subject: rename module 'finalcmd' to power-state-change --- cloudinit/config/cc_finalcmd.py | 139 ------------------------------ cloudinit/config/cc_power_state_change.py | 139 ++++++++++++++++++++++++++++++ config/cloud.cfg | 2 +- 3 files changed, 140 insertions(+), 140 deletions(-) delete mode 100644 cloudinit/config/cc_finalcmd.py create mode 100644 cloudinit/config/cc_power_state_change.py diff --git a/cloudinit/config/cc_finalcmd.py b/cloudinit/config/cc_finalcmd.py deleted file mode 100644 index d921a444..00000000 --- a/cloudinit/config/cc_finalcmd.py +++ /dev/null @@ -1,139 +0,0 @@ -# vi: ts=4 expandtab -# -# Copyright (C) 2011 Canonical Ltd. -# -# Author: Scott Moser -# -# 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 . - -from cloudinit.settings import PER_INSTANCE -from cloudinit import util - -import errno -import os -import subprocess -import sys -import time - -frequency = PER_INSTANCE - - -def handle(_name, cfg, _cloud, log, _args): - - finalcmds = cfg.get("finalcmd") - - if not finalcmds: - log.debug("No final commands") - return - - mypid = os.getpid() - cmdline = util.load_file("/proc/%s/cmdline") - - if not cmdline: - log.warn("Failed to get cmdline of current process") - return - - try: - timeout = float(cfg.get("finalcmd_timeout", 30.0)) - except ValueError: - log.warn("failed to convert finalcmd_timeout '%s' to float" % - cfg.get("finalcmd_timeout", 30.0)) - return - - devnull_fp = open("/dev/null", "w") - - shellcode = util.shellify(finalcmds) - - # note, after the fork, we do not use any of cloud-init's functions - # that would attempt to log. The primary reason for that is - # to allow the 'finalcmd' the ability to do just about anything - # and not depend on syslog services. - # Basically, it should "just work" to have finalcmd of: - # - sleep 30 - # - /sbin/poweroff - finalcmd_d = os.path.join(cloud.get_ipath_cur(), "finalcmds") - - util.fork_cb(run_after_pid_gone, mypid, cmdline, timeout, - runfinal, (shellcode, finalcmd_d, devnull_fp)) - - -def execmd(exe_args, data_in=None, output=None): - try: - proc = subprocess.Popen(exe_args, stdin=subprocess.PIPE, - stdout=output, stderr=subprocess.STDERR) - proc.communicate(data_in) - except Exception as e: - return 254 - return proc.returncode() - - -def runfinal(shellcode, finalcmd_d, output=None): - ret = execmd(["/bin/sh",], data_in=shellcode, output=output) - if not (finalcmd_d and os.path.isdir(finalcmd_d)): - sys.exit(ret) - - fails = 0 - if ret != 0: - fails = 1 - - # now runparts the final command dir - for exe_name in sorted(os.listdir(finalcmd_d)): - exe_path = os.path.join(finalcmd_d, exe_name) - if os.path.isfile(exe_path) and os.access(exe_path, os.X_OK): - ret = execmd([exe_path], data_in=None, output=output) - if ret != 0: - fails += 1 - sys.exit(fails) - - -def run_after_pid_gone(pid, pidcmdline, timeout, func, args): - # wait until pid, with /proc/pid/cmdline contents of pidcmdline - # is no longer alive. After it is gone, or timeout has passed - # execute func(args) - msg = "ERROR: Uncaught error" - end_time = time.time() + timeout - - cmdline_f = "/proc/%s/cmdline" % pid - - while True: - if time.time() > end_time: - msg = "timeout reached before %s ended" % pid - break - - try: - cmdline = "" - with open(cmdline_f) as fp: - cmdline = fp.read() - if cmdline != pidcmdline: - msg = "cmdline changed for %s [now: %s]" % (pid, cmdline) - break - - except IOError as ioerr: - if ioerr.errno == errno.ENOENT: - msg = "pidfile '%s' gone" % cmdline_f - else: - msg = "ERROR: IOError: %s" % ioerr - raise - break - - except Exception as e: - msg = "ERROR: Exception: %s" % e - raise - - if msg.startswith("ERROR:"): - sys.stderr.write(msg) - sys.stderr.write("Not executing finalcmd") - sys.exit(1) - - sys.stderr.write("calling %s with %s\n" % (func, args)) - sys.exit(func(*args)) diff --git a/cloudinit/config/cc_power_state_change.py b/cloudinit/config/cc_power_state_change.py new file mode 100644 index 00000000..d921a444 --- /dev/null +++ b/cloudinit/config/cc_power_state_change.py @@ -0,0 +1,139 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2011 Canonical Ltd. +# +# Author: Scott Moser +# +# 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 . + +from cloudinit.settings import PER_INSTANCE +from cloudinit import util + +import errno +import os +import subprocess +import sys +import time + +frequency = PER_INSTANCE + + +def handle(_name, cfg, _cloud, log, _args): + + finalcmds = cfg.get("finalcmd") + + if not finalcmds: + log.debug("No final commands") + return + + mypid = os.getpid() + cmdline = util.load_file("/proc/%s/cmdline") + + if not cmdline: + log.warn("Failed to get cmdline of current process") + return + + try: + timeout = float(cfg.get("finalcmd_timeout", 30.0)) + except ValueError: + log.warn("failed to convert finalcmd_timeout '%s' to float" % + cfg.get("finalcmd_timeout", 30.0)) + return + + devnull_fp = open("/dev/null", "w") + + shellcode = util.shellify(finalcmds) + + # note, after the fork, we do not use any of cloud-init's functions + # that would attempt to log. The primary reason for that is + # to allow the 'finalcmd' the ability to do just about anything + # and not depend on syslog services. + # Basically, it should "just work" to have finalcmd of: + # - sleep 30 + # - /sbin/poweroff + finalcmd_d = os.path.join(cloud.get_ipath_cur(), "finalcmds") + + util.fork_cb(run_after_pid_gone, mypid, cmdline, timeout, + runfinal, (shellcode, finalcmd_d, devnull_fp)) + + +def execmd(exe_args, data_in=None, output=None): + try: + proc = subprocess.Popen(exe_args, stdin=subprocess.PIPE, + stdout=output, stderr=subprocess.STDERR) + proc.communicate(data_in) + except Exception as e: + return 254 + return proc.returncode() + + +def runfinal(shellcode, finalcmd_d, output=None): + ret = execmd(["/bin/sh",], data_in=shellcode, output=output) + if not (finalcmd_d and os.path.isdir(finalcmd_d)): + sys.exit(ret) + + fails = 0 + if ret != 0: + fails = 1 + + # now runparts the final command dir + for exe_name in sorted(os.listdir(finalcmd_d)): + exe_path = os.path.join(finalcmd_d, exe_name) + if os.path.isfile(exe_path) and os.access(exe_path, os.X_OK): + ret = execmd([exe_path], data_in=None, output=output) + if ret != 0: + fails += 1 + sys.exit(fails) + + +def run_after_pid_gone(pid, pidcmdline, timeout, func, args): + # wait until pid, with /proc/pid/cmdline contents of pidcmdline + # is no longer alive. After it is gone, or timeout has passed + # execute func(args) + msg = "ERROR: Uncaught error" + end_time = time.time() + timeout + + cmdline_f = "/proc/%s/cmdline" % pid + + while True: + if time.time() > end_time: + msg = "timeout reached before %s ended" % pid + break + + try: + cmdline = "" + with open(cmdline_f) as fp: + cmdline = fp.read() + if cmdline != pidcmdline: + msg = "cmdline changed for %s [now: %s]" % (pid, cmdline) + break + + except IOError as ioerr: + if ioerr.errno == errno.ENOENT: + msg = "pidfile '%s' gone" % cmdline_f + else: + msg = "ERROR: IOError: %s" % ioerr + raise + break + + except Exception as e: + msg = "ERROR: Exception: %s" % e + raise + + if msg.startswith("ERROR:"): + sys.stderr.write(msg) + sys.stderr.write("Not executing finalcmd") + sys.exit(1) + + sys.stderr.write("calling %s with %s\n" % (func, args)) + sys.exit(func(*args)) diff --git a/config/cloud.cfg b/config/cloud.cfg index 249a593d..c1d8ea0d 100644 --- a/config/cloud.cfg +++ b/config/cloud.cfg @@ -69,7 +69,7 @@ cloud_final_modules: - keys-to-console - phone-home - final-message - - finalcmd + - power-state-change # System and/or distro specific settings # (not accessible to handlers/transforms) -- cgit v1.2.3