summaryrefslogtreecommitdiff
path: root/cloudinit/sources/helpers/vmware/imc/config_custom_script.py
blob: a7d4ad9137b9cea5deb80d4f0418bff9ad7bfcee (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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# Copyright (C) 2017 Canonical Ltd.
# Copyright (C) 2017 VMware Inc.
#
# Author: Maitreyee Saikia <msaikia@vmware.com>
#
# This file is part of cloud-init. See LICENSE file for license information.

import logging
import os
import stat
from textwrap import dedent

from cloudinit import util

LOG = logging.getLogger(__name__)


class CustomScriptNotFound(Exception):
    pass


class CustomScriptConstant(object):
    RC_LOCAL = "/etc/rc.local"
    POST_CUST_TMP_DIR = "/root/.customization"
    POST_CUST_RUN_SCRIPT_NAME = "post-customize-guest.sh"
    POST_CUST_RUN_SCRIPT = os.path.join(POST_CUST_TMP_DIR,
                                        POST_CUST_RUN_SCRIPT_NAME)
    POST_REBOOT_PENDING_MARKER = "/.guest-customization-post-reboot-pending"


class RunCustomScript(object):
    def __init__(self, scriptname, directory):
        self.scriptname = scriptname
        self.directory = directory
        self.scriptpath = os.path.join(directory, scriptname)

    def prepare_script(self):
        if not os.path.exists(self.scriptpath):
            raise CustomScriptNotFound("Script %s not found!! "
                                       "Cannot execute custom script!"
                                       % self.scriptpath)
        # Strip any CR characters from the decoded script
        util.load_file(self.scriptpath).replace("\r", "")
        st = os.stat(self.scriptpath)
        os.chmod(self.scriptpath, st.st_mode | stat.S_IEXEC)


class PreCustomScript(RunCustomScript):
    def execute(self):
        """Executing custom script with precustomization argument."""
        LOG.debug("Executing pre-customization script")
        self.prepare_script()
        util.subp(["/bin/sh", self.scriptpath, "precustomization"])


class PostCustomScript(RunCustomScript):
    def __init__(self, scriptname, directory):
        super(PostCustomScript, self).__init__(scriptname, directory)
        # Determine when to run custom script. When postreboot is True,
        # the user uploaded script will run as part of rc.local after
        # the machine reboots. This is determined by presence of rclocal.
        # When postreboot is False, script will run as part of cloud-init.
        self.postreboot = False

    def _install_post_reboot_agent(self, rclocal):
        """
        Install post-reboot agent for running custom script after reboot.
        As part of this process, we are editing the rclocal file to run a
        VMware script, which in turn is resposible for handling the user
        script.
        @param: path to rc local.
        """
        LOG.debug("Installing post-reboot customization from %s to %s",
                  self.directory, rclocal)
        if not self.has_previous_agent(rclocal):
            LOG.info("Adding post-reboot customization agent to rc.local")
            new_content = dedent("""
                # Run post-reboot guest customization
                /bin/sh %s
                exit 0
                """) % CustomScriptConstant.POST_CUST_RUN_SCRIPT
            existing_rclocal = util.load_file(rclocal).replace('exit 0\n', '')
            st = os.stat(rclocal)
            # "x" flag should be set
            mode = st.st_mode | stat.S_IEXEC
            util.write_file(rclocal, existing_rclocal + new_content, mode)

        else:
            # We don't need to update rclocal file everytime a customization
            # is requested. It just needs to be done for the first time.
            LOG.info("Post-reboot guest customization agent is already "
                     "registered in rc.local")
        LOG.debug("Installing post-reboot customization agent finished: %s",
                  self.postreboot)

    def has_previous_agent(self, rclocal):
        searchstring = "# Run post-reboot guest customization"
        if searchstring in open(rclocal).read():
            return True
        return False

    def find_rc_local(self):
        """
        Determine if rc local is present.
        """
        rclocal = ""
        if os.path.exists(CustomScriptConstant.RC_LOCAL):
            LOG.debug("rc.local detected.")
            # resolving in case of symlink
            rclocal = os.path.realpath(CustomScriptConstant.RC_LOCAL)
            LOG.debug("rc.local resolved to %s", rclocal)
        else:
            LOG.warning("Can't find rc.local, post-customization "
                        "will be run before reboot")
        return rclocal

    def install_agent(self):
        rclocal = self.find_rc_local()
        if rclocal:
            self._install_post_reboot_agent(rclocal)
            self.postreboot = True

    def execute(self):
        """
        This method executes post-customization script before or after reboot
        based on the presence of rc local.
        """
        self.prepare_script()
        self.install_agent()
        if not self.postreboot:
            LOG.warning("Executing post-customization script inline")
            util.subp(["/bin/sh", self.scriptpath, "postcustomization"])
        else:
            LOG.debug("Scheduling custom script to run post reboot")
            if not os.path.isdir(CustomScriptConstant.POST_CUST_TMP_DIR):
                os.mkdir(CustomScriptConstant.POST_CUST_TMP_DIR)
            # Script "post-customize-guest.sh" and user uploaded script are
            # are present in the same directory and needs to copied to a temp
            # directory to be executed post reboot. User uploaded script is
            # saved as customize.sh in the temp directory.
            # post-customize-guest.sh excutes customize.sh after reboot.
            LOG.debug("Copying post-customization script")
            util.copy(self.scriptpath,
                      CustomScriptConstant.POST_CUST_TMP_DIR + "/customize.sh")
            LOG.debug("Copying script to run post-customization script")
            util.copy(
                os.path.join(self.directory,
                             CustomScriptConstant.POST_CUST_RUN_SCRIPT_NAME),
                CustomScriptConstant.POST_CUST_RUN_SCRIPT)
            LOG.info("Creating post-reboot pending marker")
            util.ensure_file(CustomScriptConstant.POST_REBOOT_PENDING_MARKER)

# vi: ts=4 expandtab