Support multiple environments from a single configuration

Change-Id: I848d834aa36943027c126e26e93e4a4680521144
Story: 2002009
Task: 40037
This commit is contained in:
Pierre Riteau 2021-01-13 17:51:13 +01:00
parent f8575af9c0
commit 1419636930
16 changed files with 111 additions and 46 deletions

View File

@ -47,7 +47,7 @@
console_allocation_pool_start: "{{ ironic_serial_console_tcp_pool_start }}"
console_allocation_pool_end: "{{ ironic_serial_console_tcp_pool_end }}"
console_allocation_ironic_nodes: "{{ baremetal_nodes }}"
console_allocation_filename: "{{ kayobe_config_path }}/console-allocation.yml"
console_allocation_filename: "{{ kayobe_env_config_path }}/console-allocation.yml"
when: cmd == "enable"
- name: Enable serial console

View File

@ -7,6 +7,15 @@
# Path to Kayobe configuration directory on Ansible control host.
kayobe_config_path: "{{ lookup('env', 'KAYOBE_CONFIG_PATH') | default('/etc/kayobe', true) }}"
# Name of Kayobe environment to use. Default is $KAYOBE_ENVIRONMENT, or an
# empty string if $KAYOBE_ENVIRONMENT is not set. Can also be set via the
# --environment argument when invoking kayobe.
kayobe_environment: "{{ lookup('env', 'KAYOBE_ENVIRONMENT') }}"
# Path to Kayobe configuration directory on Ansible control host with an
# environment path appended if kayobe_environment is set.
kayobe_env_config_path: "{{ kayobe_config_path ~ ('/environments/' ~ kayobe_environment if kayobe_environment else '') }}"
###############################################################################
# Remote path configuration (seed, seed-hypervisor and overcloud hosts).

View File

@ -109,7 +109,7 @@ seed_users: "{{ users_default }}"
# squid:
# name: "squid"
# image: "stackhpc/squid:3.5.20-1"
# pre: "{{ kayobe_config_path }}/containers/squid/pre.yml"
# post: "{{ kayobe_config_path }}/containers/squid/post.yml"
# pre: "{{ kayobe_env_config_path }}/containers/squid/pre.yml"
# post: "{{ kayobe_env_config_path }}/containers/squid/post.yml"
#
seed_containers: {}

View File

@ -29,5 +29,5 @@
- item | net_bootproto != 'dhcp'
roles:
- role: ip-allocation
ip_allocation_filename: "{{ kayobe_config_path }}/network-allocation.yml"
ip_allocation_filename: "{{ kayobe_env_config_path }}/network-allocation.yml"
ip_allocation_hostname: "{{ inventory_hostname }}"

View File

@ -32,7 +32,7 @@
- block:
- name: Check whether a Kolla extra globals configuration file exists
stat:
path: "{{ kayobe_config_path ~ '/kolla/globals.yml' }}"
path: "{{ kayobe_env_config_path ~ '/kolla/globals.yml' }}"
get_checksum: False
get_md5: False
mime: False
@ -40,7 +40,7 @@
- name: Read the Kolla extra globals configuration file
set_fact:
kolla_extra_globals: "{{ lookup('template', kayobe_config_path ~ '/kolla/globals.yml') | from_yaml }}"
kolla_extra_globals: "{{ lookup('template', kayobe_env_config_path ~ '/kolla/globals.yml') | from_yaml }}"
when: globals_stat.stat.exists
tags:
- config
@ -79,9 +79,9 @@
kolla_ansible_install_epel: "{{ dnf_install_epel }}"
kolla_external_fqdn_cert: "{{ kolla_config_path }}/certificates/haproxy.pem"
kolla_internal_fqdn_cert: "{{ kolla_config_path }}/certificates/haproxy-internal.pem"
kolla_ansible_passwords_path: "{{ kayobe_config_path }}/kolla/passwords.yml"
kolla_overcloud_group_vars_path: "{{ kayobe_config_path }}/kolla/inventory/group_vars"
kolla_ansible_certificates_path: "{{ kayobe_config_path }}/kolla/certificates"
kolla_ansible_passwords_path: "{{ kayobe_env_config_path }}/kolla/passwords.yml"
kolla_overcloud_group_vars_path: "{{ kayobe_env_config_path }}/kolla/inventory/group_vars"
kolla_ansible_certificates_path: "{{ kayobe_env_config_path }}/kolla/certificates"
# NOTE: This differs from the default SELinux mode in kolla ansible,
# which is permissive. The justification for using this mode is twofold:
# 1. it avoids filling up the audit log

View File

@ -4,7 +4,7 @@
tags:
- kolla-bifrost
vars:
kolla_bifrost_extra_globals_path: "{{ kayobe_config_path ~ '/kolla/config/bifrost/bifrost.yml' }}"
kolla_bifrost_extra_globals_path: "{{ kayobe_env_config_path ~ '/kolla/config/bifrost/bifrost.yml' }}"
pre_tasks:
- name: Check whether a Kolla Bifrost extra globals configuration file exists

View File

@ -7,4 +7,4 @@
- role: kolla
kolla_install_epel: "{{ dnf_install_epel }}"
- role: kolla-build
kolla_build_extra_config_path: "{{ kayobe_config_path }}/kolla/kolla-build.conf"
kolla_build_extra_config_path: "{{ kayobe_env_config_path }}/kolla/kolla-build.conf"

View File

@ -101,7 +101,7 @@
- block:
- name: Check whether Kolla extra configuration files exist
stat:
path: "{{ kayobe_config_path }}/kolla/config/{{ item.file }}"
path: "{{ kayobe_env_config_path }}/kolla/config/{{ item.file }}"
get_checksum: False
get_md5: False
mime: False
@ -245,4 +245,4 @@
kolla_extra_octavia: "{{ kolla_extra_config.octavia | default }}"
kolla_extra_sahara: "{{ kolla_extra_config.sahara | default }}"
kolla_extra_zookeeper: "{{ kolla_extra_config.zookeeper | default }}"
kolla_extra_config_path: "{{ kayobe_config_path }}/kolla/config"
kolla_extra_config_path: "{{ kayobe_env_config_path }}/kolla/config"

View File

@ -61,7 +61,7 @@
- grafana
pre_tasks:
- name: Include Kolla passwords for Grafana local admin account credentials
include_vars: "{{ kayobe_config_path }}/kolla/passwords.yml"
include_vars: "{{ kayobe_env_config_path }}/kolla/passwords.yml"
roles:
- role: stackhpc.grafana-conf
grafana_conf_organisation: "{{ grafana_control_plane_organisation }}"

View File

@ -53,4 +53,4 @@
{% endfor %}
{% endfor %}
dest: "{{ kayobe_config_path }}/inventory/overcloud"
dest: "{{ kayobe_env_config_path }}/inventory/overcloud"

View File

@ -31,5 +31,5 @@
- include_role:
name: swift-rings
vars:
swift_config_path: "{{ kayobe_config_path }}/kolla/config/swift"
swift_config_path: "{{ kayobe_env_config_path }}/kolla/config/swift"
when: kolla_enable_swift | bool

View File

@ -18,8 +18,8 @@ For example, to deploy a squid container image:
seed_containers:
squid:
image: "stackhpc/squid:3.5.20-1"
pre: "{{ kayobe_config_path }}/containers/squid/pre.yml"
post: "{{ kayobe_config_path }}/containers/squid/post.yml"
pre: "{{ kayobe_env_config_path }}/containers/squid/pre.yml"
post: "{{ kayobe_env_config_path }}/containers/squid/post.yml"
Please notice the *optional* pre and post Ansible task files - those need to
be created in ``kayobe-config`` path and will be run before and after

View File

