diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/Makefile | 25 | ||||
-rw-r--r-- | utils/docs/man/man1/atomic-openshift-installer.1 | 186 | ||||
-rw-r--r-- | utils/docs/man/man1/atomic-openshift-installer.1.asciidoc.in | 167 | ||||
-rw-r--r-- | utils/etc/ansible-quiet.cfg | 33 | ||||
-rw-r--r-- | utils/etc/ansible.cfg | 4 | ||||
-rw-r--r-- | utils/setup.py | 2 | ||||
-rw-r--r-- | utils/src/MANIFEST.in | 1 | ||||
-rw-r--r-- | utils/src/ooinstall/cli_installer.py | 28 | ||||
-rw-r--r-- | utils/src/ooinstall/oo_config.py | 33 | ||||
-rw-r--r-- | utils/src/ooinstall/openshift_ansible.py | 46 | ||||
-rw-r--r-- | utils/src/ooinstall/utils.py | 10 | ||||
-rw-r--r-- | utils/test/cli_installer_tests.py | 166 | ||||
-rw-r--r-- | utils/test/oo_config_tests.py | 80 |
13 files changed, 664 insertions, 117 deletions
diff --git a/utils/Makefile b/utils/Makefile index 79c27626a..59aff92fd 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -25,6 +25,12 @@ NAME := oo-install TESTPACKAGE := oo-install SHORTNAME := ooinstall +# This doesn't evaluate until it's called. The -D argument is the +# directory of the target file ($@), kinda like `dirname`. +ASCII2MAN = a2x -D $(dir $@) -d manpage -f manpage $< +MANPAGES := docs/man/man1/atomic-openshift-installer.1 +VERSION := 1.3 + sdist: clean python setup.py sdist rm -fR $(SHORTNAME).egg-info @@ -35,6 +41,21 @@ clean: @rm -fR build dist rpm-build MANIFEST htmlcov .coverage cover ooinstall.egg-info oo-install @rm -fR $(NAME)env + +# To force a rebuild of the docs run 'touch' on any *.in file under +# docs/man/man1/ +docs: $(MANPAGES) + +# Regenerate %.1.asciidoc if %.1.asciidoc.in has been modified more +# recently than %.1.asciidoc. +%.1.asciidoc: %.1.asciidoc.in + sed "s/%VERSION%/$(VERSION)/" $< > $@ + +# Regenerate %.1 if %.1.asciidoc or VERSION has been modified more +# recently than %.1. (Implicitly runs the %.1.asciidoc recipe) +%.1: %.1.asciidoc + $(ASCII2MAN) + viewcover: xdg-open cover/index.html @@ -59,7 +80,7 @@ ci-pylint: @echo "#############################################" @echo "# Running PyLint Tests in virtualenv" @echo "#############################################" - . $(NAME)env/bin/activate && python -m pylint --rcfile ../git/.pylintrc src/ooinstall/cli_installer.py src/ooinstall/oo_config.py src/ooinstall/openshift_ansible.py src/ooinstall/variants.py + . $(NAME)env/bin/activate && python -m pylint --rcfile ../git/.pylintrc src/ooinstall/cli_installer.py src/ooinstall/oo_config.py src/ooinstall/openshift_ansible.py src/ooinstall/variants.py ../callback_plugins/openshift_quick_installer.py ci-list-deps: @echo "#############################################" @@ -72,12 +93,14 @@ ci-pyflakes: @echo "# Running Pyflakes Compliance Tests in virtualenv" @echo "#################################################" . $(NAME)env/bin/activate && pyflakes src/ooinstall/*.py + . $(NAME)env/bin/activate && pyflakes ../callback_plugins/openshift_quick_installer.py ci-pep8: @echo "#############################################" @echo "# Running PEP8 Compliance Tests in virtualenv" @echo "#############################################" . $(NAME)env/bin/activate && pep8 --ignore=E501,E121,E124 src/$(SHORTNAME)/ + . $(NAME)env/bin/activate && pep8 --ignore=E501,E121,E124 ../callback_plugins/openshift_quick_installer.py ci: clean virtualenv ci-list-deps ci-pep8 ci-pylint ci-pyflakes ci-unittests : diff --git a/utils/docs/man/man1/atomic-openshift-installer.1 b/utils/docs/man/man1/atomic-openshift-installer.1 new file mode 100644 index 000000000..4da82191b --- /dev/null +++ b/utils/docs/man/man1/atomic-openshift-installer.1 @@ -0,0 +1,186 @@ +'\" t +.\" Title: atomic-openshift-installer +.\" Author: [see the "AUTHOR" section] +.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> +.\" Date: 09/28/2016 +.\" Manual: atomic-openshift-installer +.\" Source: atomic-openshift-utils 1.3 +.\" Language: English +.\" +.TH "ATOMIC\-OPENSHIFT\-I" "1" "09/28/2016" "atomic\-openshift\-utils 1\&.3" "atomic\-openshift\-installer" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +atomic-openshift-installer \- Interactive OpenShift Container Platform (OCP) installer +.SH "SYNOPSIS" +.sp +atomic\-openshift\-installer [OPTIONS] COMMAND [OPTS] +.SH "DESCRIPTION" +.sp +\fBatomic\-openshift\-installer\fR makes the process for installing OCP easier by interactively gathering the data needed to run on each host\&. It can also be run in unattended mode if provided with a configuration file\&. +.SH "OPTIONS" +.sp +The following options are common to all commands\&. +.PP +\fB\-u\fR, \fB\-\-unattended\fR +.RS 4 +Run installer in +\fBunattended\fR +mode\&. You will not be prompted to answer any questions\&. +.RE +.PP +\fB\-c\fR, \fB\-\-configuration\fR \fIPATH\fR +.RS 4 +Provide an alternate +\fIPATH\fR +to an +\fIinstaller\&.cfg\&.yml\fR +file\&. +.RE +.PP +\fB\-a\fR \fIDIRECTORY\fR, \fB\-\-ansible\-playbook\-directory\fR \fIDIRECTORY\fR +.RS 4 +Manually set the +\fIDIRECTORY\fR +in which to look for Ansible playbooks\&. +.RE +.PP +\fB\-\-ansible\-log\-path\fR \fIPATH\fR +.RS 4 +Specify the +\fIPATH\fR +of the directory in which to save Ansible logs\&. +.RE +.PP +\fB\-v\fR, \fB\-\-verbose\fR +.RS 4 +Run the installer with more verbosity\&. +.RE +.PP +\fB\-d\fR, \fB\-\-debug\fR +.RS 4 +Enable installer debugging\&. Logs are saved in +\fI/tmp/installer\&.txt\fR\&. +.RE +.PP +\fB\-h\fR, \fB\-\-help\fR +.RS 4 +Show the usage help and exit\&. +.RE +.SH "COMMANDS" +.sp +\fBatomic\-openshift\-installer\fR has three modes of operation: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fBinstall\fR +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fBuninstall\fR +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fBupgrade\fR +.RE +.sp +The options specific to each command are described in the following sections\&. +.SH "INSTALL" +.sp +The \fBinstall\fR command will guide you through steps required to install an OCP cluster\&. After all of the required information has been collected (target hosts, storage options, high\-availability), the installation will begin\&. +.PP +\fB\-f\fR, \fB\-\-force\fR +.RS 4 +Forces an installation\&. This means that hosts with existing installations will be reinstalled if required\&. +.RE +.PP +\fB\-\-gen\-inventory\fR +.RS 4 +Generate an Ansible inventory file and exit\&. The default location for the inventory file is +\fI~/\&.config/openshift/hosts\fR\&. +.RE +.SH "UNINSTALL" +.sp +The \fBuninstall\fR command will uninstall OCP from your target hosts\&. This command has no additional options\&. +.SH "UPGRADE" +.sp +The \fBupgrade\fR command will upgrade a cluster of hosts to a newer version of OCP\&. +.PP +\fB\-l\fR, \fB\-\-latest\-minor\fR +.RS 4 +Upgrade to the latest minor version\&. For example, if you are running version +\fB3\&.2\&.1\fR +then this could upgrade you to +\fB3\&.2\&.2\fR\&. +.RE +.PP +\fB\-n\fR, \fB\-\-next\-major\fR +.RS 4 +Upgrade to the latest major version\&. For example, if you are running version +\fB3\&.2\fR +then this could upgrade you to +\fB3\&.3\fR\&. +.RE +.SH "FILES" +.sp +\fB~/\&.config/openshift/installer\&.cfg\&.yml\fR \(em Installer configuration file\&. Can be used to generate an inventory later or start an unattended installation\&. +.sp +\fB~/\&.config/openshift/hosts\fR \(em Generated Ansible inventory file\&. Used to run the Ansible playbooks for install, uninstall, and upgrades\&. +.sp +\fB/tmp/ansible\&.log\fR \(em The default location of the ansible log file\&. +.sp +\fB/tmp/installer\&.txt\fR \(em The location of the log file for debugging the installer\&. +.SH "AUTHOR" +.sp +Red Hat OpenShift Productization team +.sp +For a complete list of contributors, please visit the GitHub charts page\&. +.SH "COPYRIGHT" +.sp +Copyright \(co 2016 Red Hat, Inc\&. +.sp +\fBatomic\-openshift\-installer\fR is released under the terms of the ASL 2\&.0 license\&. +.SH "SEE ALSO" +.sp +\fBansible\fR(1), \fBansible\-playbook\fR(1) +.sp +\fBThe openshift\-ansible GitHub Project\fR \(em https://github\&.com/openshift/openshift\-ansible/ +.sp +\fBThe atomic\-openshift\-installer Documentation\fR \(em https://docs\&.openshift\&.com/container\-platform/3\&.3/install_config/install/quick_install\&.html diff --git a/utils/docs/man/man1/atomic-openshift-installer.1.asciidoc.in b/utils/docs/man/man1/atomic-openshift-installer.1.asciidoc.in new file mode 100644 index 000000000..64e5d14a3 --- /dev/null +++ b/utils/docs/man/man1/atomic-openshift-installer.1.asciidoc.in @@ -0,0 +1,167 @@ +atomic-openshift-installer(1) +============================= +:man source: atomic-openshift-utils +:man version: %VERSION% +:man manual: atomic-openshift-installer + + +NAME +---- +atomic-openshift-installer - Interactive OpenShift Container Platform (OCP) installer + + +SYNOPSIS +-------- +atomic-openshift-installer [OPTIONS] COMMAND [OPTS] + + +DESCRIPTION +----------- + +**atomic-openshift-installer** makes the process for installing OCP +easier by interactively gathering the data needed to run on each +host. It can also be run in unattended mode if provided with a +configuration file. + + +OPTIONS +------- + +The following options are common to all commands. + +*-u*, *--unattended*:: + +Run installer in **unattended** mode. You will not be prompted to +answer any questions. + + +*-c*, *--configuration* 'PATH':: + +Provide an alternate 'PATH' to an 'installer.cfg.yml' file. + + +*-a* 'DIRECTORY', *--ansible-playbook-directory* 'DIRECTORY':: + +Manually set the 'DIRECTORY' in which to look for Ansible playbooks. + + +*--ansible-log-path* 'PATH':: + +Specify the 'PATH' of the directory in which to save Ansible logs. + + +*-v*, *--verbose*:: + +Run the installer with more verbosity. + + +*-d*, *--debug*:: + +Enable installer debugging. Logs are saved in '/tmp/installer.txt'. + + +*-h*, *--help*:: + +Show the usage help and exit. + + +COMMANDS +-------- + +**atomic-openshift-installer** has three modes of operation: + +* **install** +* **uninstall** +* **upgrade** + +The options specific to each command are described in the following +sections. + + + +INSTALL +------- + +The **install** command will guide you through steps required to +install an OCP cluster. After all of the required information has been +collected (target hosts, storage options, high-availability), the +installation will begin. + +*-f*, *--force*:: + +Forces an installation. This means that hosts with existing +installations will be reinstalled if required. + +*--gen-inventory*:: + +Generate an Ansible inventory file and exit. The default location for +the inventory file is '~/.config/openshift/hosts'. + + +UNINSTALL +--------- + +The **uninstall** command will uninstall OCP from your target +hosts. This command has no additional options. + + +UPGRADE +------- + +The **upgrade** command will upgrade a cluster of hosts to a newer +version of OCP. + +*-l*, *--latest-minor*:: + +Upgrade to the latest minor version. For example, if you are running +version **3.2.1** then this could upgrade you to **3.2.2**. + +*-n*, *--next-major*:: + +Upgrade to the latest major version. For example, if you are running +version **3.2** then this could upgrade you to **3.3**. + + + +FILES +----- + +*~/.config/openshift/installer.cfg.yml* -- Installer configuration + file. Can be used to generate an inventory later or start an + unattended installation. + +*~/.config/openshift/hosts* -- Generated Ansible inventory file. Used + to run the Ansible playbooks for install, uninstall, and upgrades. + +*/tmp/ansible.log* -- The default location of the ansible log file. + +*/tmp/installer.txt* -- The location of the log file for debugging the + installer. + + +AUTHOR +------ + +Red Hat OpenShift Productization team + +For a complete list of contributors, please visit the GitHub charts +page. + + + +COPYRIGHT +--------- +Copyright © 2016 Red Hat, Inc. + +**atomic-openshift-installer** is released under the terms of the ASL +2.0 license. + + + +SEE ALSO +-------- +*ansible*(1), *ansible-playbook*(1) + +*The openshift-ansible GitHub Project* -- <https://github.com/openshift/openshift-ansible/> + +*The atomic-openshift-installer Documentation* -- <https://docs.openshift.com/container-platform/3.3/install_config/install/quick_install.html> diff --git a/utils/etc/ansible-quiet.cfg b/utils/etc/ansible-quiet.cfg new file mode 100644 index 000000000..0eb0efa49 --- /dev/null +++ b/utils/etc/ansible-quiet.cfg @@ -0,0 +1,33 @@ +# config file for ansible -- http://ansible.com/ +# ============================================== + +# This config file provides examples for running +# the OpenShift playbooks with the provided +# inventory scripts. Only global defaults are +# left uncommented + +[defaults] +# Add the roles directory to the roles path +roles_path = roles/ + +# Set the log_path +log_path = /tmp/ansible.log + +forks = 10 +host_key_checking = False +nocows = 1 + +retry_files_enabled = False + +deprecation_warnings=False + +# Need to handle: +# inventory - derive from OO_ANSIBLE_DIRECTORY env var +# callback_plugins - derive from pkg_resource.resource_filename +# private_key_file - prompt if missing +# remote_tmp - set if provided by user (cli) +# ssh_args - set if provided by user (cli) +# control_path + +stdout_callback = openshift_quick_installer +callback_plugins = /usr/share/ansible_plugins/callback_plugins diff --git a/utils/etc/ansible.cfg b/utils/etc/ansible.cfg index a53ab6cb1..3425e7e62 100644 --- a/utils/etc/ansible.cfg +++ b/utils/etc/ansible.cfg @@ -19,10 +19,12 @@ nocows = 1 retry_files_enabled = False +deprecation_warnings = False + # Need to handle: # inventory - derive from OO_ANSIBLE_DIRECTORY env var # callback_plugins - derive from pkg_resource.resource_filename # private_key_file - prompt if missing # remote_tmp - set if provided by user (cli) # ssh_args - set if provided by user (cli) -# control_path
\ No newline at end of file +# control_path diff --git a/utils/setup.py b/utils/setup.py index eac1b4b2e..563897bb1 100644 --- a/utils/setup.py +++ b/utils/setup.py @@ -62,7 +62,7 @@ setup( # installed, specify them here. If using Python 2.6 or less, then these # have to be included in MANIFEST.in as well. package_data={ - 'ooinstall': ['ansible.cfg', 'ansible_plugins/*'], + 'ooinstall': ['ansible.cfg', 'ansible-quiet.cfg', 'ansible_plugins/*'], }, # Although 'package_data' is the preferred approach, in some case you may diff --git a/utils/src/MANIFEST.in b/utils/src/MANIFEST.in index d4153e738..216f57e9c 100644 --- a/utils/src/MANIFEST.in +++ b/utils/src/MANIFEST.in @@ -7,3 +7,4 @@ include DESCRIPTION.rst # it's already declared in setup.py include ooinstall/* include ansible.cfg +include ansible-quiet.cfg diff --git a/utils/src/ooinstall/cli_installer.py b/utils/src/ooinstall/cli_installer.py index dd17fbf89..347ae7ec9 100644 --- a/utils/src/ooinstall/cli_installer.py +++ b/utils/src/ooinstall/cli_installer.py @@ -25,6 +25,7 @@ installer_file_handler.setLevel(logging.DEBUG) installer_log.addHandler(installer_file_handler) DEFAULT_ANSIBLE_CONFIG = '/usr/share/atomic-openshift-utils/ansible.cfg' +QUIET_ANSIBLE_CONFIG = '/usr/share/atomic-openshift-utils/ansible-quiet.cfg' DEFAULT_PLAYBOOK_DIR = '/usr/share/ansible/openshift-ansible/' UPGRADE_MAPPINGS = { @@ -483,8 +484,7 @@ def get_variant_and_version(multi_master=False): i = 1 combos = get_variant_version_combos() for (variant, version) in combos: - message = "%s\n(%s) %s %s" % (message, i, variant.description, - version.name) + message = "%s\n(%s) %s" % (message, i, variant.description) i = i + 1 message = "%s\n" % message @@ -751,7 +751,9 @@ def get_hosts_to_run_on(oo_cfg, callback_facts, unattended, force, verbose): hosts_to_run_on.remove(host) # Handle the cases where we know about uninstalled systems - if len(uninstalled_hosts) > 0: + # TODO: This logic is getting hard to understand. + # we should revise all this to be cleaner. + if not force and len(uninstalled_hosts) > 0: for uninstalled_host in uninstalled_hosts: click.echo("{} is currently uninstalled".format(uninstalled_host)) # Fall through @@ -791,7 +793,7 @@ def set_infra_nodes(hosts): if all(host.is_master() for host in hosts): infra_list = hosts else: - nodes_list = [host for host in hosts if host.is_node()] + nodes_list = [host for host in hosts if host.is_schedulable_node(hosts)] infra_list = nodes_list[:2] for host in infra_list: @@ -816,12 +818,6 @@ def set_infra_nodes(hosts): # callback=validate_ansible_dir, default=DEFAULT_PLAYBOOK_DIR, envvar='OO_ANSIBLE_PLAYBOOK_DIRECTORY') -@click.option('--ansible-config', - type=click.Path(file_okay=True, - dir_okay=False, - writable=True, - readable=True), - default=None) @click.option('--ansible-log-path', type=click.Path(file_okay=True, dir_okay=False, @@ -837,7 +833,7 @@ def set_infra_nodes(hosts): # pylint: disable=too-many-arguments # pylint: disable=line-too-long # Main CLI entrypoint, not much we can do about too many arguments. -def cli(ctx, unattended, configuration, ansible_playbook_directory, ansible_config, ansible_log_path, verbose, debug): +def cli(ctx, unattended, configuration, ansible_playbook_directory, ansible_log_path, verbose, debug): """ atomic-openshift-installer makes the process for installing OSE or AEP easier by interactively gathering the data needed to run on each host. @@ -856,7 +852,6 @@ def cli(ctx, unattended, configuration, ansible_playbook_directory, ansible_conf ctx.obj = {} ctx.obj['unattended'] = unattended ctx.obj['configuration'] = configuration - ctx.obj['ansible_config'] = ansible_config ctx.obj['ansible_log_path'] = ansible_log_path ctx.obj['verbose'] = verbose @@ -877,13 +872,13 @@ def cli(ctx, unattended, configuration, ansible_playbook_directory, ansible_conf oo_cfg.ansible_playbook_directory = ansible_playbook_directory ctx.obj['ansible_playbook_directory'] = ansible_playbook_directory - if ctx.obj['ansible_config']: - oo_cfg.settings['ansible_config'] = ctx.obj['ansible_config'] - elif 'ansible_config' not in oo_cfg.settings and \ - os.path.exists(DEFAULT_ANSIBLE_CONFIG): + if os.path.exists(DEFAULT_ANSIBLE_CONFIG): # If we're installed by RPM this file should exist and we can use it as our default: oo_cfg.settings['ansible_config'] = DEFAULT_ANSIBLE_CONFIG + if os.path.exists(QUIET_ANSIBLE_CONFIG): + oo_cfg.settings['ansible_quiet_config'] = QUIET_ANSIBLE_CONFIG + oo_cfg.settings['ansible_log_path'] = ctx.obj['ansible_log_path'] ctx.obj['oo_cfg'] = oo_cfg @@ -1085,7 +1080,6 @@ more: http://docs.openshift.com/enterprise/latest/admin_guide/overview.html """ click.echo(message) - click.pause() cli.add_command(install) cli.add_command(upgrade) diff --git a/utils/src/ooinstall/oo_config.py b/utils/src/ooinstall/oo_config.py index 393b36f6f..697ac9c08 100644 --- a/utils/src/ooinstall/oo_config.py +++ b/utils/src/ooinstall/oo_config.py @@ -12,7 +12,6 @@ installer_log = logging.getLogger('installer') CONFIG_PERSIST_SETTINGS = [ 'ansible_ssh_user', 'ansible_callback_facts_yaml', - 'ansible_config', 'ansible_inventory_path', 'ansible_log_path', 'deployment', @@ -27,6 +26,19 @@ DEPLOYMENT_VARIABLES_BLACKLIST = [ 'roles', ] +HOST_VARIABLES_BLACKLIST = [ + 'ip', + 'public_ip', + 'hostname', + 'public_hostname', + 'node_labels', + 'containerized', + 'preconfigured', + 'schedulable', + 'other_variables', + 'roles', +] + DEFAULT_REQUIRED_FACTS = ['ip', 'public_ip', 'hostname', 'public_hostname'] PRECONFIGURED_REQUIRED_FACTS = ['hostname', 'public_hostname'] @@ -67,7 +79,7 @@ class Host(object): self.containerized = kwargs.get('containerized', False) self.node_labels = kwargs.get('node_labels', '') - # allowable roles: master, node, etcd, storage, master_lb, new + # allowable roles: master, node, etcd, storage, master_lb self.roles = kwargs.get('roles', []) self.other_variables = kwargs.get('other_variables', {}) @@ -87,11 +99,13 @@ class Host(object): d = {} for prop in ['ip', 'hostname', 'public_ip', 'public_hostname', 'connect_to', - 'preconfigured', 'containerized', 'schedulable', 'roles', 'node_labels', - 'other_variables']: + 'preconfigured', 'containerized', 'schedulable', 'roles', 'node_labels', ]: # If the property is defined (not None or False), export it: if getattr(self, prop): d[prop] = getattr(self, prop) + for variable, value in self.other_variables.iteritems(): + d[variable] = value + return d def is_master(self): @@ -203,7 +217,6 @@ class OOConfig(object): role_list = loaded_config['deployment']['roles'] except KeyError as e: print_read_config_error("No such key: {}".format(e), self.config_path) - print "Error loading config, required key missing: {}".format(e) sys.exit(0) for setting in CONFIG_PERSIST_SETTINGS: @@ -238,6 +251,10 @@ class OOConfig(object): # Parse the hosts into DTO objects: for host in host_list: + host['other_variables'] = {} + for variable, value in host.iteritems(): + if variable not in HOST_VARIABLES_BLACKLIST: + host['other_variables'][variable] = value self.deployment.hosts.append(Host(**host)) # Parse the roles into Objects @@ -308,6 +325,12 @@ class OOConfig(object): if 'ansible_plugins_directory' not in self.settings: self.settings['ansible_plugins_directory'] = \ resource_filename(__name__, 'ansible_plugins') + installer_log.debug("We think the ansible plugins directory should be: %s (it is not already set)", + self.settings['ansible_plugins_directory']) + else: + installer_log.debug("The ansible plugins directory is already set: %s", + self.settings['ansible_plugins_directory']) + if 'version' not in self.settings: self.settings['version'] = 'v2' diff --git a/utils/src/ooinstall/openshift_ansible.py b/utils/src/ooinstall/openshift_ansible.py index 75d26c10a..80a79a6d2 100644 --- a/utils/src/ooinstall/openshift_ansible.py +++ b/utils/src/ooinstall/openshift_ansible.py @@ -7,6 +7,7 @@ import os import logging import yaml from ooinstall.variants import find_variant +from ooinstall.utils import debug_env installer_log = logging.getLogger('installer') @@ -30,6 +31,14 @@ VARIABLES_MAP = { 'proxy_exclude_hosts': 'openshift_no_proxy', } +HOST_VARIABLES_MAP = { + 'ip': 'openshift_ip', + 'public_ip': 'openshift_public_ip', + 'hostname': 'openshift_hostname', + 'public_hostname': 'openshift_public_hostname', + 'containerized': 'containerized', +} + def set_config(cfg): global CFG @@ -175,7 +184,6 @@ def write_proxy_settings(base_inventory): pass -# pylint: disable=too-many-branches def write_host(host, role, inventory, schedulable=None): global CFG @@ -183,22 +191,16 @@ def write_host(host, role, inventory, schedulable=None): return facts = '' - if host.ip: - facts += ' openshift_ip={}'.format(host.ip) - if host.public_ip: - facts += ' openshift_public_ip={}'.format(host.public_ip) - if host.hostname: - facts += ' openshift_hostname={}'.format(host.hostname) - if host.public_hostname: - facts += ' openshift_public_hostname={}'.format(host.public_hostname) - if host.containerized: - facts += ' containerized={}'.format(host.containerized) + for prop in HOST_VARIABLES_MAP: + if getattr(host, prop): + facts += ' {}={}'.format(HOST_VARIABLES_MAP.get(prop), getattr(host, prop)) + if host.other_variables: for variable, value in host.other_variables.iteritems(): facts += " {}={}".format(variable, value) - if host.node_labels: - if role == 'node': - facts += ' openshift_node_labels="{}"'.format(host.node_labels) + + if host.node_labels and role == 'node': + facts += ' openshift_node_labels="{}"'.format(host.node_labels) # Distinguish between three states, no schedulability specified (use default), # explicitly set to True, or explicitly set to False: @@ -225,6 +227,9 @@ def load_system_facts(inventory_file, os_facts_path, env_vars, verbose=False): Retrieves system facts from the remote systems. """ installer_log.debug("Inside load_system_facts") + installer_log.debug("load_system_facts will run with Ansible/Openshift environment variables:") + debug_env(env_vars) + FNULL = open(os.devnull, 'w') args = ['ansible-playbook', '-v'] if verbose \ else ['ansible-playbook'] @@ -232,6 +237,8 @@ def load_system_facts(inventory_file, os_facts_path, env_vars, verbose=False): '--inventory-file={}'.format(inventory_file), os_facts_path]) installer_log.debug("Going to subprocess out to ansible now with these args: %s", ' '.join(args)) + installer_log.debug("Subprocess will run with Ansible/Openshift environment variables:") + debug_env(env_vars) status = subprocess.call(args, env=env_vars, stdout=FNULL) if status != 0: installer_log.debug("Exit status from subprocess was not 0") @@ -280,17 +287,24 @@ def run_main_playbook(inventory_file, hosts, hosts_to_run_on, verbose=False): facts_env = os.environ.copy() if 'ansible_log_path' in CFG.settings: facts_env['ANSIBLE_LOG_PATH'] = CFG.settings['ansible_log_path'] - if 'ansible_config' in CFG.settings: - facts_env['ANSIBLE_CONFIG'] = CFG.settings['ansible_config'] + + # override the ansible config for our main playbook run + if 'ansible_quiet_config' in CFG.settings: + facts_env['ANSIBLE_CONFIG'] = CFG.settings['ansible_quiet_config'] + return run_ansible(main_playbook_path, inventory_file, facts_env, verbose) def run_ansible(playbook, inventory, env_vars, verbose=False): + installer_log.debug("run_ansible will run with Ansible/Openshift environment variables:") + debug_env(env_vars) + args = ['ansible-playbook', '-v'] if verbose \ else ['ansible-playbook'] args.extend([ '--inventory-file={}'.format(inventory), playbook]) + installer_log.debug("Going to subprocess out to ansible now with these args: %s", ' '.join(args)) return subprocess.call(args, env=env_vars) diff --git a/utils/src/ooinstall/utils.py b/utils/src/ooinstall/utils.py new file mode 100644 index 000000000..eb27a57e4 --- /dev/null +++ b/utils/src/ooinstall/utils.py @@ -0,0 +1,10 @@ +import logging + +installer_log = logging.getLogger('installer') + + +def debug_env(env): + for k in sorted(env.keys()): + if k.startswith("OPENSHIFT") or k.startswith("ANSIBLE") or k.startswith("OO"): + installer_log.debug("{key}: {value}".format( + key=k, value=env[k])) diff --git a/utils/test/cli_installer_tests.py b/utils/test/cli_installer_tests.py index 6d9d443ff..34392777b 100644 --- a/utils/test/cli_installer_tests.py +++ b/utils/test/cli_installer_tests.py @@ -599,82 +599,96 @@ class UnattendedCliTests(OOCliFixture): self.assertEquals('openshift-enterprise', inventory.get('OSEv3:vars', 'deployment_type')) - @patch('ooinstall.openshift_ansible.run_ansible') - @patch('ooinstall.openshift_ansible.load_system_facts') - def test_no_ansible_config_specified(self, load_facts_mock, run_ansible_mock): - load_facts_mock.return_value = (MOCK_FACTS, 0) - run_ansible_mock.return_value = 0 - - config = SAMPLE_CONFIG % 'openshift-enterprise' - - self._ansible_config_test(load_facts_mock, run_ansible_mock, - config, None, None) - - @patch('ooinstall.openshift_ansible.run_ansible') - @patch('ooinstall.openshift_ansible.load_system_facts') - def test_ansible_config_specified_cli(self, load_facts_mock, run_ansible_mock): - load_facts_mock.return_value = (MOCK_FACTS, 0) - run_ansible_mock.return_value = 0 - - config = SAMPLE_CONFIG % 'openshift-enterprise' - ansible_config = os.path.join(self.work_dir, 'ansible.cfg') - - self._ansible_config_test(load_facts_mock, run_ansible_mock, - config, ansible_config, ansible_config) - - @patch('ooinstall.openshift_ansible.run_ansible') - @patch('ooinstall.openshift_ansible.load_system_facts') - def test_ansible_config_specified_in_installer_config(self, - load_facts_mock, run_ansible_mock): - - load_facts_mock.return_value = (MOCK_FACTS, 0) - run_ansible_mock.return_value = 0 - - ansible_config = os.path.join(self.work_dir, 'ansible.cfg') - config = SAMPLE_CONFIG % 'openshift-enterprise' - config = "%s\nansible_config: %s" % (config, ansible_config) - self._ansible_config_test(load_facts_mock, run_ansible_mock, - config, None, ansible_config) - - #pylint: disable=too-many-arguments - # This method allows for drastically simpler tests to write, and the args - # are all useful. - def _ansible_config_test(self, load_facts_mock, run_ansible_mock, - installer_config, ansible_config_cli=None, expected_result=None): - """ - Utility method for testing the ways you can specify the ansible config. - """ - - load_facts_mock.return_value = (MOCK_FACTS, 0) - run_ansible_mock.return_value = 0 - - config_file = self.write_config(os.path.join(self.work_dir, - 'ooinstall.conf'), installer_config) - - self.cli_args.extend(["-c", config_file]) - if ansible_config_cli: - self.cli_args.extend(["--ansible-config", ansible_config_cli]) - self.cli_args.append("install") - result = self.runner.invoke(cli.cli, self.cli_args) - self.assert_result(result, 0) - - # Test the env vars for facts playbook: - facts_env_vars = load_facts_mock.call_args[0][2] - if expected_result: - self.assertEquals(expected_result, facts_env_vars['ANSIBLE_CONFIG']) - else: - # If user running test has rpm installed, this might be set to default: - self.assertTrue('ANSIBLE_CONFIG' not in facts_env_vars or - facts_env_vars['ANSIBLE_CONFIG'] == cli.DEFAULT_ANSIBLE_CONFIG) - - # Test the env vars for main playbook: - env_vars = run_ansible_mock.call_args[0][2] - if expected_result: - self.assertEquals(expected_result, env_vars['ANSIBLE_CONFIG']) - else: - # If user running test has rpm installed, this might be set to default: - self.assertTrue('ANSIBLE_CONFIG' not in env_vars or - env_vars['ANSIBLE_CONFIG'] == cli.DEFAULT_ANSIBLE_CONFIG) + # 2016-09-26 - tbielawa - COMMENTING OUT these tests FOR NOW while + # we wait to see if anyone notices that we took away their ability + # to set the ansible_config parameter in the command line options + # and in the installer config file. + # + # We have removed the ability to set the ansible config file + # manually so that our new quieter output mode is the default and + # only output mode. + # + # RE: https://trello.com/c/DSwwizwP - atomic-openshift-install + # should only output relevant information. + + # @patch('ooinstall.openshift_ansible.run_ansible') + # @patch('ooinstall.openshift_ansible.load_system_facts') + # def test_no_ansible_config_specified(self, load_facts_mock, run_ansible_mock): + # load_facts_mock.return_value = (MOCK_FACTS, 0) + # run_ansible_mock.return_value = 0 + + # config = SAMPLE_CONFIG % 'openshift-enterprise' + + # self._ansible_config_test(load_facts_mock, run_ansible_mock, + # config, None, None) + + # @patch('ooinstall.openshift_ansible.run_ansible') + # @patch('ooinstall.openshift_ansible.load_system_facts') + # def test_ansible_config_specified_cli(self, load_facts_mock, run_ansible_mock): + # load_facts_mock.return_value = (MOCK_FACTS, 0) + # run_ansible_mock.return_value = 0 + + # config = SAMPLE_CONFIG % 'openshift-enterprise' + # ansible_config = os.path.join(self.work_dir, 'ansible.cfg') + + # self._ansible_config_test(load_facts_mock, run_ansible_mock, + # config, ansible_config, ansible_config) + + # @patch('ooinstall.openshift_ansible.run_ansible') + # @patch('ooinstall.openshift_ansible.load_system_facts') + # def test_ansible_config_specified_in_installer_config(self, + # load_facts_mock, run_ansible_mock): + + # load_facts_mock.return_value = (MOCK_FACTS, 0) + # run_ansible_mock.return_value = 0 + + # ansible_config = os.path.join(self.work_dir, 'ansible.cfg') + # config = SAMPLE_CONFIG % 'openshift-enterprise' + # config = "%s\nansible_config: %s" % (config, ansible_config) + # self._ansible_config_test(load_facts_mock, run_ansible_mock, + # config, None, ansible_config) + + # #pylint: disable=too-many-arguments + # # This method allows for drastically simpler tests to write, and the args + # # are all useful. + # def _ansible_config_test(self, load_facts_mock, run_ansible_mock, + # installer_config, ansible_config_cli=None, expected_result=None): + # """ + # Utility method for testing the ways you can specify the ansible config. + # """ + + # load_facts_mock.return_value = (MOCK_FACTS, 0) + # run_ansible_mock.return_value = 0 + + # config_file = self.write_config(os.path.join(self.work_dir, + # 'ooinstall.conf'), installer_config) + + # self.cli_args.extend(["-c", config_file]) + # if ansible_config_cli: + # self.cli_args.extend(["--ansible-config", ansible_config_cli]) + # self.cli_args.append("install") + # result = self.runner.invoke(cli.cli, self.cli_args) + # self.assert_result(result, 0) + + # # Test the env vars for facts playbook: + # facts_env_vars = load_facts_mock.call_args[0][2] + # if expected_result: + # self.assertEquals(expected_result, facts_env_vars['ANSIBLE_CONFIG']) + # else: + # # If user running test has rpm installed, this might be set to default: + # self.assertTrue('ANSIBLE_CONFIG' not in facts_env_vars or + # facts_env_vars['ANSIBLE_CONFIG'] == cli.DEFAULT_ANSIBLE_CONFIG) + + # # Test the env vars for main playbook: + # env_vars = run_ansible_mock.call_args[0][2] + # if expected_result: + # self.assertEquals(expected_result, env_vars['ANSIBLE_CONFIG']) + # else: + # # If user running test has rpm installed, this might be set to default: + # # + # # By default we will use the quiet config + # self.assertTrue('ANSIBLE_CONFIG' not in env_vars or + # env_vars['ANSIBLE_CONFIG'] == cli.QUIET_ANSIBLE_CONFIG) # unattended with bad config file and no installed hosts (without --force) @patch('ooinstall.openshift_ansible.run_main_playbook') diff --git a/utils/test/oo_config_tests.py b/utils/test/oo_config_tests.py index b5068cc14..56fd82408 100644 --- a/utils/test/oo_config_tests.py +++ b/utils/test/oo_config_tests.py @@ -2,6 +2,7 @@ # repo. We will work on these over time. # pylint: disable=bad-continuation,missing-docstring,no-self-use,invalid-name +import cStringIO import os import unittest import tempfile @@ -9,6 +10,7 @@ import shutil import yaml from ooinstall.oo_config import OOConfig, Host, OOConfigInvalidHostError +import ooinstall.openshift_ansible SAMPLE_CONFIG = """ variant: openshift-enterprise @@ -224,3 +226,81 @@ class HostTests(OOInstallFixture): 'public_hostname': 'a.example.com', } self.assertRaises(OOConfigInvalidHostError, Host, **yaml_props) + + def test_inventory_file_quotes_node_labels(self): + """Verify a host entry wraps openshift_node_labels value in double quotes""" + yaml_props = { + 'ip': '192.168.0.1', + 'hostname': 'a.example.com', + 'connect_to': 'a-private.example.com', + 'public_ip': '192.168.0.1', + 'public_hostname': 'a.example.com', + 'new_host': True, + 'roles': ['node'], + 'node_labels': { + 'region': 'infra' + }, + + } + + new_node = Host(**yaml_props) + inventory = cStringIO.StringIO() + # This is what the 'write_host' function generates. write_host + # has no return value, it just writes directly to the file + # 'inventory' which in this test-case is a StringIO object + ooinstall.openshift_ansible.write_host( + new_node, + 'node', + inventory, + schedulable=True) + # read the value of what was written to the inventory "file" + legacy_inventory_line = inventory.getvalue() + + # Given the `yaml_props` above we should see a line like this: + # openshift_node_labels="{'region': 'infra'}" + node_labels_expected = '''openshift_node_labels="{'region': 'infra'}"''' # Quotes around the hash + node_labels_bad = '''openshift_node_labels={'region': 'infra'}''' # No quotes around the hash + + # The good line is present in the written inventory line + self.assertIn(node_labels_expected, legacy_inventory_line) + # An unquoted version is not present + self.assertNotIn(node_labels_bad, legacy_inventory_line) + + + # def test_new_write_inventory_same_as_legacy(self): + # """Verify the original write_host function produces the same output as the new method""" + # yaml_props = { + # 'ip': '192.168.0.1', + # 'hostname': 'a.example.com', + # 'connect_to': 'a-private.example.com', + # 'public_ip': '192.168.0.1', + # 'public_hostname': 'a.example.com', + # 'new_host': True, + # 'roles': ['node'], + # 'other_variables': { + # 'zzz': 'last', + # 'foo': 'bar', + # 'aaa': 'first', + # }, + # } + + # new_node = Host(**yaml_props) + # inventory = cStringIO.StringIO() + + # # This is what the original 'write_host' function will + # # generate. write_host has no return value, it just writes + # # directly to the file 'inventory' which in this test-case is + # # a StringIO object + # ooinstall.openshift_ansible.write_host( + # new_node, + # 'node', + # inventory, + # schedulable=True) + # legacy_inventory_line = inventory.getvalue() + + # # This is what the new method in the Host class generates + # new_inventory_line = new_node.inventory_string('node', schedulable=True) + + # self.assertEqual( + # legacy_inventory_line, + # new_inventory_line) |