summaryrefslogtreecommitdiff
path: root/cloudinit/config/cc_ubuntu_init_switch.py
blob: 3a2015e57c0ae754b1ed24a8bc26abb30dd8ff81 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# vi: ts=4 expandtab
#
#    Copyright (C) 2014 Canonical Ltd.
#
#    Author: Scott Moser <scott.moser@canonical.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/>.

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)))