Add kayobe python module with CLI

The CLI replaces all existing shell scripts except for
configure-kayobe.sh. Other shell scripts are now removed.
This commit is contained in:
Mark Goddard 2017-02-28 16:06:36 +00:00
parent 724db9f600
commit caf9b52ac7
18 changed files with 661 additions and 317 deletions

View File

@ -1,55 +0,0 @@
#!/bin/bash
set -e
function run_playbook {
KAYOBE_CONFIG_PATH=${KAYOBE_CONFIG_PATH:-/etc/kayobe}
# Ansible fails silently if the inventory does not exist.
test -e ${KAYOBE_CONFIG_PATH}/inventory
ansible-playbook \
-i ${KAYOBE_CONFIG_PATH}/inventory \
-e @${KAYOBE_CONFIG_PATH}/globals.yml \
-e @${KAYOBE_CONFIG_PATH}/dns.yml \
-e @${KAYOBE_CONFIG_PATH}/kolla.yml \
-e @${KAYOBE_CONFIG_PATH}/networks.yml \
-e @${KAYOBE_CONFIG_PATH}/network-allocation.yml \
-e @${KAYOBE_CONFIG_PATH}/ntp.yml \
-e @${KAYOBE_CONFIG_PATH}/swift.yml \
$@
}
function install_ansible {
if [[ -f /etc/centos-release ]]; then
sudo yum -y install epel-release
elif [[ -f /etc/redhat-release ]]; then
sudo subscription-manager repos --enable=qci-1.0-for-rhel-7-rpms
if ! yum info epel-release >/dev/null 2>&1 ; then
sudo yum -y install \
https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
fi
fi
sudo yum -y install ansible
}
function install_ansible_roles {
ansible-galaxy install \
--roles-path ansible/roles \
--role-file ansible/requirements.yml
}
function bootstrap {
run_playbook ansible/bootstrap.yml
}
function install_kolla {
run_playbook ansible/kolla.yml
}
function main {
install_ansible
install_ansible_roles
bootstrap
install_kolla
}
main $*

View File

@ -1,71 +0,0 @@
#!/bin/bash
set -e
function run_playbook {
KAYOBE_CONFIG_PATH=${KAYOBE_CONFIG_PATH:-/etc/kayobe}
# Ansible fails silently if the inventory does not exist.
test -e ${KAYOBE_CONFIG_PATH}/inventory
ansible-playbook \
-i ${KAYOBE_CONFIG_PATH}/inventory \
-e @${KAYOBE_CONFIG_PATH}/controllers.yml \
-e @${KAYOBE_CONFIG_PATH}/dns.yml \
-e @${KAYOBE_CONFIG_PATH}/globals.yml \
-e @${KAYOBE_CONFIG_PATH}/kolla.yml \
-e @${KAYOBE_CONFIG_PATH}/networks.yml \
-e @${KAYOBE_CONFIG_PATH}/network-allocation.yml \
-e @${KAYOBE_CONFIG_PATH}/ntp.yml \
-e @${KAYOBE_CONFIG_PATH}/ssh.yml \
-e @${KAYOBE_CONFIG_PATH}/swift.yml \
$@
}
function run_kolla_ansible {
export KOLLA_CONFIG_PATH=${KOLLA_CONFIG_PATH:-/etc/kolla}
# Ansible fails silently if the inventory does not exist.
test -e ${KOLLA_CONFIG_PATH}/inventory/overcloud
KOLLA_VENV=$(pwd)/ansible/kolla-venv
source ${KOLLA_VENV}/bin/activate
kolla-ansible \
--configdir ${KOLLA_CONFIG_PATH} \
--passwords ${KOLLA_CONFIG_PATH}/passwords.yml \
-i ${KOLLA_CONFIG_PATH}/inventory/overcloud \
$@
deactivate
}
function configure_os {
ansible_user=$(./kayobe-config-dump -e dump_hosts=controllers[0] -e dump_var_name=kayobe_ansible_user | head -n -1)
run_playbook ansible/ip-allocation.yml -l controllers
run_playbook ansible/ssh-known-host.yml -l controllers
run_playbook ansible/kayobe-ansible-user.yml -l controllers
run_playbook ansible/disable-selinux.yml -l controllers
run_playbook ansible/network.yml -l controllers
run_playbook ansible/ntp.yml -l controllers
run_kolla_ansible bootstrap-servers -e ansible_user=${ansible_user}
run_playbook ansible/kolla-host.yml -l controllers
run_playbook ansible/docker.yml -l controllers
}
function deploy_services {
run_playbook ansible/kolla-openstack.yml
run_playbook ansible/swift-setup.yml
run_kolla_ansible pull
run_kolla_ansible prechecks
run_kolla_ansible deploy
run_kolla_ansible post-deploy -e node_config_directory=${KOLLA_CONFIG_PATH}
}
function deploy_overcloud {
configure_os
deploy_services
}
###########################################################
# Main
function main {
deploy_overcloud
}
deploy_overcloud