@ -4,9 +4,19 @@
###############################################################################
# Local path configuration (Ansible control host).
# Path to Kayobe configuration directory on Ansible control host.
# Path to Kayobe configuration directory on Ansible control host, with an
# environment path appended if kayobe_environment is set.
#kayobe_config_path:
# Name of Kayobe environment to use. Default is $KAYOBE_ENVIRONMENT, or an
# empty string if $KAYOBE_ENVIRONMENT is not set. Can also be set via the
# --environment argument when invoking kayobe.
#kayobe_environment:
# Path to Kayobe configuration directory on Ansible control host with an
# environment path appended if kayobe_environment is set.
#kayobe_env_config_path:
###############################################################################
# Remote path configuration (seed, seed-hypervisor and overcloud hosts).

View File

@ -92,8 +92,8 @@
# squid:
# name: "squid"
# image: "stackhpc/squid:3.5.20-1"
# pre: "{{ kayobe_config_path }}/containers/squid/pre.yml"
# post: "{{ kayobe_config_path }}/containers/squid/post.yml"
# pre: "{{ kayobe_env_config_path }}/containers/squid/pre.yml"
# post: "{{ kayobe_env_config_path }}/containers/squid/post.yml"
#
#seed_containers:

View File

@ -30,12 +30,15 @@ DEFAULT_CONFIG_PATH = "/etc/kayobe"
CONFIG_PATH_ENV = "KAYOBE_CONFIG_PATH"
ENVIRONMENT_ENV = "KAYOBE_ENVIRONMENT"
LOG = logging.getLogger(__name__)
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)
default_environment = os.getenv(ENVIRONMENT_ENV)
parser.add_argument("-b", "--become", action="store_true",
help="run operations with become (nopasswd implied)")
parser.add_argument("-C", "--check", action="store_true",
@ -45,6 +48,9 @@ def add_args(parser):
help="path to Kayobe configuration. "
"(default=$%s or %s)" %
(CONFIG_PATH_ENV, DEFAULT_CONFIG_PATH))
parser.add_argument("--environment", default=default_environment,
help="specify environment name (default=$%s or None)" %
ENVIRONMENT_ENV)
parser.add_argument("-e", "--extra-vars", metavar="EXTRA_VARS",
action="append",
help="set additional variables as key=value or "
@ -69,12 +75,23 @@ def add_args(parser):
"note this has no affect on kolla-ansible.")
def _get_inventory_path(parsed_args):
def _get_kayobe_environment_path(parsed_args):
"""Return the path to the Kayobe environment or None if not specified."""
env_path = None
if parsed_args.environment:
# Specified via --environment or KAYOBE_ENVIRONMENT.
kc_environments = os.path.join(parsed_args.config_path, "environments")
env_path = os.path.join(kc_environments, parsed_args.environment)
return env_path
def _get_inventory_path(parsed_args, env_path):
"""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")
return os.path.join(env_path if env_path else parsed_args.config_path,
"inventory")
def _validate_args(parsed_args, playbooks):
@ -86,7 +103,15 @@ def _validate_args(parsed_args, playbooks):
parsed_args.config_path, result["message"])
sys.exit(1)
inventory = _get_inventory_path(parsed_args)
env_path = _get_kayobe_environment_path(parsed_args)
if env_path:
result = utils.is_readable_dir(env_path)
if not result["result"]:
LOG.error("Kayobe environment %s is invalid: %s",
env_path, result["message"])
sys.exit(1)
inventory = _get_inventory_path(parsed_args, env_path)
result = utils.is_readable_dir(inventory)
if not result["result"]:
LOG.error("Kayobe inventory %s is invalid: %s",
@ -101,19 +126,25 @@ def _validate_args(parsed_args, playbooks):
sys.exit(1)
def _get_vars_files(config_path):
def _get_vars_files(vars_paths):
"""Return a list of Kayobe Ansible configuration variable files.
The files will be sorted alphabetically by name.
The list of directories given as argument is searched to create the list of
variable files. The files will be sorted alphabetically by name for each
directory, but ordering of directories is kept to allow overrides.
"""
vars_files = []
for vars_file in os.listdir(config_path):
abs_path = os.path.join(config_path, vars_file)
if utils.is_readable_file(abs_path)["result"]:
root, ext = os.path.splitext(vars_file)
if ext in (".yml", ".yaml", ".json"):
vars_files.append(abs_path)
return sorted(vars_files)
for vars_path in vars_paths:
path_vars_files = []
for vars_file in os.listdir(vars_path):
abs_path = os.path.join(vars_path, vars_file)
if utils.is_readable_file(abs_path)["result"]:
root, ext = os.path.splitext(vars_file)
if ext in (".yml", ".yaml", ".json"):
path_vars_files.append(abs_path)
vars_files += sorted(path_vars_files)
return vars_files
def build_args(parsed_args, playbooks,
@ -126,9 +157,13 @@ def build_args(parsed_args, playbooks,
if list_tasks or (parsed_args.list_tasks and list_tasks is None):
cmd += ["--list-tasks"]
cmd += vault.build_args(parsed_args, "--vault-password-file")
inventory = _get_inventory_path(parsed_args)
env_path = _get_kayobe_environment_path(parsed_args)
inventory = _get_inventory_path(parsed_args, env_path)
cmd += ["--inventory", inventory]
vars_files = _get_vars_files(parsed_args.config_path)
vars_paths = [parsed_args.config_path]
if env_path:
vars_paths.append(env_path)
vars_files = _get_vars_files(vars_paths)
for vars_file in vars_files:
cmd += ["-e", "@%s" % vars_file]
if parsed_args.extra_vars:
@ -165,6 +200,10 @@ def _get_environment(parsed_args):
# the environment variable is set, so that it can be referenced by
# playbooks.
env.setdefault(CONFIG_PATH_ENV, parsed_args.config_path)
# If an environment has been specified via --environment, ensure the
# environment variable is set, so that it can be referenced by playbooks.
if parsed_args.environment:
env.setdefault(ENVIRONMENT_ENV, parsed_args.environment)
# If a custom Ansible configuration file exists, use it.
ansible_cfg_path = os.path.join(parsed_args.config_path, "ansible.cfg")
if utils.is_readable_file(ansible_cfg_path)["result"]:
@ -291,6 +330,7 @@ def prune_galaxy_roles(parsed_args):
def passwords_yml_exists(parsed_args):
"""Return whether passwords.yml exists in the kayobe configuration."""
passwords_path = os.path.join(parsed_args.config_path,
'kolla', 'passwords.yml')
env_path = _get_kayobe_environment_path(parsed_args)
path = env_path if env_path else parsed_args.config_path
passwords_path = os.path.join(path, 'kolla', 'passwords.yml')
return utils.is_readable_file(passwords_path)["result"]

View File

@ -52,7 +52,7 @@ class TestCase(unittest.TestCase):
expected_env = {"KAYOBE_CONFIG_PATH": "/etc/kayobe"}
mock_run.assert_called_once_with(expected_cmd, check_output=False,
quiet=False, env=expected_env)
mock_vars.assert_called_once_with("/etc/kayobe")
mock_vars.assert_called_once_with(["/etc/kayobe"])
@mock.patch.object(utils, "run_command")
@mock.patch.object(ansible, "_get_vars_files")
@ -68,6 +68,7 @@ class TestCase(unittest.TestCase):
"-b",
"-C",
"--config-path", "/path/to/config",
"--environment", "test-env",
"-e", "ev_name1=ev_value1",
"-i", "/path/to/inventory",
"-l", "group1:host",
@ -92,10 +93,12 @@ class TestCase(unittest.TestCase):
"playbook1.yml",
"playbook2.yml",
]
expected_env = {"KAYOBE_CONFIG_PATH": "/path/to/config"}
expected_env = {"KAYOBE_CONFIG_PATH": "/path/to/config",
"KAYOBE_ENVIRONMENT": "test-env"}
mock_run.assert_called_once_with(expected_cmd, check_output=False,
quiet=False, env=expected_env)
mock_vars.assert_called_once_with("/path/to/config")
mock_vars.assert_called_once_with(
["/path/to/config", "/path/to/config/environments/test-env"])
@mock.patch.object(utils, "run_command")
@mock.patch.object(ansible, "_get_vars_files")
@ -114,6 +117,7 @@ class TestCase(unittest.TestCase):
"--become",
"--check",
"--config-path", "/path/to/config",
"--environment", "test-env",
"--extra-vars", "ev_name1=ev_value1",
"--inventory", "/path/to/inventory",
"--limit", "group1:host1",
@ -141,6 +145,7 @@ class TestCase(unittest.TestCase):
"playbook2.yml",
]
expected_env = {"KAYOBE_CONFIG_PATH": "/path/to/config",
"KAYOBE_ENVIRONMENT": "test-env",
"KAYOBE_VAULT_PASSWORD": "test-pass"}
expected_calls = [
mock.call(["which", "kayobe-vault-password-helper"],
@ -149,7 +154,8 @@ class TestCase(unittest.TestCase):
env=expected_env)
]
self.assertEqual(expected_calls, mock_run.mock_calls)
mock_vars.assert_called_once_with("/path/to/config")
mock_vars.assert_called_once_with(
["/path/to/config", "/path/to/config/environments/test-env"])
@mock.patch.object(utils, "run_command")
@mock.patch.object(ansible, "_get_vars_files")
@ -262,7 +268,7 @@ class TestCase(unittest.TestCase):
expected_env = {"KAYOBE_CONFIG_PATH": "/etc/kayobe"}
mock_run.assert_called_once_with(expected_cmd, check_output=False,
quiet=False, env=expected_env)
mock_vars.assert_called_once_with("/etc/kayobe")
mock_vars.assert_called_once_with(["/etc/kayobe"])
@mock.patch.object(utils, "run_command")
@mock.patch.object(ansible, "_get_vars_files")
@ -291,7 +297,7 @@ class TestCase(unittest.TestCase):
expected_env = {"KAYOBE_CONFIG_PATH": "/etc/kayobe"}
mock_run.assert_called_once_with(expected_cmd, check_output=False,
quiet=False, env=expected_env)
mock_vars.assert_called_once_with("/etc/kayobe")
mock_vars.assert_called_once_with(["/etc/kayobe"])
@mock.patch.object(utils, "run_command")
@mock.patch.object(ansible, "_get_vars_files")
@ -320,7 +326,7 @@ class TestCase(unittest.TestCase):
expected_env = {"KAYOBE_CONFIG_PATH": "/etc/kayobe"}
mock_run.assert_called_once_with(expected_cmd, check_output=False,
quiet=False, env=expected_env)
mock_vars.assert_called_once_with("/etc/kayobe")
mock_vars.assert_called_once_with(["/etc/kayobe"])
@mock.patch.object(utils, "run_command")
@mock.patch.object(utils, "is_readable_file")
@ -346,7 +352,7 @@ class TestCase(unittest.TestCase):
}
mock_run.assert_called_once_with(expected_cmd, check_output=False,
quiet=False, env=expected_env)
mock_vars.assert_called_once_with("/etc/kayobe")
mock_vars.assert_called_once_with(["/etc/kayobe"])
mock_readable.assert_called_once_with("/etc/kayobe/ansible.cfg")
@mock.patch.object(utils, "run_command")
@ -374,7 +380,7 @@ class TestCase(unittest.TestCase):
}
mock_run.assert_called_once_with(expected_cmd, check_output=False,
quiet=False, env=expected_env)
mock_vars.assert_called_once_with("/etc/kayobe")
mock_vars.assert_called_once_with(["/etc/kayobe"])
mock_readable.assert_called_once_with("/etc/kayobe/ansible.cfg")
@mock.patch.object(utils, "run_command")