openstack-ansible/tools/install.py
2014-08-26 18:08:15 -05:00

302 lines
9.8 KiB
Python
Executable File

#!/usr/bin/env python
# Copyright 2014, Rackspace US, Inc.
#
# 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.
# Python Standard Libraries
import os
import sys
import time
import os.path
import logging
import functools
import subprocess
import logging.handlers
# PIP Libraries
import click
import pexpect
# Configuration
playbook_cmd = "ansible-playbook %s -e @/etc/rpc_deploy/user_variables.yml %s"
restart_containers_sleep_seconds = 120
color_codes_to_strip_from_log = [
'\033[0m',
'\033[0;31m',
'\033[0;32m',
'\033[0;33m',
'\033[0;34m',
'\033[0;36m']
class LXCNotInstalledOrNotRunnableError(Exception):
def __init__(self, error, output):
self.error = error
self.output = output
def __str__(self):
return repr("%s: %s" % (self.error, self.output))
class TooManyPlaybookFailuresError(Exception):
def __init__(self, playbook, retries):
self.playbook = playbook
self.retries = retries
def __str__(self):
return repr([self.playbook, self.retries])
class ChildProcessReturnedNonZeroError(Exception):
def __init__(self, errorcode):
self.errorcode = errorcode
def __str__(self):
return repr(self.errorcode)
class StripColorCodesFormatter(logging.Formatter):
def format(self, record):
for code_to_strip in color_codes_to_strip_from_log:
record.msg = record.msg.replace(
code_to_strip, '')
return logging.Formatter.format(self, record)
def configure_logger(log_file):
logger = logging.getLogger('OpenStackInstaller')
log_formatter = StripColorCodesFormatter(
"%(name)s: %(asctime)s - %(levelname)s: %(message)s")
file_handler = logging.handlers.RotatingFileHandler(
log_file, maxBytes=104857600, backupCount=10)
file_handler.setFormatter(log_formatter)
file_handler.setLevel(logging.DEBUG)
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setFormatter(log_formatter)
stream_handler.setLevel(logging.INFO)
logger.setLevel(logging.DEBUG)
logger.addHandler(file_handler)
logger.addHandler(stream_handler)
return logger
def execute(command):
"""
Executes a command, polling for output and printing the output as it runs.
"""
child = pexpect.spawn(command)
while True:
try:
child.expect('\n', timeout=None)
click.echo(child.before)
LOG.debug(child.before)
except pexpect.EOF:
break
child.close()
if child.exitstatus != 0:
raise ChildProcessReturnedNonZeroError(child.exitstatus)
def scriptdir():
"""
Returns the directory where this script is located.
"""
return os.path.dirname(os.path.realpath(__file__))
def rundir():
"""
Returns the directory where our ansible playbooks should be executed from.
"""
return os.path.realpath(
os.path.join(scriptdir(), '../rpc_deployment'))
def get_container_name(containerstring):
"""
Returns the container name that contains containerstring. In the case
where there are multiple containers matching containerstring, returns only
the first one. Returns None for no match.
"""
try:
containers = subprocess.check_output(
'lxc-ls',
stderr=subprocess.STDOUT).split('\n')
except subprocess.CalledProcessError as e:
raise LXCNotInstalledOrNotRunnableError(e.returncode, e.output)
except OSError as e:
raise LXCNotInstalledOrNotRunnableError(e.errno, e.strerror)
for line in containers:
if containerstring in line:
return line
return None
def print_status(haproxy, galera, rabbit, ansible_rundir, retries):
click.echo("Running ansible-lxc-rpc installer/updater.")
click.echo("About to install openstack.")
click.echo("Using %s as our ansible run directory" % ansible_rundir)
click.echo("Trying %s retries with each playbook" % retries)
if haproxy:
click.echo("We will install haproxy. Do not use this in a "
"production environment.")
else:
click.echo("NOT installing haproxy. This means you must have a "
"loadbalancer in place, configured separately.")
if galera:
click.echo("We will install galera.")
else:
click.echo("NOT installing galera. This means that you must "
"separately install and configure the database.")
if rabbit:
click.echo('We will install RabbitMQ.')
else:
click.echo("NOT installing RabbitMQ. This means you are "
"reponsible for installing and configuring it.")
def run_restart_containers_playbook(retries, ansible_rundir, galera):
run_playbook('setup/restart-containers.yml', retries, ansible_rundir)
LOG.debug("Sleeping %s seconds" % restart_containers_sleep_seconds)
time.sleep(restart_containers_sleep_seconds)
if galera:
# Restarting containers does not always restart galera, especially
# if it's an AIO. The system startup scripts do not have the bootstrap
# necessary for a single-node cluster.
run_playbook(
'infrastructure/galera-startup.yml', retries, ansible_rundir)
def run_playbook(playbook, retries, ansible_rundir):
"""
Runs an individual playbook. Attempts multiple runs.
"""
extra_options = ""
success = False
attempt = 1
os.chdir(ansible_rundir)
playbook = os.path.join('playbooks', playbook)
os.environ["ANSIBLE_FORCE_COLOR"] = "true"
while not success:
playbook_full_cmd = playbook_cmd % (extra_options, playbook)
try:
LOG.info(
'About to execute attempt %s of command: "%s"' %
(attempt, playbook_full_cmd))
execute(playbook_full_cmd)
LOG.info('Successfully completed playbook "%s"' % playbook)
success = True
except ChildProcessReturnedNonZeroError:
attempt += 1
pb_name = playbook.split('/')[-1].split('.')[0]
extra_options = "-vvvv --limit @/%s/%s.retry" % (
os.path.expanduser("~"), pb_name)
os.environ["GIT_CURL_VERBOSE"] = "1"
if not success and attempt > retries:
raise TooManyPlaybookFailuresError(playbook, retries)
@click.command()
@click.option('--haproxy/--no-haproxy',
default=False,
help='Should we install Haproxy? Defaults to no.')
@click.option('--galera/--no-galera',
default=False,
help='Should we install Galera? Defaults to no.')
@click.option('--rabbit/--no-rabbit',
default=False,
help='Should we install RabbitMQ? Defaults to no.')
@click.option('--retries',
default=3,
help='Number of retries to attempt on an Ansible playbook '
'before giving up.')
def run_the_ansibles(haproxy, galera, rabbit, retries):
ansible_rundir = rundir()
print_status(haproxy, galera, rabbit, ansible_rundir, retries)
run_p = functools.partial(
run_playbook, retries=retries, ansible_rundir=ansible_rundir)
run_p('setup/host-setup.yml')
run_p('setup/build-containers.yml')
run_restart_containers_playbook(retries, ansible_rundir, galera=False)
run_p('setup/host-common.yml')
if galera:
galera_container = get_container_name('galera')
if galera_container is not None and os.path.isfile(
os.path.join('/openstack/', galera_container, 'galera.cache')):
run_p('infrastructure/galera-bootstrap.yml')
run_p('infrastructure/galera-config.yml')
run_p('infrastructure/galera-startup.yml')
else:
run_p('infrastructure/galera-install.yml')
run_p('infrastructure/galera-startup.yml')
run_p('infrastructure/memcached-install.yml')
if rabbit:
run_p('infrastructure/rabbit-install.yml')
run_p('infrastructure/rsyslog-install.yml')
run_p('infrastructure/elasticsearch-install.yml')
run_p('infrastructure/logstash-install.yml')
run_p('infrastructure/kibana-install.yml')
run_p('infrastructure/rsyslog-config.yml')
run_p('infrastructure/es2unix-install.yml')
if haproxy:
run_p('infrastructure/haproxy-install.yml')
run_restart_containers_playbook(retries, ansible_rundir, galera)
run_p('openstack/utility.yml')
run_p('openstack/openstack-common.yml')
run_p('openstack/keystone.yml')
run_p('openstack/keystone-add-all-services.yml')
run_p('openstack/keystone-add-users.yml')
run_p('openstack/glance-all.yml')
run_p('openstack/heat-all.yml')
run_p('openstack/nova-all.yml')
run_p('openstack/neutron-all.yml')
run_p('openstack/cinder-all.yml')
run_p('openstack/horizon.yml')
run_restart_containers_playbook(retries, ansible_rundir, galera)
LOG = configure_logger('/var/log/rpc-openstack-installer.log')
def main():
try:
run_the_ansibles()
except TooManyPlaybookFailuresError as e:
LOG.critical(
"Failed running playbook '%s' %s times. Aborting..." %
(e.playbook, e.retries))
sys.exit(50)
except LXCNotInstalledOrNotRunnableError as e:
LOG.critical(
'Failed running lxc-ls. '
'Got return code %s and output "%s"' % (e.error, e.output))
sys.exit(51)
if __name__ == '__main__':
main()