View File

@ -1,72 +0,0 @@
#!/bin/bash
set -e
function run_playbook {
KAYOBE_CONFIG_PATH=${KAYOBE_CONFIG_PATH:-/etc/kayobe}
# Ansible fails silently if the inventory does not exist.
test -e ${KAYOBE_CONFIG_PATH}/inventory
ansible-playbook \
-i ${KAYOBE_CONFIG_PATH}/inventory \
-e @${KAYOBE_CONFIG_PATH}/bifrost.yml \
-e @${KAYOBE_CONFIG_PATH}/dns.yml \
-e @${KAYOBE_CONFIG_PATH}/globals.yml \
-e @${KAYOBE_CONFIG_PATH}/kolla.yml \
-e @${KAYOBE_CONFIG_PATH}/networks.yml \
-e @${KAYOBE_CONFIG_PATH}/network-allocation.yml \
-e @${KAYOBE_CONFIG_PATH}/ntp.yml \
-e @${KAYOBE_CONFIG_PATH}/seed-vm.yml \
-e @${KAYOBE_CONFIG_PATH}/ssh.yml \
-e @${KAYOBE_CONFIG_PATH}/swift.yml \
$@
}
function run_kolla_ansible {
export KOLLA_CONFIG_PATH=${KOLLA_CONFIG_PATH:-/etc/kolla}
# Ansible fails silently if the inventory does not exist.
test -e ${KOLLA_CONFIG_PATH}/inventory/seed
KOLLA_VENV=$(pwd)/ansible/kolla-venv
source ${KOLLA_VENV}/bin/activate
kolla-ansible \
--configdir ${KOLLA_CONFIG_PATH} \
--passwords ${KOLLA_CONFIG_PATH}/passwords.yml \
-i ${KOLLA_CONFIG_PATH}/inventory/seed \
$@
deactivate
}
function configure_os {
ansible_user=$(./kayobe-config-dump -e dump_hosts=seed -e dump_var_name=kayobe_ansible_user | head -n -1)
run_playbook ansible/ip-allocation.yml -l seed
run_playbook ansible/ssh-known-host.yml -l seed
run_playbook ansible/kayobe-ansible-user.yml -l seed
run_playbook ansible/disable-selinux.yml -l seed
run_playbook ansible/network.yml -l seed
run_playbook ansible/ntp.yml -l seed
run_kolla_ansible bootstrap-servers -e ansible_user=${ansible_user}
run_playbook ansible/kolla-host.yml -l seed
run_playbook ansible/docker.yml -l seed
}
function deploy_bifrost {
# Use a pre-built bifrost image in the stackhpc repository.
# The image was built via kolla-build -t source bifrost-deploy.
run_playbook ansible/kolla-bifrost.yml
run_kolla_ansible deploy-bifrost \
-e kolla_install_type=source \
-e docker_namespace=stackhpc
}
function deploy_seed_node {
configure_os
deploy_bifrost
}
###########################################################
# Main
function main {
deploy_seed_node
}
main $*

View File

