diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/src/ooinstall/cli_installer.py | 182 | ||||
-rw-r--r-- | utils/src/ooinstall/oo_config.py | 12 | ||||
-rw-r--r-- | utils/src/ooinstall/openshift_ansible.py | 64 | ||||
-rw-r--r-- | utils/src/ooinstall/variants.py | 3 | ||||
-rw-r--r-- | utils/test/cli_installer_tests.py | 244 | ||||
-rw-r--r-- | utils/test/fixture.py | 22 |
6 files changed, 406 insertions, 121 deletions
diff --git a/utils/src/ooinstall/cli_installer.py b/utils/src/ooinstall/cli_installer.py index ace834323..c896a74b4 100644 --- a/utils/src/ooinstall/cli_installer.py +++ b/utils/src/ooinstall/cli_installer.py @@ -11,6 +11,7 @@ from ooinstall import OOConfig from ooinstall.oo_config import OOConfigInvalidHostError from ooinstall.oo_config import Host from ooinstall.variants import find_variant, get_variant_version_combos +from distutils.version import LooseVersion DEFAULT_ANSIBLE_CONFIG = '/usr/share/atomic-openshift-utils/ansible.cfg' DEFAULT_PLAYBOOK_DIR = '/usr/share/ansible/openshift-ansible/' @@ -166,6 +167,9 @@ http://docs.openshift.com/enterprise/latest/architecture/infrastructure_componen if num_masters >= 3: collect_master_lb(hosts) + if not existing_env: + collect_storage_host(hosts) + return hosts @@ -202,8 +206,9 @@ Please add one more to proceed.""" elif len(masters) >= 3: ha_message = """ NOTE: Multiple Masters specified, this will be an HA deployment with a separate -etcd cluster. You will be prompted to provide the FQDN of a load balancer once -finished entering hosts.""" +etcd cluster. You will be prompted to provide the FQDN of a load balancer and +a host for storage once finished entering hosts. +""" click.echo(ha_message) dedicated_nodes_message = """ @@ -243,6 +248,8 @@ def print_host_summary(all_hosts, host): click.echo(" - Etcd Member") else: click.echo(" - Etcd (Embedded)") + if host.storage: + click.echo(" - Storage") def collect_master_lb(hosts): @@ -291,6 +298,48 @@ hostname. master_lb = Host(**host_props) hosts.append(master_lb) +def collect_storage_host(hosts): + """ + Get a valid host for storage from the user and append it to the list of + hosts. + """ + message = """ +Setting up High Availability Masters requires a storage host. Please provide a +host that will be configured as a Registry Storage. + +Note: Containerized storage hosts are not currently supported. +""" + click.echo(message) + host_props = {} + + first_master = next(host for host in hosts if host.master) + + hostname_or_ip = click.prompt('Enter hostname or IP address', + value_proc=validate_prompt_hostname, + default=first_master.connect_to) + existing, existing_host = is_host_already_node_or_master(hostname_or_ip, hosts) + if existing and existing_host.node: + existing_host.storage = True + else: + host_props['connect_to'] = hostname_or_ip + host_props['preconfigured'] = False + host_props['master'] = False + host_props['node'] = False + host_props['storage'] = True + storage = Host(**host_props) + hosts.append(storage) + +def is_host_already_node_or_master(hostname, hosts): + is_existing = False + existing_host = None + + for host in hosts: + if host.connect_to == hostname and (host.master or host.node): + is_existing = True + existing_host = host + + return is_existing, existing_host + def confirm_hosts_facts(oo_cfg, callback_facts): hosts = oo_cfg.hosts click.clear() @@ -330,11 +379,15 @@ Notes: for h in hosts: if h.preconfigured == True: continue - default_facts[h.connect_to] = {} - h.ip = callback_facts[h.connect_to]["common"]["ip"] - h.public_ip = callback_facts[h.connect_to]["common"]["public_ip"] - h.hostname = callback_facts[h.connect_to]["common"]["hostname"] - h.public_hostname = callback_facts[h.connect_to]["common"]["public_hostname"] + try: + default_facts[h.connect_to] = {} + h.ip = callback_facts[h.connect_to]["common"]["ip"] + h.public_ip = callback_facts[h.connect_to]["common"]["public_ip"] + h.hostname = callback_facts[h.connect_to]["common"]["hostname"] + h.public_hostname = callback_facts[h.connect_to]["common"]["public_hostname"] + except KeyError: + click.echo("Problem fetching facts from {}".format(h.connect_to)) + continue default_facts_lines.append(",".join([h.connect_to, h.ip, @@ -468,6 +521,34 @@ def error_if_missing_info(oo_cfg): if missing_info: sys.exit(1) +def get_proxy_hostnames_and_excludes(): + message = """ +If a proxy is needed to reach HTTP and HTTPS traffic please enter the name below. +This proxy will be configured by default for all processes needing to reach systems outside +the cluster. + +More advanced configuration is possible if using ansible directly: + +https://docs.openshift.com/enterprise/latest/install_config/http_proxies.html +""" + click.echo(message) + + message = "Specify your http proxy ? (ENTER for none)" + http_proxy_hostname = click.prompt(message, default='') + + message = "Specify your https proxy ? (ENTER for none)" + https_proxy_hostname = click.prompt(message, default=http_proxy_hostname) + + if http_proxy_hostname or https_proxy_hostname: + message = """ +All hosts in your openshift inventory will automatically be added to the NO_PROXY value. +Please provide any additional hosts to be added to NO_PROXY. (ENTER for none) +""" + proxy_excludes = click.prompt(message, default='') + else: + proxy_excludes = '' + + return http_proxy_hostname, https_proxy_hostname, proxy_excludes def get_missing_info_from_user(oo_cfg): """ Prompts the user for any information missing from the given configuration. """ @@ -514,6 +595,14 @@ https://docs.openshift.com/enterprise/latest/admin_guide/install/prerequisites.h oo_cfg.settings['master_routingconfig_subdomain'] = get_master_routingconfig_subdomain() click.clear() + if not oo_cfg.settings.get('openshift_http_proxy', None) and \ + LooseVersion(oo_cfg.settings.get('variant_version', '0.0')) >= LooseVersion('3.2'): + http_proxy, https_proxy, proxy_excludes = get_proxy_hostnames_and_excludes() + oo_cfg.settings['openshift_http_proxy'] = http_proxy + oo_cfg.settings['openshift_https_proxy'] = https_proxy + oo_cfg.settings['openshift_no_proxy'] = proxy_excludes + click.clear() + return oo_cfg @@ -618,7 +707,7 @@ def get_hosts_to_run_on(oo_cfg, callback_facts, unattended, force, verbose): openshift_ansible.set_config(oo_cfg) click.echo('Gathering information from hosts...') callback_facts, error = openshift_ansible.default_facts(oo_cfg.hosts, verbose) - if error: + if error or callback_facts is None: click.echo("There was a problem fetching the required information. See " \ "{} for details.".format(oo_cfg.settings['ansible_log_path'])) sys.exit(1) @@ -732,42 +821,67 @@ def uninstall(ctx): @click.command() +@click.option('--latest-minor', '-l', is_flag=True, default=False) +@click.option('--next-major', '-n', is_flag=True, default=False) @click.pass_context -def upgrade(ctx): +def upgrade(ctx, latest_minor, next_major): oo_cfg = ctx.obj['oo_cfg'] verbose = ctx.obj['verbose'] + upgrade_mappings = { + '3.0':{ + 'minor_version' :'3.0', + 'minor_playbook':'v3_0_minor/upgrade.yml', + 'major_version' :'3.1', + 'major_playbook':'v3_0_to_v3_1/upgrade.yml', + }, + '3.1':{ + 'minor_version' :'3.1', + 'minor_playbook':'v3_1_minor/upgrade.yml', + 'major_playbook':'v3_1_to_v3_2/upgrade.yml', + 'major_version' :'3.2', + } + } + if len(oo_cfg.hosts) == 0: click.echo("No hosts defined in: %s" % oo_cfg.config_path) sys.exit(1) old_variant = oo_cfg.settings['variant'] old_version = oo_cfg.settings['variant_version'] - + mapping = upgrade_mappings.get(old_version) message = """ This tool will help you upgrade your existing OpenShift installation. """ click.echo(message) - click.echo("Version {} found. Do you want to update to the latest version of {} " \ - "or migrate to the next major release?".format(old_version, old_version)) - resp = click.prompt("(1) Update to latest {} (2) Migrate to next relese".format(old_version)) - if resp == "2": - # TODO: Make this a lot more flexible - new_version = "3.1" + if not (latest_minor or next_major): + click.echo("Version {} found. Do you want to update to the latest version of {} " \ + "or migrate to the next major release?".format(old_version, old_version)) + response = click.prompt("(1) Update to latest {} " \ + "(2) Migrate to next release".format(old_version), + type=click.Choice(['1', '2']),) + if response == "1": + latest_minor = True + if response == "2": + next_major = True + + if next_major: + playbook = mapping['major_playbook'] + new_version = mapping['major_version'] # Update config to reflect the version we're targetting, we'll write # to disk once ansible completes successfully, not before. + oo_cfg.settings['variant_version'] = new_version if oo_cfg.settings['variant'] == 'enterprise': oo_cfg.settings['variant'] = 'openshift-enterprise' - version = find_variant(oo_cfg.settings['variant'])[1] - oo_cfg.settings['variant_version'] = version.name - else: - new_version = old_version + + if latest_minor: + playbook = mapping['minor_playbook'] + new_version = mapping['minor_version'] click.echo("Openshift will be upgraded from %s %s to %s %s on the following hosts:\n" % ( - old_variant, old_version, oo_cfg.settings['variant'], - oo_cfg.settings['variant_version'])) + old_variant, old_version, oo_cfg.settings['variant'], new_version)) for host in oo_cfg.hosts: click.echo(" * %s" % host.connect_to) @@ -778,7 +892,7 @@ def upgrade(ctx): click.echo("Upgrade cancelled.") sys.exit(0) - retcode = openshift_ansible.run_upgrade_playbook(old_version, new_version, verbose) + retcode = openshift_ansible.run_upgrade_playbook(playbook, verbose) if retcode > 0: click.echo("Errors encountered during upgrade, please check %s." % oo_cfg.settings['ansible_log_path']) @@ -789,8 +903,10 @@ def upgrade(ctx): @click.command() @click.option('--force', '-f', is_flag=True, default=False) +@click.option('--gen-inventory', is_flag=True, default=False, + help="Generate an ansible inventory file and exit.") @click.pass_context -def install(ctx, force): +def install(ctx, force, gen_inventory): oo_cfg = ctx.obj['oo_cfg'] verbose = ctx.obj['verbose'] @@ -805,7 +921,7 @@ def install(ctx, force): click.echo('Gathering information from hosts...') callback_facts, error = openshift_ansible.default_facts(oo_cfg.hosts, verbose) - if error: + if error or callback_facts is None: click.echo("There was a problem fetching the required information. " \ "Please see {} for details.".format(oo_cfg.settings['ansible_log_path'])) sys.exit(1) @@ -813,7 +929,6 @@ def install(ctx, force): hosts_to_run_on, callback_facts = get_hosts_to_run_on( oo_cfg, callback_facts, ctx.obj['unattended'], force, verbose) - click.echo('Writing config to: %s' % oo_cfg.config_path) # We already verified this is not the case for unattended installs, so this can # only trigger for live CLI users: @@ -823,7 +938,18 @@ def install(ctx, force): if len(oo_cfg.calc_missing_facts()) > 0: confirm_hosts_facts(oo_cfg, callback_facts) + # Write quick installer config file to disk: oo_cfg.save_to_disk() + # Write ansible inventory file to disk: + inventory_file = openshift_ansible.generate_inventory(hosts_to_run_on) + + click.echo() + click.echo('Wrote atomic-openshift-installer config: %s' % oo_cfg.config_path) + click.echo("Wrote ansible inventory: %s" % inventory_file) + click.echo() + + if gen_inventory: + sys.exit(0) click.echo('Ready to run installation process.') message = """ @@ -832,8 +958,8 @@ If changes are needed please edit the config file above and re-run. if not ctx.obj['unattended']: confirm_continue(message) - error = openshift_ansible.run_main_playbook(oo_cfg.hosts, - hosts_to_run_on, verbose) + error = openshift_ansible.run_main_playbook(inventory_file, oo_cfg.hosts, + hosts_to_run_on, verbose) if error: # The bootstrap script will print out the log location. message = """ diff --git a/utils/src/ooinstall/oo_config.py b/utils/src/ooinstall/oo_config.py index b1af21773..24dfbe013 100644 --- a/utils/src/ooinstall/oo_config.py +++ b/utils/src/ooinstall/oo_config.py @@ -11,6 +11,8 @@ PERSIST_SETTINGS = [ 'ansible_config', 'ansible_log_path', 'master_routingconfig_subdomain', + 'proxy', + 'proxy_exclude_hosts', 'variant', 'variant_version', 'version', @@ -50,13 +52,17 @@ class Host(object): # Should this host run as an HAProxy: self.master_lb = kwargs.get('master_lb', False) + # Should this host run as an HAProxy: + self.storage = kwargs.get('storage', False) + self.containerized = kwargs.get('containerized', False) if self.connect_to is None: raise OOConfigInvalidHostError("You must specify either an ip " \ "or hostname as 'connect_to'") - if self.master is False and self.node is False and self.master_lb is False: + if self.master is False and self.node is False and \ + self.master_lb is False and self.storage is False: raise OOConfigInvalidHostError( "You must specify each host as either a master or a node.") @@ -70,7 +76,7 @@ class Host(object): """ Used when exporting to yaml. """ d = {} for prop in ['ip', 'hostname', 'public_ip', 'public_hostname', - 'master', 'node', 'master_lb', 'containerized', + 'master', 'node', 'master_lb', 'storage', 'containerized', 'connect_to', 'preconfigured', 'new_host']: # If the property is defined (not None or False), export it: if getattr(self, prop): @@ -194,7 +200,7 @@ class OOConfig(object): self.settings['ansible_ssh_user'] = '' self.settings['ansible_inventory_path'] = \ - '{}/hosts'.format(self.settings['ansible_inventory_directory']) + '{}/hosts'.format(os.path.dirname(self.config_path)) # clean up any empty sets for setting in self.settings.keys(): diff --git a/utils/src/ooinstall/openshift_ansible.py b/utils/src/ooinstall/openshift_ansible.py index 3a135139b..97aee0b53 100644 --- a/utils/src/ooinstall/openshift_ansible.py +++ b/utils/src/ooinstall/openshift_ansible.py @@ -21,13 +21,14 @@ def generate_inventory(hosts): nodes = [host for host in hosts if host.node] new_nodes = [host for host in hosts if host.node and host.new_host] proxy = determine_proxy_configuration(hosts) + storage = determine_storage_configuration(hosts) multiple_masters = len(masters) > 1 scaleup = len(new_nodes) > 0 base_inventory_path = CFG.settings['ansible_inventory_path'] base_inventory = open(base_inventory_path, 'w') - write_inventory_children(base_inventory, multiple_masters, proxy, scaleup) + write_inventory_children(base_inventory, multiple_masters, proxy, storage, scaleup) write_inventory_vars(base_inventory, multiple_masters, proxy) @@ -37,10 +38,10 @@ def generate_inventory(hosts): base_inventory.write('deployment_type={}\n'.format(ver.ansible_key)) if 'OO_INSTALL_ADDITIONAL_REGISTRIES' in os.environ: - base_inventory.write('cli_docker_additional_registries={}\n' + base_inventory.write('openshift_docker_additional_registries={}\n' .format(os.environ['OO_INSTALL_ADDITIONAL_REGISTRIES'])) if 'OO_INSTALL_INSECURE_REGISTRIES' in os.environ: - base_inventory.write('cli_docker_insecure_registries={}\n' + base_inventory.write('openshift_docker_insecure_registries={}\n' .format(os.environ['OO_INSTALL_INSECURE_REGISTRIES'])) if 'OO_INSTALL_PUDDLE_REPO' in os.environ: # We have to double the '{' here for literals @@ -73,11 +74,16 @@ def generate_inventory(hosts): base_inventory.write('\n[lb]\n') write_host(proxy, base_inventory) + if scaleup: base_inventory.write('\n[new_nodes]\n') for node in new_nodes: write_host(node, base_inventory) + if storage: + base_inventory.write('\n[nfs]\n') + write_host(storage, base_inventory) + base_inventory.close() return base_inventory_path @@ -87,11 +93,15 @@ def determine_proxy_configuration(hosts): if proxy.hostname == None: proxy.hostname = proxy.connect_to proxy.public_hostname = proxy.connect_to - return proxy - return None + return proxy + +def determine_storage_configuration(hosts): + storage = next((host for host in hosts if host.storage), None) + + return storage -def write_inventory_children(base_inventory, multiple_masters, proxy, scaleup): +def write_inventory_children(base_inventory, multiple_masters, proxy, storage, scaleup): global CFG base_inventory.write('\n[OSEv3:children]\n') @@ -103,13 +113,15 @@ def write_inventory_children(base_inventory, multiple_masters, proxy, scaleup): base_inventory.write('etcd\n') if not getattr(proxy, 'preconfigured', True): base_inventory.write('lb\n') + if storage: + base_inventory.write('nfs\n') def write_inventory_vars(base_inventory, multiple_masters, proxy): global CFG base_inventory.write('\n[OSEv3:vars]\n') base_inventory.write('ansible_ssh_user={}\n'.format(CFG.settings['ansible_ssh_user'])) if CFG.settings['ansible_ssh_user'] != 'root': - base_inventory.write('ansible_become=true\n') + base_inventory.write('ansible_become=yes\n') if multiple_masters and proxy is not None: base_inventory.write('openshift_master_cluster_method=native\n') base_inventory.write("openshift_master_cluster_hostname={}\n".format(proxy.hostname)) @@ -117,8 +129,21 @@ def write_inventory_vars(base_inventory, multiple_masters, proxy): "openshift_master_cluster_public_hostname={}\n".format(proxy.public_hostname)) if CFG.settings.get('master_routingconfig_subdomain', False): base_inventory.write( - "openshift_master_default_subdomain={}\n".format(CFG.settings['master_routingconfig_subdomain'])) - + "openshift_master_default_subdomain={}\n".format( + CFG.settings['master_routingconfig_subdomain'])) + if CFG.settings.get('variant_version', None) == '3.1': + #base_inventory.write('openshift_image_tag=v{}\n'.format(CFG.settings.get('variant_version'))) + base_inventory.write('openshift_image_tag=v{}\n'.format('3.1.1.6')) + + if CFG.settings.get('openshift_http_proxy', ''): + base_inventory.write("openshift_http_proxy={}\n".format( + CFG.settings['openshift_http_proxy'])) + if CFG.settings.get('openshift_https_proxy', ''): + base_inventory.write("openshift_https_proxy={}\n".format( + CFG.settings['openshift_https_proxy'])) + if CFG.settings.get('openshift_no_proxy', ''): + base_inventory.write("openshift_no_proxy={}\n".format( + CFG.settings['openshift_no_proxy'])) def write_host(host, inventory, schedulable=None): @@ -155,7 +180,7 @@ def write_host(host, inventory, schedulable=None): if no_pwd_sudo == 1: print 'The atomic-openshift-installer requires sudo access without a password.' sys.exit(1) - facts += ' ansible_become=true' + facts += ' ansible_become=yes' inventory.write('{} {}\n'.format(host.connect_to, facts)) @@ -201,9 +226,8 @@ def default_facts(hosts, verbose=False): return load_system_facts(inventory_file, os_facts_path, facts_env, verbose) -def run_main_playbook(hosts, hosts_to_run_on, verbose=False): +def run_main_playbook(inventory_file, hosts, hosts_to_run_on, verbose=False): global CFG - inventory_file = generate_inventory(hosts_to_run_on) if len(hosts_to_run_on) != len(hosts): main_playbook_path = os.path.join(CFG.ansible_playbook_directory, 'playbooks/byo/openshift-node/scaleup.yml') @@ -239,18 +263,10 @@ def run_uninstall_playbook(verbose=False): return run_ansible(playbook, inventory_file, facts_env, verbose) -def run_upgrade_playbook(old_version, new_version, verbose=False): - # TODO: do not hardcode the upgrade playbook, add ability to select the - # right playbook depending on the type of upgrade. - old_version = old_version.replace('.', '_') - new_version = old_version.replace('.', '_') - if old_version == new_version: - playbook = os.path.join(CFG.settings['ansible_playbook_directory'], - 'playbooks/byo/openshift-cluster/upgrades/v{}_minor/upgrade.yml'.format(new_version)) - else: - playbook = os.path.join(CFG.settings['ansible_playbook_directory'], - 'playbooks/byo/openshift-cluster/upgrades/v{}_to_v{}/upgrade.yml'.format(old_version, - new_version)) +def run_upgrade_playbook(playbook, verbose=False): + playbook = os.path.join(CFG.settings['ansible_playbook_directory'], + 'playbooks/byo/openshift-cluster/upgrades/{}'.format(playbook)) + # TODO: Upgrade inventory for upgrade? inventory_file = generate_inventory(CFG.hosts) facts_env = os.environ.copy() diff --git a/utils/src/ooinstall/variants.py b/utils/src/ooinstall/variants.py index 571025543..9d98379bb 100644 --- a/utils/src/ooinstall/variants.py +++ b/utils/src/ooinstall/variants.py @@ -36,6 +36,7 @@ class Variant(object): # WARNING: Keep the versions ordered, most recent last: OSE = Variant('openshift-enterprise', 'OpenShift Enterprise', [ + Version('3.2', 'openshift-enterprise'), Version('3.1', 'openshift-enterprise'), Version('3.0', 'enterprise') ] @@ -43,6 +44,7 @@ OSE = Variant('openshift-enterprise', 'OpenShift Enterprise', AEP = Variant('atomic-enterprise', 'Atomic Enterprise Platform', [ + Version('3.2', 'atomic-enterprise'), Version('3.1', 'atomic-enterprise') ] ) @@ -74,4 +76,3 @@ def get_variant_version_combos(): for ver in variant.versions: combos.append((variant, ver)) return combos - diff --git a/utils/test/cli_installer_tests.py b/utils/test/cli_installer_tests.py index baab5d56f..66ed66660 100644 --- a/utils/test/cli_installer_tests.py +++ b/utils/test/cli_installer_tests.py @@ -1,6 +1,6 @@ # TODO: Temporarily disabled due to importing old code into openshift-ansible # repo. We will work on these over time. -# pylint: disable=bad-continuation,missing-docstring,no-self-use,invalid-name +# pylint: disable=bad-continuation,missing-docstring,no-self-use,invalid-name,too-many-lines import copy import os @@ -37,6 +37,14 @@ MOCK_FACTS = { 'public_hostname': 'node2.example.com' } }, + '10.1.0.1': { + 'common': { + 'ip': '10.1.0.1', + 'public_ip': '10.1.0.1', + 'hostname': 'storage-private.example.com', + 'public_hostname': 'storage.example.com' + } + }, } MOCK_FACTS_QUICKHA = { @@ -72,6 +80,14 @@ MOCK_FACTS_QUICKHA = { 'public_hostname': 'proxy.example.com' } }, + '10.1.0.1': { + 'common': { + 'ip': '10.1.0.1', + 'public_ip': '10.1.0.1', + 'hostname': 'storage-private.example.com', + 'public_hostname': 'storage.example.com' + } + }, } # Missing connect_to on some hosts: @@ -137,6 +153,12 @@ hosts: public_ip: 24.222.0.5 public_hostname: proxy.example.com master_lb: true + - connect_to: 10.1.0.1 + ip: 10.1.0.1 + hostname: storage-private.example.com + public_ip: 24.222.0.6 + public_hostname: storage.example.com + storage: true """ QUICKHA_2_MASTER_CONFIG = """ @@ -169,6 +191,12 @@ hosts: public_ip: 24.222.0.5 public_hostname: proxy.example.com master_lb: true + - connect_to: 10.1.0.1 + ip: 10.1.0.1 + hostname: storage-private.example.com + public_ip: 24.222.0.6 + public_hostname: storage.example.com + storage: true """ QUICKHA_CONFIG_REUSED_LB = """ @@ -197,6 +225,12 @@ hosts: public_hostname: node2.example.com node: true master: true + - connect_to: 10.1.0.1 + ip: 10.1.0.1 + hostname: storage-private.example.com + public_ip: 24.222.0.6 + public_hostname: storage.example.com + storage: true """ QUICKHA_CONFIG_NO_LB = """ @@ -224,6 +258,12 @@ hosts: public_hostname: node2.example.com node: true master: true + - connect_to: 10.1.0.1 + ip: 10.1.0.1 + hostname: storage-private.example.com + public_ip: 24.222.0.6 + public_hostname: storage.example.com + storage: true """ QUICKHA_CONFIG_PRECONFIGURED_LB = """ @@ -263,6 +303,12 @@ hosts: public_hostname: proxy.example.com master_lb: true preconfigured: true + - connect_to: 10.1.0.1 + ip: 10.1.0.1 + hostname: storage-private.example.com + public_ip: 24.222.0.6 + public_hostname: storage.example.com + storage: true """ class UnattendedCliTests(OOCliFixture): @@ -371,7 +417,7 @@ class UnattendedCliTests(OOCliFixture): self.assert_result(result, 0) load_facts_args = load_facts_mock.call_args[0] - self.assertEquals(os.path.join(self.work_dir, ".ansible/hosts"), + self.assertEquals(os.path.join(self.work_dir, "hosts"), load_facts_args[0]) self.assertEquals(os.path.join(self.work_dir, "playbooks/byo/openshift_facts.yml"), load_facts_args[1]) @@ -385,8 +431,8 @@ class UnattendedCliTests(OOCliFixture): env_vars['ANSIBLE_CONFIG'] == cli.DEFAULT_ANSIBLE_CONFIG) # Make sure we ran on the expected masters and nodes: - hosts = run_playbook_mock.call_args[0][0] - hosts_to_run_on = run_playbook_mock.call_args[0][1] + hosts = run_playbook_mock.call_args[0][1] + hosts_to_run_on = run_playbook_mock.call_args[0][2] self.assertEquals(3, len(hosts)) self.assertEquals(3, len(hosts_to_run_on)) @@ -409,7 +455,7 @@ class UnattendedCliTests(OOCliFixture): # Check the inventory file looks as we would expect: inventory = ConfigParser.ConfigParser(allow_no_value=True) - inventory.read(os.path.join(self.work_dir, '.ansible/hosts')) + inventory.read(os.path.join(self.work_dir, 'hosts')) self.assertEquals('bob', inventory.get('OSEv3:vars', 'ansible_ssh_user')) self.assertEquals('openshift-enterprise', @@ -448,11 +494,11 @@ class UnattendedCliTests(OOCliFixture): self.assertEquals('openshift-enterprise', written_config['variant']) # We didn't specify a version so the latest should have been assumed, # and written to disk: - self.assertEquals('3.1', written_config['variant_version']) + self.assertEquals('3.2', written_config['variant_version']) # Make sure the correct value was passed to ansible: inventory = ConfigParser.ConfigParser(allow_no_value=True) - inventory.read(os.path.join(self.work_dir, '.ansible/hosts')) + inventory.read(os.path.join(self.work_dir, 'hosts')) self.assertEquals('openshift-enterprise', inventory.get('OSEv3:vars', 'deployment_type')) @@ -480,7 +526,7 @@ class UnattendedCliTests(OOCliFixture): self.assertEquals('3.0', written_config['variant_version']) inventory = ConfigParser.ConfigParser(allow_no_value=True) - inventory.read(os.path.join(self.work_dir, '.ansible/hosts')) + inventory.read(os.path.join(self.work_dir, 'hosts')) self.assertEquals('enterprise', inventory.get('OSEv3:vars', 'deployment_type')) @@ -593,10 +639,10 @@ class UnattendedCliTests(OOCliFixture): self.assert_result(result, 0) # Make sure we ran on the expected masters and nodes: - hosts = run_playbook_mock.call_args[0][0] - hosts_to_run_on = run_playbook_mock.call_args[0][1] - self.assertEquals(5, len(hosts)) - self.assertEquals(5, len(hosts_to_run_on)) + hosts = run_playbook_mock.call_args[0][1] + hosts_to_run_on = run_playbook_mock.call_args[0][2] + self.assertEquals(6, len(hosts)) + self.assertEquals(6, len(hosts_to_run_on)) #unattended with two masters, one node, and haproxy @patch('ooinstall.openshift_ansible.run_main_playbook') @@ -663,10 +709,10 @@ class UnattendedCliTests(OOCliFixture): self.assert_result(result, 0) # Make sure we ran on the expected masters and nodes: - hosts = run_playbook_mock.call_args[0][0] - hosts_to_run_on = run_playbook_mock.call_args[0][1] - self.assertEquals(5, len(hosts)) - self.assertEquals(5, len(hosts_to_run_on)) + hosts = run_playbook_mock.call_args[0][1] + hosts_to_run_on = run_playbook_mock.call_args[0][2] + self.assertEquals(6, len(hosts)) + self.assertEquals(6, len(hosts_to_run_on)) class AttendedCliTests(OOCliFixture): @@ -688,26 +734,27 @@ class AttendedCliTests(OOCliFixture): ('10.0.0.3', False, False)], ssh_user='root', variant_num=1, - confirm_facts='y') + confirm_facts='y', + storage='10.1.0.1',) self.cli_args.append("install") result = self.runner.invoke(cli.cli, self.cli_args, input=cli_input) self.assert_result(result, 0) self._verify_load_facts(load_facts_mock) - self._verify_run_playbook(run_playbook_mock, 3, 3) + self._verify_run_playbook(run_playbook_mock, 4, 4) written_config = read_yaml(self.config_file) - self._verify_config_hosts(written_config, 3) + self._verify_config_hosts(written_config, 4) inventory = ConfigParser.ConfigParser(allow_no_value=True) - inventory.read(os.path.join(self.work_dir, '.ansible/hosts')) - self.assertEquals('False', - inventory.get('nodes', '10.0.0.1 openshift_schedulable')) - self.assertEquals(None, - inventory.get('nodes', '10.0.0.2')) - self.assertEquals(None, - inventory.get('nodes', '10.0.0.3')) + inventory.read(os.path.join(self.work_dir, 'hosts')) + self.assert_inventory_host_var(inventory, 'nodes', '10.0.0.1', + 'openshift_schedulable=False') + self.assert_inventory_host_var_unset(inventory, 'nodes', '10.0.0.2', + 'openshift_schedulable') + self.assert_inventory_host_var_unset(inventory, 'nodes', '10.0.0.3', + 'openshift_schedulable') # interactive with config file and some installed some uninstalled hosts @patch('ooinstall.openshift_ansible.run_main_playbook') @@ -730,7 +777,8 @@ class AttendedCliTests(OOCliFixture): add_nodes=[('10.0.0.3', False, False)], ssh_user='root', variant_num=1, - confirm_facts='y') + confirm_facts='y', + storage='10.0.0.1',) self.cli_args.append("install") result = self.runner.invoke(cli.cli, self.cli_args, @@ -781,7 +829,8 @@ class AttendedCliTests(OOCliFixture): ssh_user='root', variant_num=1, schedulable_masters_ok=True, - confirm_facts='y') + confirm_facts='y', + storage='10.0.0.1',) self._verify_get_hosts_to_run_on(mock_facts, load_facts_mock, run_playbook_mock, @@ -805,28 +854,29 @@ class AttendedCliTests(OOCliFixture): ssh_user='root', variant_num=1, confirm_facts='y', - master_lb=('10.0.0.5', False)) + master_lb=('10.0.0.5', False), + storage='10.1.0.1',) self.cli_args.append("install") result = self.runner.invoke(cli.cli, self.cli_args, input=cli_input) self.assert_result(result, 0) self._verify_load_facts(load_facts_mock) - self._verify_run_playbook(run_playbook_mock, 5, 5) + self._verify_run_playbook(run_playbook_mock, 6, 6) written_config = read_yaml(self.config_file) - self._verify_config_hosts(written_config, 5) + self._verify_config_hosts(written_config, 6) inventory = ConfigParser.ConfigParser(allow_no_value=True) - inventory.read(os.path.join(self.work_dir, '.ansible/hosts')) - self.assertEquals('False', - inventory.get('nodes', '10.0.0.1 openshift_schedulable')) - self.assertEquals('False', - inventory.get('nodes', '10.0.0.2 openshift_schedulable')) - self.assertEquals('False', - inventory.get('nodes', '10.0.0.3 openshift_schedulable')) - self.assertEquals(None, - inventory.get('nodes', '10.0.0.4')) + inventory.read(os.path.join(self.work_dir, 'hosts')) + self.assert_inventory_host_var(inventory, 'nodes', '10.0.0.1', + 'openshift_schedulable=False') + self.assert_inventory_host_var(inventory, 'nodes', '10.0.0.2', + 'openshift_schedulable=False') + self.assert_inventory_host_var(inventory, 'nodes', '10.0.0.3', + 'openshift_schedulable=False') + self.assert_inventory_host_var_unset(inventory, 'nodes', '10.0.0.4', + 'openshift_schedulable') self.assertTrue(inventory.has_section('etcd')) self.assertEquals(3, len(inventory.items('etcd'))) @@ -845,26 +895,64 @@ class AttendedCliTests(OOCliFixture): ssh_user='root', variant_num=1, confirm_facts='y', - master_lb=('10.0.0.5', False)) + master_lb=('10.0.0.5', False), + storage='10.1.0.1',) self.cli_args.append("install") result = self.runner.invoke(cli.cli, self.cli_args, input=cli_input) self.assert_result(result, 0) self._verify_load_facts(load_facts_mock) - self._verify_run_playbook(run_playbook_mock, 4, 4) + self._verify_run_playbook(run_playbook_mock, 5, 5) written_config = read_yaml(self.config_file) - self._verify_config_hosts(written_config, 4) + self._verify_config_hosts(written_config, 5) inventory = ConfigParser.ConfigParser(allow_no_value=True) - inventory.read(os.path.join(self.work_dir, '.ansible/hosts')) - self.assertEquals('True', - inventory.get('nodes', '10.0.0.1 openshift_schedulable')) - self.assertEquals('True', - inventory.get('nodes', '10.0.0.2 openshift_schedulable')) - self.assertEquals('True', - inventory.get('nodes', '10.0.0.3 openshift_schedulable')) + inventory.read(os.path.join(self.work_dir, 'hosts')) + self.assert_inventory_host_var(inventory, 'nodes', '10.0.0.1', + 'openshift_schedulable=True') + self.assert_inventory_host_var(inventory, 'nodes', '10.0.0.2', + 'openshift_schedulable=True') + self.assert_inventory_host_var(inventory, 'nodes', '10.0.0.3', + 'openshift_schedulable=True') + + # Checks the inventory (as a ConfigParser) for the given host, host + # variable, and expected value. + def assert_inventory_host_var(self, inventory, section, host, variable): + # Config parser splits on the first "=", so we end up with: + # 'hostname key1' -> 'val1 key2=val2 key3=val3' + # + # Convert to something easier to test: + for (a, b) in inventory.items(section): + full_line = "%s=%s" % (a, b) + tokens = full_line.split() + if tokens[0] == host: + found = False + for token in tokens: + if token == variable: + found = True + continue + self.assertTrue("Unable to find %s in line: %s" % + (variable, full_line), found) + return + self.fail("unable to find host %s in inventory" % host) + + def assert_inventory_host_var_unset(self, inventory, section, host, variable): + # Config parser splits on the first "=", so we end up with: + # 'hostname key1' -> 'val1 key2=val2 key3=val3' + # + # Convert to something easier to test: + for (a, b) in inventory.items(section): + full_line = "%s=%s" % (a, b) + tokens = full_line.split() + if tokens[0] == host: + self.assertFalse(("%s=" % variable) in full_line, + msg='%s host variable was set: %s' % + (variable, full_line)) + return + self.fail("unable to find host %s in inventory" % host) + #interactive multimaster: attempting to use a master as the load balancer should fail: @patch('ooinstall.openshift_ansible.run_main_playbook') @@ -881,7 +969,8 @@ class AttendedCliTests(OOCliFixture): ssh_user='root', variant_num=1, confirm_facts='y', - master_lb=(['10.0.0.2', '10.0.0.5'], False)) + master_lb=(['10.0.0.2', '10.0.0.5'], False), + storage='10.1.0.1') self.cli_args.append("install") result = self.runner.invoke(cli.cli, self.cli_args, input=cli_input) @@ -898,7 +987,8 @@ class AttendedCliTests(OOCliFixture): ('10.0.0.1', True, False)], ssh_user='root', variant_num=1, - confirm_facts='y') + confirm_facts='y', + storage='10.0.0.1') self.cli_args.append("install") result = self.runner.invoke(cli.cli, self.cli_args, input=cli_input) @@ -911,9 +1001,9 @@ class AttendedCliTests(OOCliFixture): self._verify_config_hosts(written_config, 1) inventory = ConfigParser.ConfigParser(allow_no_value=True) - inventory.read(os.path.join(self.work_dir, '.ansible/hosts')) - self.assertEquals('True', - inventory.get('nodes', '10.0.0.1 openshift_schedulable')) + inventory.read(os.path.join(self.work_dir, 'hosts')) + self.assert_inventory_host_var(inventory, 'nodes', '10.0.0.1', + 'openshift_schedulable=True') #interactive 3.0 install confirm no HA hints @patch('ooinstall.openshift_ansible.run_main_playbook') @@ -925,15 +1015,55 @@ class AttendedCliTests(OOCliFixture): cli_input = build_input(hosts=[ ('10.0.0.1', True, False)], ssh_user='root', - variant_num=2, - confirm_facts='y') + variant_num=3, + confirm_facts='y', + storage='10.1.0.1',) self.cli_args.append("install") result = self.runner.invoke(cli.cli, self.cli_args, input=cli_input) self.assert_result(result, 0) + print result.output self.assertTrue("NOTE: Add a total of 3 or more Masters to perform an HA installation." not in result.output) + @patch('ooinstall.openshift_ansible.run_main_playbook') + @patch('ooinstall.openshift_ansible.load_system_facts') + def test_gen_inventory(self, load_facts_mock, run_playbook_mock): + load_facts_mock.return_value = (MOCK_FACTS, 0) + run_playbook_mock.return_value = 0 + + cli_input = build_input(hosts=[ + ('10.0.0.1', True, False), + ('10.0.0.2', False, False), + ('10.0.0.3', False, False)], + ssh_user='root', + variant_num=1, + confirm_facts='y', + storage='10.1.0.1',) + self.cli_args.append("install") + self.cli_args.append("--gen-inventory") + result = self.runner.invoke(cli.cli, self.cli_args, + input=cli_input) + self.assert_result(result, 0) + + self._verify_load_facts(load_facts_mock) + + # Make sure run playbook wasn't called: + self.assertEquals(0, len(run_playbook_mock.mock_calls)) + + written_config = read_yaml(self.config_file) + self._verify_config_hosts(written_config, 4) + + inventory = ConfigParser.ConfigParser(allow_no_value=True) + inventory.read(os.path.join(self.work_dir, 'hosts')) + self.assert_inventory_host_var(inventory, 'nodes', '10.0.0.1', + 'openshift_schedulable=False') + self.assert_inventory_host_var_unset(inventory, 'nodes', '10.0.0.2', + 'openshift_schedulable') + self.assert_inventory_host_var_unset(inventory, 'nodes', '10.0.0.3', + 'openshift_schedulable') + + # TODO: test with config file, attended add node # TODO: test with config file, attended new node already in config file # TODO: test with config file, attended new node already in config file, plus manually added nodes diff --git a/utils/test/fixture.py b/utils/test/fixture.py index 1b1c2e5c2..e01eaebaf 100644 --- a/utils/test/fixture.py +++ b/utils/test/fixture.py @@ -68,7 +68,7 @@ class OOCliFixture(OOInstallFixture): def _verify_load_facts(self, load_facts_mock): """ Check that we ran load facts with expected inputs. """ load_facts_args = load_facts_mock.call_args[0] - self.assertEquals(os.path.join(self.work_dir, ".ansible/hosts"), + self.assertEquals(os.path.join(self.work_dir, "hosts"), load_facts_args[0]) self.assertEquals(os.path.join(self.work_dir, "playbooks/byo/openshift_facts.yml"), @@ -81,8 +81,8 @@ class OOCliFixture(OOInstallFixture): def _verify_run_playbook(self, run_playbook_mock, exp_hosts_len, exp_hosts_to_run_on_len): """ Check that we ran playbook with expected inputs. """ - hosts = run_playbook_mock.call_args[0][0] - hosts_to_run_on = run_playbook_mock.call_args[0][1] + hosts = run_playbook_mock.call_args[0][1] + hosts_to_run_on = run_playbook_mock.call_args[0][2] self.assertEquals(exp_hosts_len, len(hosts)) self.assertEquals(exp_hosts_to_run_on_len, len(hosts_to_run_on)) @@ -92,7 +92,7 @@ class OOCliFixture(OOInstallFixture): self.assertTrue('hostname' in host) self.assertTrue('public_hostname' in host) if 'preconfigured' not in host: - self.assertTrue(host['node']) + self.assertTrue('node' in host or 'storage' in host) self.assertTrue('ip' in host) self.assertTrue('public_ip' in host) @@ -133,8 +133,8 @@ class OOCliFixture(OOInstallFixture): self._verify_run_playbook(run_playbook_mock, exp_hosts_len, exp_hosts_to_run_on_len) # Make sure we ran on the expected masters and nodes: - hosts = run_playbook_mock.call_args[0][0] - hosts_to_run_on = run_playbook_mock.call_args[0][1] + hosts = run_playbook_mock.call_args[0][1] + hosts_to_run_on = run_playbook_mock.call_args[0][2] self.assertEquals(exp_hosts_len, len(hosts)) self.assertEquals(exp_hosts_to_run_on_len, len(hosts_to_run_on)) @@ -142,7 +142,7 @@ class OOCliFixture(OOInstallFixture): #pylint: disable=too-many-arguments,too-many-branches,too-many-statements def build_input(ssh_user=None, hosts=None, variant_num=None, add_nodes=None, confirm_facts=None, schedulable_masters_ok=None, - master_lb=None): + master_lb=None, storage=None): """ Build an input string simulating a user entering values in an interactive attended install. @@ -197,7 +197,13 @@ def build_input(ssh_user=None, hosts=None, variant_num=None, inputs.append(master_lb[0]) inputs.append('y' if master_lb[1] else 'n') - inputs.append('example.com') + if storage: + inputs.append(storage) + + inputs.append('subdomain.example.com') + inputs.append('proxy.example.com') + inputs.append('proxy-private.example.com') + inputs.append('exclude.example.com') # TODO: support option 2, fresh install if add_nodes: |