summaryrefslogtreecommitdiff
path: root/heat/cloudinit/loguserdata.py
blob: e02a59baeb420af8714d7128e6cb22e18b9da64b (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
#!/bin/bash
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
"true" '''\'
# NOTE(vgridnev): ubuntu trusty by default has python3,
# but pkg_resources can't be imported.
echo "import pkg_resources" | python3 2>/dev/null
has_py3=$?
if [ $has_py3 = 0 ]; then
    interpreter="python3"
else
    interpreter="python"
fi
exec $interpreter "$0"
'''

import datetime
from distutils import version
import errno
import logging
import os
import re
import subprocess
import sys

import pkg_resources


VAR_PATH = '/var/lib/heat-cfntools'
LOG = logging.getLogger('heat-provision')


def chk_ci_version():
    try:
        v = version.LooseVersion(
            pkg_resources.get_distribution('cloud-init').version)
        return v >= version.LooseVersion('0.6.0')
    except Exception:
        pass
    data = subprocess.Popen(['cloud-init', '--version'],
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE).communicate()
    if data[0]:
        raise Exception()
    # data[1] has such format: 'cloud-init 0.7.5\n', need to parse version
    v = re.split(' |\n', data[1])[1].split('.')
    return tuple(v) >= tuple(['0', '6', '0'])


def init_logging():
    LOG.setLevel(logging.INFO)
    LOG.addHandler(logging.StreamHandler())
    fh = logging.FileHandler("/var/log/heat-provision.log")
    os.chmod(fh.baseFilename, int("600", 8))
    LOG.addHandler(fh)


def call(args):

    class LogStream(object):

        def write(self, data):
            LOG.info(data)

    LOG.info('%s\n', ' '.join(args))  # noqa
    try:
        ls = LogStream()
        p = subprocess.Popen(args, stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        data = p.communicate()
        if data:
            for x in data:
                ls.write(x)
    except OSError:
        ex_type, ex, tb = sys.exc_info()
        if ex.errno == errno.ENOEXEC:
            LOG.error('Userdata empty or not executable: %s', ex)
            return os.EX_OK
        else:
            LOG.error('OS error running userdata: %s', ex)
            return os.EX_OSERR
    except Exception:
        ex_type, ex, tb = sys.exc_info()
        LOG.error('Unknown error running userdata: %s', ex)
        return os.EX_SOFTWARE
    return p.returncode


def main():

    try:
        if not chk_ci_version():
            # pre 0.6.0 - user data executed via cloudinit, not this helper
            LOG.error('Unable to log provisioning, need a newer version of '
                      'cloud-init')
            return -1
    except Exception:
        LOG.warning('Can not determine the version of cloud-init. It is '
                    'possible to get errors while logging provisioning.')

    userdata_path = os.path.join(VAR_PATH, 'cfn-userdata')
    os.chmod(userdata_path, int("700", 8))

    LOG.info('Provision began: %s', datetime.datetime.now())
    returncode = call([userdata_path])
    LOG.info('Provision done: %s', datetime.datetime.now())
    if returncode:
        return returncode


if __name__ == '__main__':
    init_logging()

    code = main()
    if code:
        LOG.error('Provision failed with exit code %s', code)
        sys.exit(code)

    provision_log = os.path.join(VAR_PATH, 'provision-finished')
    # touch the file so it is timestamped with when finished
    with open(provision_log, 'a'):
        os.utime(provision_log, None)