@ -1,18 +0,0 @@
#!/bin/bash
dump_dir=$(mktemp -d)
# Execute the dump-config.yml playbook.
./kayobe-playbook \
ansible/dump-config.yml \
-e dump_path=${dump_dir} \
$@ \
> /dev/null
result=$?
if [[ $result -eq 0 ]]; then
cat ${dump_dir}/*.yml
fi
rm -rf ${dump_dir}
exit $result

View File

@ -1,22 +0,0 @@
#!/bin/bash
KAYOBE_CONFIG_PATH=${KAYOBE_CONFIG_PATH:-/etc/kayobe}
# Ansible fails silently if the inventory does not exist.
test -e ${KAYOBE_CONFIG_PATH}/inventory
# Execute a Kayobe playbook.
exec ansible-playbook \
-i ${KAYOBE_CONFIG_PATH}/inventory \
-e @${KAYOBE_CONFIG_PATH}/bifrost.yml \
-e @${KAYOBE_CONFIG_PATH}/controllers.yml \
-e @${KAYOBE_CONFIG_PATH}/dns.yml \
-e @${KAYOBE_CONFIG_PATH}/globals.yml \
-e @${KAYOBE_CONFIG_PATH}/kolla.yml \
-e @${KAYOBE_CONFIG_PATH}/networks.yml \
-e @${KAYOBE_CONFIG_PATH}/network-allocation.yml \
-e @${KAYOBE_CONFIG_PATH}/ntp.yml \
-e @${KAYOBE_CONFIG_PATH}/seed-vm.yml \
-e @${KAYOBE_CONFIG_PATH}/ssh.yml \
-e @${KAYOBE_CONFIG_PATH}/swift.yml \
$@

0
kayobe/__init__.py Normal file
View File

173
kayobe/ansible.py Normal file
View File

@ -0,0 +1,173 @@
import logging
import os
import os.path
import shutil
import subprocess
import sys
import tempfile
from kayobe import utils
DEFAULT_CONFIG_PATH = "/etc/kayobe"
CONFIG_PATH_ENV = "KAYOBE_CONFIG_PATH"
LOG = logging.getLogger(__name__)
def galaxy_install(role_file, roles_path):
"""Install Ansible roles via Ansible Galaxy."""
cmd = ["ansible-galaxy", "install"]
cmd += ["--roles-path", roles_path]
cmd += ["--role-file", role_file]
try:
subprocess.check_call(cmd)
except subprocess.CalledProcessError as e:
LOG.error("Failed to install Ansible roles from %s via Ansible "
"Galaxy: returncode %d", role_file, e.returncode)
sys.exit(e.returncode)
def add_args(parser):
"""Add arguments required for running Ansible playbooks to a parser."""
default_config_path = os.getenv(CONFIG_PATH_ENV, DEFAULT_CONFIG_PATH)
parser.add_argument("-b", "--become", action="store_true",
help="run operations with become (nopasswd implied)")
parser.add_argument("-C", "--check", action="store_true",
help="don't make any changes; instead, try to predict "
"some of the changes that may occur")
parser.add_argument("--config-path", default=default_config_path,
help="path to Kayobe configuration. "
"(default=$%s or %s)" %
(CONFIG_PATH_ENV, DEFAULT_CONFIG_PATH))
parser.add_argument("-e", "--extra-vars", metavar="EXTRA_VARS",
action="append",
help="set additional variables as key=value or "
"YAML/JSON")
parser.add_argument("-i", "--inventory", metavar="INVENTORY",
help="specify inventory host path "
"(default=$%s/inventory or %s/inventory) or "
"comma-separated host list" %
(CONFIG_PATH_ENV, DEFAULT_CONFIG_PATH))
parser.add_argument("-l", "--limit", metavar="SUBSET",
help="further limit selected hosts to an additional "
"pattern")
parser.add_argument("-t", "--tags", metavar="TAGS", action="append",
help="only run plays and tasks tagged with these "
"values")
def _get_inventory_path(parsed_args):
"""Return the path to the Kayobe inventory."""
if parsed_args.inventory:
return parsed_args.inventory
else:
return os.path.join(parsed_args.config_path, "inventory")
def _validate_args(parsed_args, playbooks):
"""Validate Kayobe Ansible arguments."""
result = utils.is_readable_dir(parsed_args.config_path)
if not result["result"]:
LOG.error("Kayobe configuration path %s is invalid: %s",
parsed_args.config_path, result["message"])
sys.exit(1)
inventory = _get_inventory_path(parsed_args)
result = utils.is_readable_file(inventory)
if not result["result"]:
LOG.error("Kayobe inventory %s is invalid: %s",
inventory, result["message"])
sys.exit(1)
for playbook in playbooks:
result = utils.is_readable_file(playbook)
if not result["result"]:
LOG.error("Kayobe playbook %s is invalid: %s",
playbook, result["message"])
sys.exit(1)
def build_args(parsed_args, playbooks,
extra_vars=None, limit=None, tags=None):
"""Build arguments required for running Ansible playbooks."""
cmd = ["ansible-playbook"]
inventory = _get_inventory_path(parsed_args)
cmd += ["--inventory", inventory]
for vars_file in os.listdir(parsed_args.config_path):
abs_path = os.path.join(parsed_args.config_path, vars_file)
if os.path.isfile(abs_path):
root, ext = os.path.splitext(vars_file)
if ext in (".yml", ".yaml", ".json"):
cmd += ["-e", "@%s" % abs_path]
if parsed_args.extra_vars:
for extra_var in parsed_args.extra_vars:
cmd += ["-e", extra_var]
if extra_vars:
for extra_var_name, extra_var_value in extra_vars.items():
cmd += ["-e", "%s=%s" % (extra_var_name, extra_var_value)]
if parsed_args.become:
cmd += ["--become"]
if parsed_args.check:
cmd += ["--check"]
if parsed_args.limit or limit:
limits = [l for l in [parsed_args.limit, limit] if l]
cmd += ["--limit", "&".join(limits)]
if parsed_args.tags or tags:
all_tags = [t for t in [parsed_args.tags, tags] if t]
cmd += ["--tags", ",".join(all_tags)]
cmd += playbooks
return cmd
def run_playbooks(parsed_args, playbooks,
extra_vars=None, limit=None, tags=None, quiet=False):
"""Run a Kayobe Ansible playbook."""
_validate_args(parsed_args, playbooks)
cmd = build_args(parsed_args, playbooks,
extra_vars=extra_vars, limit=limit, tags=tags)
try:
utils.run_command(cmd, quiet=quiet)
except subprocess.CalledProcessError as e:
LOG.error("Kayobe playbook(s) %s exited %d",
", ".join(playbooks), e.returncode)
sys.exit(e.returncode)
def run_playbook(parsed_args, playbook, *args, **kwargs):
"""Run a Kayobe Ansible playbook."""
return run_playbooks(parsed_args, [playbook], *args, **kwargs)
def config_dump(parsed_args, host=None, hosts=None, var_name=None,
facts=False, extra_vars=None):
dump_dir = tempfile.mkdtemp()
try:
if not extra_vars:
extra_vars = {}
extra_vars["dump_path"] = dump_dir
if host or hosts:
extra_vars["dump_hosts"] = host or hosts
if var_name:
extra_vars["dump_var_name"] = var_name
if facts is not None:
extra_vars["dump_facts"] = facts
run_playbook(parsed_args, "ansible/dump-config.yml",
extra_vars=extra_vars, quiet=True)
hostvars = {}
for path in os.listdir(dump_dir):
LOG.debug("Found dump file %s", path)
inventory_hostname, ext = os.path.splitext(path)
if ext == ".yml":
hvars = utils.read_yaml_file(os.path.join(dump_dir, path))
if host:
return hvars
else:
hostvars[inventory_hostname] = hvars
else:
LOG.warning("Unexpected extension on config dump file %s",
path)
return hostvars
finally:
shutil.rmtree(dump_dir)

0
kayobe/cli/__init__.py Normal file
View File

212
kayobe/cli/commands.py Normal file
View File

@ -0,0 +1,212 @@
import json
import platform
import sys
from cliff.command import Command
from kayobe import ansible
from kayobe import kolla_ansible
from kayobe import utils
class KayobeAnsibleMixin(object):
"""Mixin class for commands running Kayobe Ansible playbooks."""
def get_parser(self, prog_name):
parser = super(KayobeAnsibleMixin, self).get_parser(prog_name)
group = parser.add_argument_group("Kayobe Ansible")
ansible.add_args(group)
return parser
class KollaAnsibleMixin(object):
"""Mixin class for commands running Kolla Ansible."""
def get_parser(self, prog_name):
parser = super(KollaAnsibleMixin, self).get_parser(prog_name)
group = parser.add_argument_group("Kolla Ansible")
kolla_ansible.add_args(group)
return parser
class ControlHostBootstrap(KayobeAnsibleMixin, Command):
"""Bootstrap the Kayobe control environment."""
def take_action(self, parsed_args):
self.app.LOG.debug("Bootstrapping Kayobe control host")
linux_distname = platform.linux_distribution()[0]
if linux_distname == "CentOS Linux":
utils.yum_install(["epel-release"])
else:
# On RHEL, the following should be done to install EPEL:
# sudo subscription-manager repos --enable=qci-1.0-for-rhel-7-rpms
# if ! yum info epel-release >/dev/null 2>&1 ; then
# sudo yum -y install \
# https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
# fi
self.app.LOG.error("%s is not currently supported", linux_distname)
sys.exit(1)
utils.yum_install(["ansible"])
ansible.galaxy_install("ansible/requirements.yml", "ansible/roles")
playbooks = ["ansible/%s.yml" % playbook for playbook in
"bootstrap", "kolla"]
ansible.run_playbooks(parsed_args, playbooks)
class ConfigurationDump(KayobeAnsibleMixin, Command):
"""Dump Kayobe configuration."""
def get_parser(self, prog_name):
parser = super(ConfigurationDump, self).get_parser(prog_name)
group = parser.add_argument_group("Configuration Dump")
group.add_argument("--dump-facts", default=False,
help="whether to gather and dump host facts")
group.add_argument("--host",
help="name of a host to dump config for")
group.add_argument("--hosts",
help="name of hosts and/or groups to dump config "
"for")
group.add_argument("--var-name",
help="name of a variable to dump")
return parser
def take_action(self, parsed_args):
self.app.LOG.debug("Dumping Ansible configuration")
hostvars = ansible.config_dump(parsed_args,
host=parsed_args.host,
hosts=parsed_args.hosts,
facts=parsed_args.dump_facts,
var_name=parsed_args.var_name)
try:
json.dump(hostvars, sys.stdout, sort_keys=True, indent=4)
except TypeError as e:
self.app.LOG.error("Failed to JSON encode configuration: %s",
repr(e))
sys.exit(1)
class PlaybookRun(KayobeAnsibleMixin, Command):
"""Run a Kayobe Ansible playbook."""
def get_parser(self, prog_name):
parser = super(PlaybookRun, self).get_parser(prog_name)
group = parser.add_argument_group("Kayobe Ansible")
group.add_argument("playbook", nargs="+",
help="name of the playbook(s) to run")
return parser
def take_action(self, parsed_args):
self.app.LOG.debug("Running Kayobe playbook(s)")
ansible.run_playbooks(parsed_args, parsed_args.playbook)
class KollaAnsibleRun(KollaAnsibleMixin, Command):
"""Run a Kolla Ansible command."""
def get_parser(self, prog_name):
parser = super(KollaAnsibleRun, self).get_parser(prog_name)
group = parser.add_argument_group("Kolla Ansible")
group.add_argument("--kolla-inventory-filename", default="overcloud",
choices=["seed", "overcloud"],
help="name of the kolla-ansible inventory file, "
"one of seed or overcloud (default "
"overcloud)")
group.add_argument("command",
help="name of the kolla-ansible command to run")
return parser
def take_action(self, parsed_args):
self.app.LOG.debug("Running Kolla Ansible command")
kolla_ansible.run(parsed_args, parsed_args.command,
parsed_args.kolla_inventory_filename)
class SeedVMProvision(KollaAnsibleMixin, KayobeAnsibleMixin, Command):
"""Provision the seed VM."""
def take_action(self, parsed_args):
self.app.LOG.debug("Provisioning seed VM")
ansible.run_playbook(parsed_args, "ansible/seed-vm.yml")
class SeedDeploy(KollaAnsibleMixin, KayobeAnsibleMixin, Command):
"""Deploy the seed node services."""
def take_action(self, parsed_args):
self.app.LOG.debug("Deploying seed services")
self._configure_os(parsed_args)
self._deploy_bifrost(parsed_args)
def _configure_os(self, parsed_args):
ansible_user = ansible.config_dump(parsed_args, host="seed",
var_name="kayobe_ansible_user")
playbooks = ["ansible/%s.yml" % playbook for playbook in
"ip-allocation", "ssh-known-host", "kayobe-ansible-user",
"disable-selinux", "network", "ntp"]
ansible.run_playbooks(parsed_args, playbooks, limit="seed")
kolla_ansible.run_seed(parsed_args, "bootstrap-servers",
extra_vars={"ansible_user": ansible_user})
playbooks = ["ansible/%s.yml" % playbook for playbook in
"kolla-host", "docker"]
ansible.run_playbooks(parsed_args, playbooks, limit="seed")
def _deploy_bifrost(self, parsed_args):
ansible.run_playbook(parsed_args, "ansible/kolla-bifrost.yml")
# FIXME: Do this via configuration.
extra_vars = {"kolla_install_type": "source",
"docker_namespace": "stackhpc"}
kolla_ansible.run_seed(parsed_args, "deploy-bifrost",
extra_vars=extra_vars)
class OvercloudProvision(KollaAnsibleMixin, KayobeAnsibleMixin, Command):
"""Provision the overcloud."""
def take_action(self, parsed_args):
self.app.LOG.debug("Provisioning overcloud")
self._configure_network(parsed_args)
self._configure_bios_and_raid(parsed_args)
self._deploy_servers(parsed_args)
def _configure_network(self, parsed_args):
self.app.LOG.debug("TODO: configure overcloud network")
def _configure_bios_and_raid(self, parsed_args):
self.app.LOG.debug("TODO: configure overcloud BIOS and RAID")
def _deploy_servers(self, parsed_args):
self.app.LOG.debug("Deploying overcloud servers via Bifrost")
kolla_ansible.run_seed(parsed_args, "deploy-servers")
class OvercloudDeploy(KollaAnsibleMixin, KayobeAnsibleMixin, Command):
"""Deploy the overcloud services."""
def take_action(self, parsed_args):
self.app.LOG.debug("Deploying overcloud services")
self._configure_os(parsed_args)
self._deploy_services(parsed_args)
def _configure_os(self, parsed_args):
ansible_user = ansible.config_dump(parsed_args, host="controllers[0]",
var_name="kayobe_ansible_user")
playbooks = ["ansible/%s.yml" % playbook for playbook in
"ip-allocation", "ssh-known-host", "kayobe-ansible-user",
"disable-selinux", "network", "ntp"]
ansible.run_playbooks(parsed_args, playbooks, limit="controllers")
kolla_ansible.run_overcloud(parsed_args, "bootstrap-servers",
extra_vars={"ansible_user": ansible_user})
playbooks = ["ansible/%s.yml" % playbook for playbook in
"kolla-host", "docker"]
ansible.run_playbooks(parsed_args, playbooks, limit="controllers")
def _deploy_services(self, parsed_args):
playbooks = ["ansible/%s.yml" % playbook for playbook in
"kolla-openstack", "swift-setup"]
ansible.run_playbooks(parsed_args, playbooks)
for command in ["pull", "prechecks", "deploy"]:
kolla_ansible.run_overcloud(parsed_args, command)
# FIXME: Fudge to work around incorrect configuration path.
extra_vars = {"node_config_directory": parsed_args.config_path}
kolla_ansible.run_overcloud(parsed_args, command,
extra_vars=extra_vars)

0
kayobe/cmd/__init__.py Normal file
View File

35
kayobe/cmd/kayobe.py Normal file
View File

@ -0,0 +1,35 @@
import sys
from cliff.app import App
from cliff.commandmanager import CommandManager
class KayobeApp(App):
def __init__(self):
super(KayobeApp, self).__init__(
description='Kayobe Command Line Interface (CLI)',
version='0.1',
command_manager=CommandManager('kayobe.cli'),
deferred_help=True,
)
def initialize_app(self, argv):
self.LOG.debug('initialize_app')
def prepare_to_run_command(self, cmd):
self.LOG.debug('prepare_to_run_command %s', cmd.__class__.__name__)
def clean_up(self, cmd, result, err):
self.LOG.debug('clean_up %s', cmd.__class__.__name__)
if err:
self.LOG.debug('got an error: %s', err)
def main(argv=sys.argv[1:]):
myapp = KayobeApp()
return myapp.run(argv)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

121
kayobe/kolla_ansible.py Normal file
View File

@ -0,0 +1,121 @@
import logging
import os
import os.path
import subprocess
import sys
from kayobe import utils
DEFAULT_CONFIG_PATH = "/etc/kolla"
CONFIG_PATH_ENV = "KOLLA_CONFIG_PATH"
DEFAULT_VENV_PATH = "ansible/kolla-venv"
VENV_PATH_ENV = "KOLLA_VENV"
LOG = logging.getLogger(__name__)
def add_args(parser):
"""Add arguments required for running Kolla Ansible to a parser."""
default_config_path = os.getenv(CONFIG_PATH_ENV, DEFAULT_CONFIG_PATH)
default_venv = os.getenv(VENV_PATH_ENV, DEFAULT_VENV_PATH)
parser.add_argument("--kolla-config-path", default=default_config_path,
help="path to Kolla configuration. "
"(default=$%s or %s)" %
(CONFIG_PATH_ENV, DEFAULT_CONFIG_PATH))
parser.add_argument("--kolla-extra-vars", metavar="EXTRA_VARS",
action="append",
help="set additional variables as key=value or "
"YAML/JSON for Kolla Ansible")
parser.add_argument("--kolla-inventory", metavar="INVENTORY",
help="specify inventory host path "
"(default=$%s/inventory or %s/inventory) or "
"comma-separated host list for Kolla Ansible" %
(CONFIG_PATH_ENV, DEFAULT_CONFIG_PATH))
parser.add_argument("--kolla-tags", metavar="TAGS", action="append",
help="only run plays and tasks tagged with these "
"values in Kolla Ansible")
parser.add_argument("--kolla-venv", metavar="VENV", default=default_venv,
help="path to virtualenv where Kolla Ansible is "
"installed")
def _get_inventory_path(parsed_args, inventory_filename):
"""Return the path to the Kolla inventory."""
if parsed_args.kolla_inventory:
return parsed_args.kolla_inventory
else:
return os.path.join(parsed_args.kolla_config_path, "inventory",
inventory_filename)
def _validate_args(parsed_args, inventory_filename):
"""Validate Kayobe Ansible arguments."""
result = utils.is_readable_dir(parsed_args.kolla_config_path)
if not result["result"]:
LOG.error("Kolla configuration path %s is invalid: %s",
parsed_args.kolla_config_path, result["message"])
sys.exit(1)
inventory = _get_inventory_path(parsed_args, inventory_filename)
result = utils.is_readable_file(inventory)
if not result["result"]:
LOG.error("Kolla inventory %s is invalid: %s",
inventory, result["message"])
sys.exit(1)
result = utils.is_readable_dir(parsed_args.kolla_venv)
if not result["result"]:
LOG.error("Kolla virtualenv %s is invalid: %s",
parsed_args.kolla_venv, result["message"])
sys.exit(1)
def build_args(parsed_args, command, inventory_filename, extra_vars=None,
tags=None):
"""Build arguments required for running Kolla Ansible."""
venv_activate = os.path.join(parsed_args.kolla_venv, "bin", "activate")
cmd = ["source", venv_activate, "&&"]
cmd = ["kolla-ansible", command]
inventory = _get_inventory_path(parsed_args, inventory_filename)
cmd += ["--inventory", inventory]
cmd += ["--configdir", parsed_args.kolla_config_path]
cmd += ["--passwords",
os.path.join(parsed_args.kolla_config_path, "passwords.yml")]
if parsed_args.kolla_extra_vars:
for extra_var in parsed_args.kolla_extra_vars:
cmd += ["-e", extra_var]
if extra_vars:
for extra_var_name, extra_var_value in extra_vars.items():
cmd += ["-e", "%s=%s" % (extra_var_name, extra_var_value)]
if parsed_args.kolla_tags or tags:
all_tags = [t for t in [parsed_args.kolla_tags, tags] if t]
cmd += ["--tags", ",".join(all_tags)]
return cmd
def run(parsed_args, command, inventory_filename, extra_vars=None,
tags=None, quiet=False):
"""Run a Kolla Ansible command."""
_validate_args(parsed_args, inventory_filename)
cmd = build_args(parsed_args, command,
inventory_filename=inventory_filename,
extra_vars=extra_vars, tags=tags)
try:
utils.run_command(" ".join(cmd), quiet=quiet, shell=True)
except subprocess.CalledProcessError as e:
LOG.error("kolla-ansible %s exited %d", command, e.returncode)
sys.exit(e.returncode)
def run_seed(*args, **kwargs):
"""Run a Kolla Ansible command using the seed inventory."""
return run(*args, inventory_filename="seed", **kwargs)
def run_overcloud(*args, **kwargs):
"""Run a Kolla Ansible command using the overcloud inventory."""
return run(*args, inventory_filename="overcloud", **kwargs)

68
kayobe/utils.py Normal file
View File

@ -0,0 +1,68 @@
import logging
import os
import subprocess
import sys
import yaml
LOG = logging.getLogger(__name__)
def yum_install(packages):
"""Install a list of packages via Yum."""
cmd = ["sudo", "yum", "-y", "install"]
cmd += packages
try:
subprocess.check_call(cmd)
except subprocess.CalledProcessError as e:
print ("Failed to install packages %s via Yum: returncode %d" %
(", ".join(packages), e.returncode))
sys.exit(e.returncode)
def read_yaml_file(path):
"""Read and decode a YAML file."""
try:
with open(path, "r") as f:
content = f.read()
except IOError as e:
print ("Failed to open config dump file %s: %s" %
(path, repr(e)))
sys.exit(1)
try:
return yaml.load(content)
except ValueError as e:
print ("Failed to decode config dump YAML file %s: %s" %
(path, repr(e)))
sys.exit(1)
def is_readable_dir(path):
"""Check whether a path references a readable directory."""
if not os.path.exists(path):
return {"result": False, "message": "Path does not exist"}
if not os.path.isdir(path):
return {"result": False, "message": "Path is not a directory"}
if not os.access(path, os.R_OK):
return {"result": False, "message": "Directory is not readable"}
return {"result": True}
def is_readable_file(path):
"""Check whether a path references a readable file."""
if not os.path.exists(path):
return {"result": False, "message": "Path does not exist"}
if not os.path.isfile(path):
return {"result": False, "message": "Path is not a file"}
if not os.access(path, os.R_OK):
return {"result": False, "message": "File is not readable"}
return {"result": True}
def run_command(cmd, quiet=False, **kwargs):
"""Run a command, checking the output."""
if quiet:
kwargs["stdout"] = subprocess.PIPE
kwargs["stderr"] = subprocess.PIPE
LOG.debug("Running command: %s", " ".join(cmd))
subprocess.check_call(cmd, **kwargs)

View File

@ -1,45 +0,0 @@
#!/bin/bash
set -e
function run_kolla_ansible {
export KOLLA_CONFIG_PATH=${KOLLA_CONFIG_PATH:-/etc/kolla}
# Ansible fails silently if the inventory does not exist.
test -e ${KOLLA_CONFIG_PATH}/inventory/seed
KOLLA_VENV=$(pwd)/ansible/kolla-venv
source ${KOLLA_VENV}/bin/activate
kolla-ansible \
--configdir ${KOLLA_CONFIG_PATH} \
--passwords ${KOLLA_CONFIG_PATH}/passwords.yml \
-i ${KOLLA_CONFIG_PATH}/inventory/seed \
$@
deactivate
}
function configure_network {
echo "TODO: configure overcloud network"
}
function configure_bios_and_raid {
echo "TODO: configure overcloud BIOS and RAID"
}
function deploy_servers {
# Deploy servers with Bifrost
run_kolla_ansible deploy-servers
}
function provision_overcloud {
configure_network
configure_bios_and_raid
deploy_servers
}
###########################################################
# Main
function main {
provision_overcloud
}
provision_overcloud

View File

@ -1,34 +0,0 @@
#!/bin/bash
set -e
function run_playbook {
KAYOBE_CONFIG_PATH=${KAYOBE_CONFIG_PATH:-/etc/kayobe}
# Ansible fails silently if the inventory does not exist.
test -e ${KAYOBE_CONFIG_PATH}/inventory
ansible-playbook \
-i ${KAYOBE_CONFIG_PATH}/inventory \
-e @${KAYOBE_CONFIG_PATH}/dns.yml \
-e @${KAYOBE_CONFIG_PATH}/globals.yml \
-e @${KAYOBE_CONFIG_PATH}/kolla.yml \
-e @${KAYOBE_CONFIG_PATH}/networks.yml \
-e @${KAYOBE_CONFIG_PATH}/network-allocation.yml \
-e @${KAYOBE_CONFIG_PATH}/ntp.yml \
-e @${KAYOBE_CONFIG_PATH}/seed-vm.yml \
-e @${KAYOBE_CONFIG_PATH}/ssh.yml \
-e @${KAYOBE_CONFIG_PATH}/swift.yml \
$@
}
function provision_seed_vm {
run_playbook ansible/seed-vm.yml
}
###########################################################
# Main
function main {
provision_seed_vm
}
main $*

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
cliff

51
setup.py Normal file
View File

@ -0,0 +1,51 @@
#!/usr/bin/env python
from setuptools import setup, find_packages
PROJECT = 'kayobe'
VERSION = '0.1'
try:
long_description = open('README.md', 'rt').read()
except IOError:
long_description = ''
setup(
name=PROJECT,
version=VERSION,
description='OpenStack deployment for scientific computing',
long_description=long_description,
author='StackHPC',
author_email='mark@stackhpc.com',
url='https://github.com/stackhpc/kayobe',
download_url='https://github.com/stackhpc/kayobe/tarball/master',
provides=[],
install_requires=['cliff'],
namespace_packages=[],
packages=find_packages(),
include_package_data=True,
entry_points={
'console_scripts': [
'kayobe = kayobe.cmd.kayobe:main'
],
'kayobe.cli': [
'control_host_bootstrap = kayobe.cli.commands:ControlHostBootstrap',
'configuration_dump = kayobe.cli.commands:ConfigurationDump',
'kolla_ansible_run = kayobe.cli.commands:KollaAnsibleRun',
'overcloud_deploy = kayobe.cli.commands:OvercloudDeploy',
'overcloud_provision = kayobe.cli.commands:OvercloudProvision',
'playbook_run = kayobe.cli.commands:PlaybookRun',
'seed_deploy = kayobe.cli.commands:SeedDeploy',
'seed_vm_provision = kayobe.cli.commands:SeedVMProvision',
],
},
zip_safe=False,
)