heat/heat/cloudinit/loguserdata.py
Vitaly Gridnev f95a8051ca Validate that python3 is ready for loguserdata
By default, ubuntu trusty images has python3 executable in
path, but we can't use that for executing loguserdata script,
because pkg_resources can't be imported. Now it's proposed to
try importing pkg_resources for validating readiness of
python3 for executing this script. If pkg_resources can't be
imported there is no other choice except using python2.

Change-Id: Icb4f58630016874eb40dd77590469fc5de6287e4
Closes-bug: 1617069
2016-08-26 11:49:30 +00:00

133 lines
3.8 KiB
Python
Executable File

#!/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)