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 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
#!/usr/bin/env python
# Copyright 2013 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# 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.
import argparse
import fcntl
import logging
import os
import signal
import subprocess
import sys
import time
import psutil
OLD_BASE_DIR = '/opt/stack/os-config-refresh'
DEFAULT_BASE_DIR = '/usr/libexec/os-refresh-config'
def default_base_dir():
"""Determine the default base directory path
If the OS_REFRESH_CONFIG_BASE_DIR environment variable is set,
use its value.
Otherwise, prefer the new default path, but still allow the old one for
backwards compatibility.
"""
base_dir = os.environ.get('OS_REFRESH_CONFIG_BASE_DIR')
if base_dir is None:
# NOTE(bnemec): Prefer the new location, but still allow the old one.
if os.path.isdir(OLD_BASE_DIR) and not os.path.isdir(DEFAULT_BASE_DIR):
logging.warning('Base directory %s is deprecated. The recommended '
'base directory is %s',
OLD_BASE_DIR, DEFAULT_BASE_DIR)
base_dir = OLD_BASE_DIR
else:
base_dir = DEFAULT_BASE_DIR
return base_dir
BASE_DIR = default_base_dir()
PHASES = ['pre-configure',
'configure',
'post-configure',
'migration']
def timeout():
p = psutil.Process()
try:
# psutils version >= 2
children = list(p.children(recursive=True))
except AttributeError:
children = list(p.get_children(recursive=True))
for child in children:
child.kill()
def exit(lock, statuscode=0):
signal.alarm(0)
if lock:
lock.truncate(0)
lock.close()
return statuscode
def main(argv=sys.argv):
parser = argparse.ArgumentParser(
description="""Runs through all of the phases to ensure
configuration is applied and enabled on a machine. Will exit with
an error if any phase has a problem. Scripts should not depend on
eachother having worked properly. Set OS_REFRESH_CONFIG_BASE_DIR
environment variable to override the default
""")
parser.add_argument('--print-base', default=False, action='store_true',
help='Print base dir and exit')
parser.add_argument('--print-phases', default=False, action='store_true',
help='Print phases (tab separated) and exit')
parser.add_argument('--log-level', default='INFO',
choices=['ERROR', 'WARN', 'CRITICAL', 'INFO', 'DEBUG'])
parser.add_argument('--lockfile',
default='/var/run/os-refresh-config.lock',
help='Lock file to prevent multiple running copies.')
parser.add_argument('--timeout',
type=int,
help='Seconds until the current run will be '
'terminated.')
options = parser.parse_args(argv[1:])
if options.print_base:
print(BASE_DIR)
return 0
if options.print_phases:
print("\t".join(PHASES))
return 0
log = logging.getLogger('os-refresh-config')
handler = logging.StreamHandler(sys.stderr)
handler.setFormatter(
logging.Formatter(
'[%(asctime)s] (%(name)s) [%(levelname)s] %(message)s'))
log.addHandler(handler)
log.setLevel(options.log_level)
# Keep open (and thus, locked) for duration of program
lock = open(options.lockfile, 'a')
try:
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError as e:
log.error('Could not lock %s. %s' % (options.lockfile, e))
return e.errno
lock.truncate(0)
lock.write("Locked by pid==%d at %s\n" % (os.getpid(), time.localtime()))
def timeout_handler(signum, frame):
log.error('Timeout reached: %ss. Sending SIGKILL to all children' %
options.timeout)
timeout()
if options.timeout:
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(options.timeout)
for phase in PHASES:
phase_dir = os.path.join(BASE_DIR, '%s.d' % phase)
log.debug('Checking %s' % phase_dir)
if os.path.exists(phase_dir):
args = ['dib-run-parts']
args.append(phase_dir)
try:
log.info('Starting phase %s' % phase)
log.debug('Running %s' % args)
subprocess.check_call(args, close_fds=True)
sys.stdout.flush()
sys.stderr.flush()
log.info('Completed phase %s' % phase)
except subprocess.CalledProcessError as e:
log.error("during %s phase. [%s]\n" % (phase, e))
error_dir = os.path.join(BASE_DIR, 'error.d')
if os.path.exists(error_dir):
log.info('Calling error handlers.')
try:
subprocess.call(['dib-run-parts', error_dir])
except OSError:
pass
log.error("Aborting...")
return exit(lock, 1)
else:
log.debug('No dir for phase %s' % phase)
return exit(lock)
if __name__ == '__main__':
sys.exit(main(sys.argv))
|