diff --git a/contrib/inventory_builder/inventory.py b/contrib/inventory_builder/inventory.py deleted file mode 100644 index 76e7c0c46641ceac87b2d1ce2c5baf849de645ec..0000000000000000000000000000000000000000 --- a/contrib/inventory_builder/inventory.py +++ /dev/null @@ -1,480 +0,0 @@ -#!/usr/bin/env python3 -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Usage: inventory.py ip1 [ip2 ...] -# Examples: inventory.py 10.10.1.3 10.10.1.4 10.10.1.5 -# -# Advanced usage: -# Add another host after initial creation: inventory.py 10.10.1.5 -# Add range of hosts: inventory.py 10.10.1.3-10.10.1.5 -# Add hosts with different ip and access ip: -# inventory.py 10.0.0.1,192.168.10.1 10.0.0.2,192.168.10.2 10.0.0.3,192.168.1.3 -# Add hosts with a specific hostname, ip, and optional access ip: -# inventory.py first,10.0.0.1,192.168.10.1 second,10.0.0.2 last,10.0.0.3 -# Delete a host: inventory.py -10.10.1.3 -# Delete a host by id: inventory.py -node1 -# -# Load a YAML or JSON file with inventory data: inventory.py load hosts.yaml -# YAML file should be in the following format: -# group1: -# host1: -# ip: X.X.X.X -# var: val -# group2: -# host2: -# ip: X.X.X.X - -from collections import OrderedDict -from ipaddress import ip_address -from ruamel.yaml import YAML - -import os -import re -import subprocess -import sys - -ROLES = ['all', 'kube_control_plane', 'kube_node', 'etcd', 'k8s_cluster', - 'calico_rr'] -PROTECTED_NAMES = ROLES -AVAILABLE_COMMANDS = ['help', 'print_cfg', 'print_ips', 'print_hostnames', - 'load', 'add'] -_boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True, - '0': False, 'no': False, 'false': False, 'off': False} -yaml = YAML() -yaml.Representer.add_representer(OrderedDict, yaml.Representer.represent_dict) - - -def get_var_as_bool(name, default): - value = os.environ.get(name, '') - return _boolean_states.get(value.lower(), default) - -# Configurable as shell vars start - - -CONFIG_FILE = os.environ.get("CONFIG_FILE", "./inventory/sample/hosts.yaml") -# Remove the reference of KUBE_MASTERS after some deprecation cycles. -KUBE_CONTROL_HOSTS = int(os.environ.get("KUBE_CONTROL_HOSTS", - os.environ.get("KUBE_MASTERS", 2))) -# Reconfigures cluster distribution at scale -SCALE_THRESHOLD = int(os.environ.get("SCALE_THRESHOLD", 50)) -MASSIVE_SCALE_THRESHOLD = int(os.environ.get("MASSIVE_SCALE_THRESHOLD", 200)) - -DEBUG = get_var_as_bool("DEBUG", True) -HOST_PREFIX = os.environ.get("HOST_PREFIX", "node") -USE_REAL_HOSTNAME = get_var_as_bool("USE_REAL_HOSTNAME", False) - -# Configurable as shell vars end - - -class KubesprayInventory(object): - - def __init__(self, changed_hosts=None, config_file=None): - self.config_file = config_file - self.yaml_config = {} - loadPreviousConfig = False - printHostnames = False - # See whether there are any commands to process - if changed_hosts and changed_hosts[0] in AVAILABLE_COMMANDS: - if changed_hosts[0] == "add": - loadPreviousConfig = True - changed_hosts = changed_hosts[1:] - elif changed_hosts[0] == "print_hostnames": - loadPreviousConfig = True - printHostnames = True - else: - self.parse_command(changed_hosts[0], changed_hosts[1:]) - sys.exit(0) - - # If the user wants to remove a node, we need to load the config anyway - if changed_hosts and changed_hosts[0][0] == "-": - loadPreviousConfig = True - - if self.config_file and loadPreviousConfig: # Load previous YAML file - try: - self.hosts_file = open(config_file, 'r') - self.yaml_config = yaml.load(self.hosts_file) - except OSError as e: - # I am assuming we are catching "cannot open file" exceptions - print(e) - sys.exit(1) - - if printHostnames: - self.print_hostnames() - sys.exit(0) - - self.ensure_required_groups(ROLES) - - if changed_hosts: - changed_hosts = self.range2ips(changed_hosts) - self.hosts = self.build_hostnames(changed_hosts, - loadPreviousConfig) - self.purge_invalid_hosts(self.hosts.keys(), PROTECTED_NAMES) - self.set_all(self.hosts) - self.set_k8s_cluster() - etcd_hosts_count = 3 if len(self.hosts.keys()) >= 3 else 1 - self.set_etcd(list(self.hosts.keys())[:etcd_hosts_count]) - if len(self.hosts) >= SCALE_THRESHOLD: - self.set_kube_control_plane(list(self.hosts.keys())[ - etcd_hosts_count:(etcd_hosts_count + KUBE_CONTROL_HOSTS)]) - else: - self.set_kube_control_plane( - list(self.hosts.keys())[:KUBE_CONTROL_HOSTS]) - self.set_kube_node(self.hosts.keys()) - if len(self.hosts) >= SCALE_THRESHOLD: - self.set_calico_rr(list(self.hosts.keys())[:etcd_hosts_count]) - else: # Show help if no options - self.show_help() - sys.exit(0) - - self.write_config(self.config_file) - - def write_config(self, config_file): - if config_file: - with open(self.config_file, 'w') as f: - yaml.dump(self.yaml_config, f) - - else: - print("WARNING: Unable to save config. Make sure you set " - "CONFIG_FILE env var.") - - def debug(self, msg): - if DEBUG: - print("DEBUG: {0}".format(msg)) - - def get_ip_from_opts(self, optstring): - if 'ip' in optstring: - return optstring['ip'] - else: - raise ValueError("IP parameter not found in options") - - def ensure_required_groups(self, groups): - for group in groups: - if group == 'all': - self.debug("Adding group {0}".format(group)) - if group not in self.yaml_config: - all_dict = OrderedDict([('hosts', OrderedDict({})), - ('children', OrderedDict({}))]) - self.yaml_config = {'all': all_dict} - else: - self.debug("Adding group {0}".format(group)) - if group not in self.yaml_config['all']['children']: - self.yaml_config['all']['children'][group] = {'hosts': {}} - - def get_host_id(self, host): - '''Returns integer host ID (without padding) from a given hostname.''' - try: - short_hostname = host.split('.')[0] - return int(re.findall("\\d+$", short_hostname)[-1]) - except IndexError: - raise ValueError("Host name must end in an integer") - - # Keeps already specified hosts, - # and adds or removes the hosts provided as an argument - def build_hostnames(self, changed_hosts, loadPreviousConfig=False): - existing_hosts = OrderedDict() - highest_host_id = 0 - # Load already existing hosts from the YAML - if loadPreviousConfig: - try: - for host in self.yaml_config['all']['hosts']: - # Read configuration of an existing host - hostConfig = self.yaml_config['all']['hosts'][host] - existing_hosts[host] = hostConfig - # If the existing host seems - # to have been created automatically, detect its ID - if host.startswith(HOST_PREFIX): - host_id = self.get_host_id(host) - if host_id > highest_host_id: - highest_host_id = host_id - except Exception as e: - # I am assuming we are catching automatically - # created hosts without IDs - print(e) - sys.exit(1) - - # FIXME(mattymo): Fix condition where delete then add reuses highest id - next_host_id = highest_host_id + 1 - next_host = "" - - all_hosts = existing_hosts.copy() - for host in changed_hosts: - # Delete the host from config the hostname/IP has a "-" prefix - if host[0] == "-": - realhost = host[1:] - if self.exists_hostname(all_hosts, realhost): - self.debug("Marked {0} for deletion.".format(realhost)) - all_hosts.pop(realhost) - elif self.exists_ip(all_hosts, realhost): - self.debug("Marked {0} for deletion.".format(realhost)) - self.delete_host_by_ip(all_hosts, realhost) - # Host/Argument starts with a digit, - # then we assume its an IP address - elif host[0].isdigit(): - if ',' in host: - ip, access_ip = host.split(',') - else: - ip = host - access_ip = host - if self.exists_hostname(all_hosts, host): - self.debug("Skipping existing host {0}.".format(host)) - continue - elif self.exists_ip(all_hosts, ip): - self.debug("Skipping existing host {0}.".format(ip)) - continue - - if USE_REAL_HOSTNAME: - cmd = ("ssh -oStrictHostKeyChecking=no " - + access_ip + " 'hostname -s'") - next_host = subprocess.check_output(cmd, shell=True) - next_host = next_host.strip().decode('ascii') - else: - # Generates a hostname because we have only an IP address - next_host = "{0}{1}".format(HOST_PREFIX, next_host_id) - next_host_id += 1 - # Uses automatically generated node name - # in case we dont provide it. - all_hosts[next_host] = {'ansible_host': access_ip, - 'ip': ip, - 'access_ip': access_ip} - # Host/Argument starts with a letter, then we assume its a hostname - elif host[0].isalpha(): - if ',' in host: - try: - hostname, ip, access_ip = host.split(',') - except Exception: - hostname, ip = host.split(',') - access_ip = ip - if self.exists_hostname(all_hosts, host): - self.debug("Skipping existing host {0}.".format(host)) - continue - elif self.exists_ip(all_hosts, ip): - self.debug("Skipping existing host {0}.".format(ip)) - continue - all_hosts[hostname] = {'ansible_host': access_ip, - 'ip': ip, - 'access_ip': access_ip} - return all_hosts - - # Expand IP ranges into individual addresses - def range2ips(self, hosts): - reworked_hosts = [] - - def ips(start_address, end_address): - try: - # Python 3.x - start = int(ip_address(start_address)) - end = int(ip_address(end_address)) - except Exception: - # Python 2.7 - start = int(ip_address(str(start_address))) - end = int(ip_address(str(end_address))) - return [ip_address(ip).exploded for ip in range(start, end + 1)] - - for host in hosts: - if '-' in host and not (host.startswith('-') or host[0].isalpha()): - start, end = host.strip().split('-') - try: - reworked_hosts.extend(ips(start, end)) - except ValueError: - raise Exception("Range of ip_addresses isn't valid") - else: - reworked_hosts.append(host) - return reworked_hosts - - def exists_hostname(self, existing_hosts, hostname): - return hostname in existing_hosts.keys() - - def exists_ip(self, existing_hosts, ip): - for host_opts in existing_hosts.values(): - if ip == self.get_ip_from_opts(host_opts): - return True - return False - - def delete_host_by_ip(self, existing_hosts, ip): - for hostname, host_opts in existing_hosts.items(): - if ip == self.get_ip_from_opts(host_opts): - del existing_hosts[hostname] - return - raise ValueError("Unable to find host by IP: {0}".format(ip)) - - def purge_invalid_hosts(self, hostnames, protected_names=[]): - for role in self.yaml_config['all']['children']: - if role != 'k8s_cluster' and self.yaml_config['all']['children'][role]['hosts']: # noqa - all_hosts = self.yaml_config['all']['children'][role]['hosts'].copy() # noqa - for host in all_hosts.keys(): - if host not in hostnames and host not in protected_names: - self.debug( - "Host {0} removed from role {1}".format(host, role)) # noqa - del self.yaml_config['all']['children'][role]['hosts'][host] # noqa - # purge from all - if self.yaml_config['all']['hosts']: - all_hosts = self.yaml_config['all']['hosts'].copy() - for host in all_hosts.keys(): - if host not in hostnames and host not in protected_names: - self.debug("Host {0} removed from role all".format(host)) - del self.yaml_config['all']['hosts'][host] - - def add_host_to_group(self, group, host, opts=""): - self.debug("adding host {0} to group {1}".format(host, group)) - if group == 'all': - if self.yaml_config['all']['hosts'] is None: - self.yaml_config['all']['hosts'] = {host: None} - self.yaml_config['all']['hosts'][host] = opts - elif group != 'k8s_cluster:children': - if self.yaml_config['all']['children'][group]['hosts'] is None: - self.yaml_config['all']['children'][group]['hosts'] = { - host: None} - else: - self.yaml_config['all']['children'][group]['hosts'][host] = None # noqa - - def set_kube_control_plane(self, hosts): - for host in hosts: - self.add_host_to_group('kube_control_plane', host) - - def set_all(self, hosts): - for host, opts in hosts.items(): - self.add_host_to_group('all', host, opts) - - def set_k8s_cluster(self): - k8s_cluster = {'children': {'kube_control_plane': None, - 'kube_node': None}} - self.yaml_config['all']['children']['k8s_cluster'] = k8s_cluster - - def set_calico_rr(self, hosts): - for host in hosts: - if host in self.yaml_config['all']['children']['kube_control_plane']: # noqa - self.debug("Not adding {0} to calico_rr group because it " - "conflicts with kube_control_plane " - "group".format(host)) - continue - if host in self.yaml_config['all']['children']['kube_node']: - self.debug("Not adding {0} to calico_rr group because it " - "conflicts with kube_node group".format(host)) - continue - self.add_host_to_group('calico_rr', host) - - def set_kube_node(self, hosts): - for host in hosts: - if len(self.yaml_config['all']['hosts']) >= SCALE_THRESHOLD: - if host in self.yaml_config['all']['children']['etcd']['hosts']: # noqa - self.debug("Not adding {0} to kube_node group because of " - "scale deployment and host is in etcd " - "group.".format(host)) - continue - if len(self.yaml_config['all']['hosts']) >= MASSIVE_SCALE_THRESHOLD: # noqa - if host in self.yaml_config['all']['children']['kube_control_plane']['hosts']: # noqa - self.debug("Not adding {0} to kube_node group because of " - "scale deployment and host is in " - "kube_control_plane group.".format(host)) - continue - self.add_host_to_group('kube_node', host) - - def set_etcd(self, hosts): - for host in hosts: - self.add_host_to_group('etcd', host) - - def load_file(self, files=None): - '''Directly loads JSON to inventory.''' - - if not files: - raise Exception("No input file specified.") - - import json - - for filename in list(files): - # Try JSON - try: - with open(filename, 'r') as f: - data = json.load(f) - except ValueError: - raise Exception("Cannot read %s as JSON, or CSV", filename) - - self.ensure_required_groups(ROLES) - self.set_k8s_cluster() - for group, hosts in data.items(): - self.ensure_required_groups([group]) - for host, opts in hosts.items(): - optstring = {'ansible_host': opts['ip'], - 'ip': opts['ip'], - 'access_ip': opts['ip']} - self.add_host_to_group('all', host, optstring) - self.add_host_to_group(group, host) - self.write_config(self.config_file) - - def parse_command(self, command, args=None): - if command == 'help': - self.show_help() - elif command == 'print_cfg': - self.print_config() - elif command == 'print_ips': - self.print_ips() - elif command == 'print_hostnames': - self.print_hostnames() - elif command == 'load': - self.load_file(args) - else: - raise Exception("Invalid command specified.") - - def show_help(self): - help_text = '''Usage: inventory.py ip1 [ip2 ...] -Examples: inventory.py 10.10.1.3 10.10.1.4 10.10.1.5 - -Available commands: -help - Display this message -print_cfg - Write inventory file to stdout -print_ips - Write a space-delimited list of IPs from "all" group -print_hostnames - Write a space-delimited list of Hostnames from "all" group -add - Adds specified hosts into an already existing inventory - -Advanced usage: -Create new or overwrite old inventory file: inventory.py 10.10.1.5 -Add another host after initial creation: inventory.py add 10.10.1.6 -Add range of hosts: inventory.py 10.10.1.3-10.10.1.5 -Add hosts with different ip and access ip: inventory.py 10.0.0.1,192.168.10.1 10.0.0.2,192.168.10.2 10.0.0.3,192.168.10.3 -Add hosts with a specific hostname, ip, and optional access ip: first,10.0.0.1,192.168.10.1 second,10.0.0.2 last,10.0.0.3 -Delete a host: inventory.py -10.10.1.3 -Delete a host by id: inventory.py -node1 - -Configurable env vars: -DEBUG Enable debug printing. Default: True -CONFIG_FILE File to write config to Default: ./inventory/sample/hosts.yaml -HOST_PREFIX Host prefix for generated hosts. Default: node -KUBE_CONTROL_HOSTS Set the number of kube-control-planes. Default: 2 -SCALE_THRESHOLD Separate ETCD role if # of nodes >= 50 -MASSIVE_SCALE_THRESHOLD Separate K8s control-plane and ETCD if # of nodes >= 200 -''' # noqa - print(help_text) - - def print_config(self): - yaml.dump(self.yaml_config, sys.stdout) - - def print_hostnames(self): - print(' '.join(self.yaml_config['all']['hosts'].keys())) - - def print_ips(self): - ips = [] - for host, opts in self.yaml_config['all']['hosts'].items(): - ips.append(self.get_ip_from_opts(opts)) - print(' '.join(ips)) - - -def main(argv=None): - if not argv: - argv = sys.argv[1:] - KubesprayInventory(argv, CONFIG_FILE) - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/contrib/inventory_builder/requirements.txt b/contrib/inventory_builder/requirements.txt deleted file mode 100644 index c54501a4bd39c6dff8511573107e53240b1a8aa1..0000000000000000000000000000000000000000 --- a/contrib/inventory_builder/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -configparser>=3.3.0 -ipaddress -ruamel.yaml>=0.15.88 diff --git a/contrib/inventory_builder/setup.cfg b/contrib/inventory_builder/setup.cfg deleted file mode 100644 index a775367e2005ceaefb8eb30e6b655deea26d9bc1..0000000000000000000000000000000000000000 --- a/contrib/inventory_builder/setup.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[metadata] -name = kubespray-inventory-builder -version = 0.1 diff --git a/contrib/inventory_builder/setup.py b/contrib/inventory_builder/setup.py deleted file mode 100644 index 43c5ca1b4969930cb18b03c78528e2e99ec147d2..0000000000000000000000000000000000000000 --- a/contrib/inventory_builder/setup.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT -import setuptools - -# In python < 2.7.4, a lazy loading of package `pbr` will break -# setuptools if some other modules registered functions in `atexit`. -# solution from: http://bugs.python.org/issue15881#msg170215 -try: - import multiprocessing # noqa -except ImportError: - pass - -setuptools.setup( - setup_requires=[], - pbr=False) diff --git a/contrib/inventory_builder/test-requirements.txt b/contrib/inventory_builder/test-requirements.txt deleted file mode 100644 index 98a662a4dde66e413991d8edbb9ba8a7a08f33ba..0000000000000000000000000000000000000000 --- a/contrib/inventory_builder/test-requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -hacking>=0.10.2 -mock>=1.3.0 -pytest>=2.8.0 diff --git a/contrib/inventory_builder/tests/test_inventory.py b/contrib/inventory_builder/tests/test_inventory.py deleted file mode 100644 index 5d6649d683020d638e89e44b111b62c44bb37f91..0000000000000000000000000000000000000000 --- a/contrib/inventory_builder/tests/test_inventory.py +++ /dev/null @@ -1,595 +0,0 @@ -# Copyright 2016 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import inventory -from io import StringIO -import unittest -from unittest import mock - -from collections import OrderedDict -import sys - -path = "./contrib/inventory_builder/" -if path not in sys.path: - sys.path.append(path) - -import inventory # noqa - - -class TestInventoryPrintHostnames(unittest.TestCase): - - @mock.patch('ruamel.yaml.YAML.load') - def test_print_hostnames(self, load_mock): - mock_io = mock.mock_open(read_data='') - load_mock.return_value = OrderedDict({'all': {'hosts': { - 'node1': {'ansible_host': '10.90.0.2', - 'ip': '10.90.0.2', - 'access_ip': '10.90.0.2'}, - 'node2': {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'}}}}) - with mock.patch('builtins.open', mock_io): - with self.assertRaises(SystemExit) as cm: - with mock.patch('sys.stdout', new_callable=StringIO) as stdout: - inventory.KubesprayInventory( - changed_hosts=["print_hostnames"], - config_file="file") - self.assertEqual("node1 node2\n", stdout.getvalue()) - self.assertEqual(cm.exception.code, 0) - - -class TestInventory(unittest.TestCase): - @mock.patch('inventory.sys') - def setUp(self, sys_mock): - sys_mock.exit = mock.Mock() - super(TestInventory, self).setUp() - self.data = ['10.90.3.2', '10.90.3.3', '10.90.3.4'] - self.inv = inventory.KubesprayInventory() - - def test_get_ip_from_opts(self): - optstring = {'ansible_host': '10.90.3.2', - 'ip': '10.90.3.2', - 'access_ip': '10.90.3.2'} - expected = "10.90.3.2" - result = self.inv.get_ip_from_opts(optstring) - self.assertEqual(expected, result) - - def test_get_ip_from_opts_invalid(self): - optstring = "notanaddr=value something random!chars:D" - self.assertRaisesRegex(ValueError, "IP parameter not found", - self.inv.get_ip_from_opts, optstring) - - def test_ensure_required_groups(self): - groups = ['group1', 'group2'] - self.inv.ensure_required_groups(groups) - for group in groups: - self.assertIn(group, self.inv.yaml_config['all']['children']) - - def test_get_host_id(self): - hostnames = ['node99', 'no99de01', '01node01', 'node1.domain', - 'node3.xyz123.aaa'] - expected = [99, 1, 1, 1, 3] - for hostname, expected in zip(hostnames, expected): - result = self.inv.get_host_id(hostname) - self.assertEqual(expected, result) - - def test_get_host_id_invalid(self): - bad_hostnames = ['node', 'no99de', '01node', 'node.111111'] - for hostname in bad_hostnames: - self.assertRaisesRegex(ValueError, "Host name must end in an", - self.inv.get_host_id, hostname) - - def test_build_hostnames_add_duplicate(self): - changed_hosts = ['10.90.0.2'] - expected = OrderedDict([('node3', - {'ansible_host': '10.90.0.2', - 'ip': '10.90.0.2', - 'access_ip': '10.90.0.2'})]) - self.inv.yaml_config['all']['hosts'] = expected - result = self.inv.build_hostnames(changed_hosts, True) - self.assertEqual(expected, result) - - def test_build_hostnames_add_two(self): - changed_hosts = ['10.90.0.2', '10.90.0.3'] - expected = OrderedDict([ - ('node1', {'ansible_host': '10.90.0.2', - 'ip': '10.90.0.2', - 'access_ip': '10.90.0.2'}), - ('node2', {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'})]) - self.inv.yaml_config['all']['hosts'] = OrderedDict() - result = self.inv.build_hostnames(changed_hosts) - self.assertEqual(expected, result) - - def test_build_hostnames_add_three(self): - changed_hosts = ['10.90.0.2', '10.90.0.3', '10.90.0.4'] - expected = OrderedDict([ - ('node1', {'ansible_host': '10.90.0.2', - 'ip': '10.90.0.2', - 'access_ip': '10.90.0.2'}), - ('node2', {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'}), - ('node3', {'ansible_host': '10.90.0.4', - 'ip': '10.90.0.4', - 'access_ip': '10.90.0.4'})]) - result = self.inv.build_hostnames(changed_hosts) - self.assertEqual(expected, result) - - def test_build_hostnames_add_one(self): - changed_hosts = ['10.90.0.2'] - expected = OrderedDict([('node1', - {'ansible_host': '10.90.0.2', - 'ip': '10.90.0.2', - 'access_ip': '10.90.0.2'})]) - result = self.inv.build_hostnames(changed_hosts) - self.assertEqual(expected, result) - - def test_build_hostnames_delete_first(self): - changed_hosts = ['-10.90.0.2'] - existing_hosts = OrderedDict([ - ('node1', {'ansible_host': '10.90.0.2', - 'ip': '10.90.0.2', - 'access_ip': '10.90.0.2'}), - ('node2', {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'})]) - self.inv.yaml_config['all']['hosts'] = existing_hosts - expected = OrderedDict([ - ('node2', {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'})]) - result = self.inv.build_hostnames(changed_hosts, True) - self.assertEqual(expected, result) - - def test_build_hostnames_delete_by_hostname(self): - changed_hosts = ['-node1'] - existing_hosts = OrderedDict([ - ('node1', {'ansible_host': '10.90.0.2', - 'ip': '10.90.0.2', - 'access_ip': '10.90.0.2'}), - ('node2', {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'})]) - self.inv.yaml_config['all']['hosts'] = existing_hosts - expected = OrderedDict([ - ('node2', {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'})]) - result = self.inv.build_hostnames(changed_hosts, True) - self.assertEqual(expected, result) - - def test_exists_hostname_positive(self): - hostname = 'node1' - expected = True - existing_hosts = OrderedDict([ - ('node1', {'ansible_host': '10.90.0.2', - 'ip': '10.90.0.2', - 'access_ip': '10.90.0.2'}), - ('node2', {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'})]) - result = self.inv.exists_hostname(existing_hosts, hostname) - self.assertEqual(expected, result) - - def test_exists_hostname_negative(self): - hostname = 'node99' - expected = False - existing_hosts = OrderedDict([ - ('node1', {'ansible_host': '10.90.0.2', - 'ip': '10.90.0.2', - 'access_ip': '10.90.0.2'}), - ('node2', {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'})]) - result = self.inv.exists_hostname(existing_hosts, hostname) - self.assertEqual(expected, result) - - def test_exists_ip_positive(self): - ip = '10.90.0.2' - expected = True - existing_hosts = OrderedDict([ - ('node1', {'ansible_host': '10.90.0.2', - 'ip': '10.90.0.2', - 'access_ip': '10.90.0.2'}), - ('node2', {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'})]) - result = self.inv.exists_ip(existing_hosts, ip) - self.assertEqual(expected, result) - - def test_exists_ip_negative(self): - ip = '10.90.0.200' - expected = False - existing_hosts = OrderedDict([ - ('node1', {'ansible_host': '10.90.0.2', - 'ip': '10.90.0.2', - 'access_ip': '10.90.0.2'}), - ('node2', {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'})]) - result = self.inv.exists_ip(existing_hosts, ip) - self.assertEqual(expected, result) - - def test_delete_host_by_ip_positive(self): - ip = '10.90.0.2' - expected = OrderedDict([ - ('node2', {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'})]) - existing_hosts = OrderedDict([ - ('node1', {'ansible_host': '10.90.0.2', - 'ip': '10.90.0.2', - 'access_ip': '10.90.0.2'}), - ('node2', {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'})]) - self.inv.delete_host_by_ip(existing_hosts, ip) - self.assertEqual(expected, existing_hosts) - - def test_delete_host_by_ip_negative(self): - ip = '10.90.0.200' - existing_hosts = OrderedDict([ - ('node1', {'ansible_host': '10.90.0.2', - 'ip': '10.90.0.2', - 'access_ip': '10.90.0.2'}), - ('node2', {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'})]) - self.assertRaisesRegex(ValueError, "Unable to find host", - self.inv.delete_host_by_ip, existing_hosts, ip) - - def test_purge_invalid_hosts(self): - proper_hostnames = ['node1', 'node2'] - bad_host = 'doesnotbelong2' - existing_hosts = OrderedDict([ - ('node1', {'ansible_host': '10.90.0.2', - 'ip': '10.90.0.2', - 'access_ip': '10.90.0.2'}), - ('node2', {'ansible_host': '10.90.0.3', - 'ip': '10.90.0.3', - 'access_ip': '10.90.0.3'}), - ('doesnotbelong2', {'whateveropts=ilike'})]) - self.inv.yaml_config['all']['hosts'] = existing_hosts - self.inv.purge_invalid_hosts(proper_hostnames) - self.assertNotIn( - bad_host, self.inv.yaml_config['all']['hosts'].keys()) - - def test_add_host_to_group(self): - group = 'etcd' - host = 'node1' - opts = {'ip': '10.90.0.2'} - - self.inv.add_host_to_group(group, host, opts) - self.assertEqual( - self.inv.yaml_config['all']['children'][group]['hosts'].get(host), - None) - - def test_set_kube_control_plane(self): - group = 'kube_control_plane' - host = 'node1' - - self.inv.set_kube_control_plane([host]) - self.assertIn( - host, self.inv.yaml_config['all']['children'][group]['hosts']) - - def test_set_all(self): - hosts = OrderedDict([ - ('node1', 'opt1'), - ('node2', 'opt2')]) - - self.inv.set_all(hosts) - for host, opt in hosts.items(): - self.assertEqual( - self.inv.yaml_config['all']['hosts'].get(host), opt) - - def test_set_k8s_cluster(self): - group = 'k8s_cluster' - expected_hosts = ['kube_node', 'kube_control_plane'] - - self.inv.set_k8s_cluster() - for host in expected_hosts: - self.assertIn( - host, - self.inv.yaml_config['all']['children'][group]['children']) - - def test_set_kube_node(self): - group = 'kube_node' - host = 'node1' - - self.inv.set_kube_node([host]) - self.assertIn( - host, self.inv.yaml_config['all']['children'][group]['hosts']) - - def test_set_etcd(self): - group = 'etcd' - host = 'node1' - - self.inv.set_etcd([host]) - self.assertIn( - host, self.inv.yaml_config['all']['children'][group]['hosts']) - - def test_scale_scenario_one(self): - num_nodes = 50 - hosts = OrderedDict() - - for hostid in range(1, num_nodes+1): - hosts["node" + str(hostid)] = "" - - self.inv.set_all(hosts) - self.inv.set_etcd(list(hosts.keys())[0:3]) - self.inv.set_kube_control_plane(list(hosts.keys())[0:2]) - self.inv.set_kube_node(hosts.keys()) - for h in range(3): - self.assertFalse( - list(hosts.keys())[h] in - self.inv.yaml_config['all']['children']['kube_node']['hosts']) - - def test_scale_scenario_two(self): - num_nodes = 500 - hosts = OrderedDict() - - for hostid in range(1, num_nodes+1): - hosts["node" + str(hostid)] = "" - - self.inv.set_all(hosts) - self.inv.set_etcd(list(hosts.keys())[0:3]) - self.inv.set_kube_control_plane(list(hosts.keys())[3:5]) - self.inv.set_kube_node(hosts.keys()) - for h in range(5): - self.assertFalse( - list(hosts.keys())[h] in - self.inv.yaml_config['all']['children']['kube_node']['hosts']) - - def test_range2ips_range(self): - changed_hosts = ['10.90.0.2', '10.90.0.4-10.90.0.6', '10.90.0.8'] - expected = ['10.90.0.2', - '10.90.0.4', - '10.90.0.5', - '10.90.0.6', - '10.90.0.8'] - result = self.inv.range2ips(changed_hosts) - self.assertEqual(expected, result) - - def test_range2ips_incorrect_range(self): - host_range = ['10.90.0.4-a.9b.c.e'] - self.assertRaisesRegex(Exception, "Range of ip_addresses isn't valid", - self.inv.range2ips, host_range) - - def test_build_hostnames_create_with_one_different_ips(self): - changed_hosts = ['10.90.0.2,192.168.0.2'] - expected = OrderedDict([('node1', - {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'})]) - result = self.inv.build_hostnames(changed_hosts) - self.assertEqual(expected, result) - - def test_build_hostnames_create_with_two_different_ips(self): - changed_hosts = ['10.90.0.2,192.168.0.2', '10.90.0.3,192.168.0.3'] - expected = OrderedDict([ - ('node1', {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'}), - ('node2', {'ansible_host': '192.168.0.3', - 'ip': '10.90.0.3', - 'access_ip': '192.168.0.3'})]) - result = self.inv.build_hostnames(changed_hosts) - self.assertEqual(expected, result) - - def test_build_hostnames_create_with_three_different_ips(self): - changed_hosts = ['10.90.0.2,192.168.0.2', - '10.90.0.3,192.168.0.3', - '10.90.0.4,192.168.0.4'] - expected = OrderedDict([ - ('node1', {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'}), - ('node2', {'ansible_host': '192.168.0.3', - 'ip': '10.90.0.3', - 'access_ip': '192.168.0.3'}), - ('node3', {'ansible_host': '192.168.0.4', - 'ip': '10.90.0.4', - 'access_ip': '192.168.0.4'})]) - result = self.inv.build_hostnames(changed_hosts) - self.assertEqual(expected, result) - - def test_build_hostnames_overwrite_one_with_different_ips(self): - changed_hosts = ['10.90.0.2,192.168.0.2'] - expected = OrderedDict([('node1', - {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'})]) - existing = OrderedDict([('node5', - {'ansible_host': '192.168.0.5', - 'ip': '10.90.0.5', - 'access_ip': '192.168.0.5'})]) - self.inv.yaml_config['all']['hosts'] = existing - result = self.inv.build_hostnames(changed_hosts) - self.assertEqual(expected, result) - - def test_build_hostnames_overwrite_three_with_different_ips(self): - changed_hosts = ['10.90.0.2,192.168.0.2'] - expected = OrderedDict([('node1', - {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'})]) - existing = OrderedDict([ - ('node3', {'ansible_host': '192.168.0.3', - 'ip': '10.90.0.3', - 'access_ip': '192.168.0.3'}), - ('node4', {'ansible_host': '192.168.0.4', - 'ip': '10.90.0.4', - 'access_ip': '192.168.0.4'}), - ('node5', {'ansible_host': '192.168.0.5', - 'ip': '10.90.0.5', - 'access_ip': '192.168.0.5'})]) - self.inv.yaml_config['all']['hosts'] = existing - result = self.inv.build_hostnames(changed_hosts) - self.assertEqual(expected, result) - - def test_build_hostnames_different_ips_add_duplicate(self): - changed_hosts = ['10.90.0.2,192.168.0.2'] - expected = OrderedDict([('node3', - {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'})]) - existing = expected - self.inv.yaml_config['all']['hosts'] = existing - result = self.inv.build_hostnames(changed_hosts, True) - self.assertEqual(expected, result) - - def test_build_hostnames_add_two_different_ips_into_one_existing(self): - changed_hosts = ['10.90.0.3,192.168.0.3', '10.90.0.4,192.168.0.4'] - expected = OrderedDict([ - ('node2', {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'}), - ('node3', {'ansible_host': '192.168.0.3', - 'ip': '10.90.0.3', - 'access_ip': '192.168.0.3'}), - ('node4', {'ansible_host': '192.168.0.4', - 'ip': '10.90.0.4', - 'access_ip': '192.168.0.4'})]) - - existing = OrderedDict([ - ('node2', {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'})]) - self.inv.yaml_config['all']['hosts'] = existing - result = self.inv.build_hostnames(changed_hosts, True) - self.assertEqual(expected, result) - - def test_build_hostnames_add_two_different_ips_into_two_existing(self): - changed_hosts = ['10.90.0.4,192.168.0.4', '10.90.0.5,192.168.0.5'] - expected = OrderedDict([ - ('node2', {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'}), - ('node3', {'ansible_host': '192.168.0.3', - 'ip': '10.90.0.3', - 'access_ip': '192.168.0.3'}), - ('node4', {'ansible_host': '192.168.0.4', - 'ip': '10.90.0.4', - 'access_ip': '192.168.0.4'}), - ('node5', {'ansible_host': '192.168.0.5', - 'ip': '10.90.0.5', - 'access_ip': '192.168.0.5'})]) - - existing = OrderedDict([ - ('node2', {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'}), - ('node3', {'ansible_host': '192.168.0.3', - 'ip': '10.90.0.3', - 'access_ip': '192.168.0.3'})]) - self.inv.yaml_config['all']['hosts'] = existing - result = self.inv.build_hostnames(changed_hosts, True) - self.assertEqual(expected, result) - - def test_build_hostnames_add_two_different_ips_into_three_existing(self): - changed_hosts = ['10.90.0.5,192.168.0.5', '10.90.0.6,192.168.0.6'] - expected = OrderedDict([ - ('node2', {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'}), - ('node3', {'ansible_host': '192.168.0.3', - 'ip': '10.90.0.3', - 'access_ip': '192.168.0.3'}), - ('node4', {'ansible_host': '192.168.0.4', - 'ip': '10.90.0.4', - 'access_ip': '192.168.0.4'}), - ('node5', {'ansible_host': '192.168.0.5', - 'ip': '10.90.0.5', - 'access_ip': '192.168.0.5'}), - ('node6', {'ansible_host': '192.168.0.6', - 'ip': '10.90.0.6', - 'access_ip': '192.168.0.6'})]) - - existing = OrderedDict([ - ('node2', {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'}), - ('node3', {'ansible_host': '192.168.0.3', - 'ip': '10.90.0.3', - 'access_ip': '192.168.0.3'}), - ('node4', {'ansible_host': '192.168.0.4', - 'ip': '10.90.0.4', - 'access_ip': '192.168.0.4'})]) - self.inv.yaml_config['all']['hosts'] = existing - result = self.inv.build_hostnames(changed_hosts, True) - self.assertEqual(expected, result) - - # Add two IP addresses into a config that has - # three already defined IP addresses. One of the IP addresses - # is a duplicate. - def test_build_hostnames_add_two_duplicate_one_overlap(self): - changed_hosts = ['10.90.0.4,192.168.0.4', '10.90.0.5,192.168.0.5'] - expected = OrderedDict([ - ('node2', {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'}), - ('node3', {'ansible_host': '192.168.0.3', - 'ip': '10.90.0.3', - 'access_ip': '192.168.0.3'}), - ('node4', {'ansible_host': '192.168.0.4', - 'ip': '10.90.0.4', - 'access_ip': '192.168.0.4'}), - ('node5', {'ansible_host': '192.168.0.5', - 'ip': '10.90.0.5', - 'access_ip': '192.168.0.5'})]) - - existing = OrderedDict([ - ('node2', {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'}), - ('node3', {'ansible_host': '192.168.0.3', - 'ip': '10.90.0.3', - 'access_ip': '192.168.0.3'}), - ('node4', {'ansible_host': '192.168.0.4', - 'ip': '10.90.0.4', - 'access_ip': '192.168.0.4'})]) - self.inv.yaml_config['all']['hosts'] = existing - result = self.inv.build_hostnames(changed_hosts, True) - self.assertEqual(expected, result) - - # Add two duplicate IP addresses into a config that has - # three already defined IP addresses - def test_build_hostnames_add_two_duplicate_two_overlap(self): - changed_hosts = ['10.90.0.3,192.168.0.3', '10.90.0.4,192.168.0.4'] - expected = OrderedDict([ - ('node2', {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'}), - ('node3', {'ansible_host': '192.168.0.3', - 'ip': '10.90.0.3', - 'access_ip': '192.168.0.3'}), - ('node4', {'ansible_host': '192.168.0.4', - 'ip': '10.90.0.4', - 'access_ip': '192.168.0.4'})]) - - existing = OrderedDict([ - ('node2', {'ansible_host': '192.168.0.2', - 'ip': '10.90.0.2', - 'access_ip': '192.168.0.2'}), - ('node3', {'ansible_host': '192.168.0.3', - 'ip': '10.90.0.3', - 'access_ip': '192.168.0.3'}), - ('node4', {'ansible_host': '192.168.0.4', - 'ip': '10.90.0.4', - 'access_ip': '192.168.0.4'})]) - self.inv.yaml_config['all']['hosts'] = existing - result = self.inv.build_hostnames(changed_hosts, True) - self.assertEqual(expected, result) diff --git a/contrib/inventory_builder/tox.ini b/contrib/inventory_builder/tox.ini deleted file mode 100644 index c9c70428cc41cd87a7ecb5848592097b3e5182cd..0000000000000000000000000000000000000000 --- a/contrib/inventory_builder/tox.ini +++ /dev/null @@ -1,34 +0,0 @@ -[tox] -minversion = 1.6 -skipsdist = True -envlist = pep8 - -[testenv] -allowlist_externals = py.test -usedevelop = True -deps = - -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt -setenv = VIRTUAL_ENV={envdir} -passenv = - http_proxy - HTTP_PROXY - https_proxy - HTTPS_PROXY - no_proxy - NO_PROXY -commands = pytest -vv #{posargs:./tests} - -[testenv:pep8] -usedevelop = False -allowlist_externals = bash -commands = - bash -c "find {toxinidir}/* -type f -name '*.py' -print0 | xargs -0 flake8" - -[testenv:venv] -commands = {posargs} - -[flake8] -show-source = true -builtins = _ -exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg