From 0a44214233860122bbb91edc8d91a7f33a94d859 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Thu, 31 Jul 2014 13:41:22 -0400 Subject: initial commit oif iniit_switch --- cloudinit/config/cc_ubuntu_init_switch.py | 120 ++++++++++++++++++++++++++++++ config/cloud.cfg | 1 + 2 files changed, 121 insertions(+) create mode 100644 cloudinit/config/cc_ubuntu_init_switch.py diff --git a/cloudinit/config/cc_ubuntu_init_switch.py b/cloudinit/config/cc_ubuntu_init_switch.py new file mode 100644 index 00000000..3a2015e5 --- /dev/null +++ b/cloudinit/config/cc_ubuntu_init_switch.py @@ -0,0 +1,120 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2014 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 log as logging +from cloudinit import util +from cloudinit.distros import ubuntu + +import os +import time + +frequency = PER_INSTANCE +REBOOT_CMD = ["/sbin/reboot"] + +DEFAULT_CONFIG = { + 'init_switch': {'target': None} +} + +SWITCH_INIT = """ +#!/bin/sh +# switch_init: [upstart | systemd] + +is_systemd() { + [ "$(dpkg-divert --listpackage /sbin/init)" = "systemd-sysv" ] +} +debug() { echo "$@" 1>&2; } +fail() { echo "$@" 1>&2; exit 1; } + +if [ "$1" = "systemd" ]; then + if is_systemd; then + debug "already systemd, nothing to do" + else + [ -f /lib/systemd/systemd ] || fail "no systemd available"; + dpkg-divert --package systemd-sysv --divert /sbin/init.diverted \\ + --rename /sbin/init + fi + [ -f /sbin/init ] || ln /lib/systemd/systemd /sbin/init +elif [ "$1" = "upstart" ]; then + if is_systemd; then + rm -f /sbin/init + dpkg-divert --package systemd-sysv --rename --remove /sbin/init + else + debug "already upstart, nothing to do." + fi +else + fail "Error. expect 'upstart' or 'systemd'" +fi +""" + + +def handle(name, cfg, cloud, log, _args): + + if not isinstance(cloud.distro, ubuntu.Distro): + log.debug("%s: distro is '%s', not ubuntu. returning", + name, cloud.distro.__class__) + return + + cfg = util.mergemanydict(cfg, DEFAULT_CONFIG) + target = cfg['init_switch']['target'] + if not target: + log.debug("%s: target=%s. nothing to do", name, target) + return + + supported = ('upstart', 'systemd') + if target not in supported: + log.warn("%s: target set to %s, expected one of: %s", + name, target, str(supported)) + + if os.path.exists("/run/systemd/systemd"): + current = "systemd" + else: + current = "upstart" + + if current == target: + log.debug("%s: current = target = %s. nothing to do", name, target) + return + + try: + util.subp(['sh', '-c', SWITCH_INIT, '--', target]) + except util.ProcessExecutionError as e: + log.warn("%s: Failed to switch to init '%s'. %s", name, target, e) + return + + try: + log.warn("%s: rebooting from '%s' to '%s'", name, current, target) + logging.flushLoggers(log) + _fire_reboot(log, initial_sleep=4) + except Exception as e: + util.logexc(log, "Requested reboot did not happen!") + raise + + +def _fire_reboot(log, wait_attempts=6, initial_sleep=1, backoff=2): + util.subp(REBOOT_CMD) + start = time.time() + wait_time = initial_sleep + for _i in range(0, wait_attempts): + time.sleep(wait_time) + wait_time *= backoff + elapsed = time.time() - start + log.debug("Rebooted, but still running after %s seconds", int(elapsed)) + # If we got here, not good + elapsed = time.time() - start + raise RuntimeError(("Reboot did not happen" + " after %s seconds!") % (int(elapsed))) diff --git a/config/cloud.cfg b/config/cloud.cfg index b746e3db..200050d3 100644 --- a/config/cloud.cfg +++ b/config/cloud.cfg @@ -24,6 +24,7 @@ preserve_hostname: false # The modules that run in the 'init' stage cloud_init_modules: - migrator + - ubuntu-init-switch - seed_random - bootcmd - write-files -- cgit v1.2.3 From 9aafd6e78bb30c160d9cce5835314b67e86d0328 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Thu, 31 Jul 2014 13:59:12 -0400 Subject: fix for config, allow specifying target for cloud-init --single --- cloudinit/config/cc_ubuntu_init_switch.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cloudinit/config/cc_ubuntu_init_switch.py b/cloudinit/config/cc_ubuntu_init_switch.py index 3a2015e5..c476c51c 100644 --- a/cloudinit/config/cc_ubuntu_init_switch.py +++ b/cloudinit/config/cc_ubuntu_init_switch.py @@ -63,15 +63,19 @@ fi """ -def handle(name, cfg, cloud, log, _args): +def handle(name, cfg, cloud, log, args): if not isinstance(cloud.distro, ubuntu.Distro): log.debug("%s: distro is '%s', not ubuntu. returning", name, cloud.distro.__class__) return - cfg = util.mergemanydict(cfg, DEFAULT_CONFIG) + cfg = util.mergemanydict([cfg, DEFAULT_CONFIG]) target = cfg['init_switch']['target'] + + if len(args) != 0: + target = args[0] + if not target: log.debug("%s: target=%s. nothing to do", name, target) return -- cgit v1.2.3 From 5f59179ac0a281423701d36f05b2e42258f75248 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Thu, 31 Jul 2014 14:04:42 -0400 Subject: fix path to systemd --- cloudinit/config/cc_ubuntu_init_switch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinit/config/cc_ubuntu_init_switch.py b/cloudinit/config/cc_ubuntu_init_switch.py index c476c51c..3ab23a23 100644 --- a/cloudinit/config/cc_ubuntu_init_switch.py +++ b/cloudinit/config/cc_ubuntu_init_switch.py @@ -85,7 +85,7 @@ def handle(name, cfg, cloud, log, args): log.warn("%s: target set to %s, expected one of: %s", name, target, str(supported)) - if os.path.exists("/run/systemd/systemd"): + if os.path.exists("/run/systemd"): current = "systemd" else: current = "upstart" -- cgit v1.2.3 From de57d05b5039d13c3dda5fb0382287b847517954 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Thu, 31 Jul 2014 14:13:37 -0400 Subject: add some doc to module --- cloudinit/config/cc_ubuntu_init_switch.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/cloudinit/config/cc_ubuntu_init_switch.py b/cloudinit/config/cc_ubuntu_init_switch.py index 3ab23a23..faa41ede 100644 --- a/cloudinit/config/cc_ubuntu_init_switch.py +++ b/cloudinit/config/cc_ubuntu_init_switch.py @@ -16,6 +16,28 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +""" +ubuntu_init_switch: reboot system into another init + +This provides a way for the user to boot with systemd even if the +image is set to boot with upstart. It should be run as one of the first +cloud_init_modules, and will switch the init system and then issue a reboot. +The next boot will come up in the target init system and no action will +be taken. + +This should be inert on non-ubuntu systems, and also exit quickly. + +config is comes under the top level 'init_switch' dictionary. + +#cloud-config +init_switch: + target: systemd + +'target' can be 'systemd' or 'upstart'. Best effort is made, but its possible +this system will break, and probably won't interact well with any other +mechanism you've used to switch the init system. +""" + from cloudinit.settings import PER_INSTANCE from cloudinit import log as logging from cloudinit import util -- cgit v1.2.3 From eb6f294ff89696651b6ba4bda97f05ed6e0dfcf0 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Thu, 31 Jul 2014 14:18:55 -0400 Subject: exit and warn if no 'dpkg' (probably wrong distro) --- cloudinit/config/cc_ubuntu_init_switch.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cloudinit/config/cc_ubuntu_init_switch.py b/cloudinit/config/cc_ubuntu_init_switch.py index faa41ede..485c46a4 100644 --- a/cloudinit/config/cc_ubuntu_init_switch.py +++ b/cloudinit/config/cc_ubuntu_init_switch.py @@ -102,6 +102,10 @@ def handle(name, cfg, cloud, log, args): log.debug("%s: target=%s. nothing to do", name, target) return + if not util.which('dpkg'): + log.warn("%s: 'dpkg' not available. Assuming not ubuntu", name) + return + supported = ('upstart', 'systemd') if target not in supported: log.warn("%s: target set to %s, expected one of: %s", -- cgit v1.2.3