From 3fe2c8ccddb9463c7c8d8c8ccdc8e6c93f837aca Mon Sep 17 00:00:00 2001 From: Nolan Brubaker Date: Thu, 5 Nov 2015 16:17:15 -0500 Subject: [PATCH] Add testing for dynamic_inventory.py This change introduces a testing framework for the dynamic_inventory.py script, which will be helpful in debugging any changes made to the script. Current testing is indirect, relying on the other, very high level gate tests, which may take longer to identify inventory generation issues. Currently, it does not do actual unit testing, instead opting to start by testing from 'outside', calling the script through a subprocess. In time, this should be replaced, but as a start it's easier to implement without making many changes. The 'tests/inventory' directory has the necessary pieces symlinked in; namely, env.d, openstack_environment.yml, and a sample openstack_user-config.yml (based on the AIO one already used in the gates). A new tox environment, 'inventory' was added rather than creating a new 'gate-check-inventory.sh' file. This should fit the general openstack-infra testing framework better. For the first test, I decided to add a loop that runs 100 times because of the nature of the script. Since the dynamic_inventory script does not assign IP addresses sequentially, the loop helps exercise a larger sample than just one run per test. On my local machine, this took approximately 2 seconds. Running it 1,000 times took about 30 seconds. Thus, the time spent right now is minimal in comparison to the rest of the gate jobs. As it stands, the test suite is by no means complete, but serves as a starting point. Change-Id: I7410360ecec919639a299f72f2f3cc818bce8e33 --- .../openstack_user_config.yml.aio | 4 +- tests/inventory/env.d | 1 + tests/inventory/openstack_environment.yml | 1 + tests/inventory/openstack_user_config.yml | 1 + tests/test_inventory.py | 72 +++++++++++++++++++ tox.ini | 7 +- 6 files changed, 84 insertions(+), 2 deletions(-) create mode 120000 tests/inventory/env.d create mode 120000 tests/inventory/openstack_environment.yml create mode 120000 tests/inventory/openstack_user_config.yml create mode 100644 tests/test_inventory.py diff --git a/etc/openstack_deploy/openstack_user_config.yml.aio b/etc/openstack_deploy/openstack_user_config.yml.aio index 6fd26c2ff3..adcfb9a2f8 100644 --- a/etc/openstack_deploy/openstack_user_config.yml.aio +++ b/etc/openstack_deploy/openstack_user_config.yml.aio @@ -12,7 +12,9 @@ used_ips: global_overrides: internal_lb_vip_address: 172.29.236.100 - external_lb_vip_address: {{ bootstrap_host_public_address | default(ansible_default_ipv4.address) }} + # The external IP is quoted simply to ensure that the .aio file can be used as input + # dynamic inventory testing. + external_lb_vip_address: "{{ bootstrap_host_public_address | default(ansible_default_ipv4.address) }}" tunnel_bridge: "br-vxlan" management_bridge: "br-mgmt" provider_networks: diff --git a/tests/inventory/env.d b/tests/inventory/env.d new file mode 120000 index 0000000000..c2b03618e0 --- /dev/null +++ b/tests/inventory/env.d @@ -0,0 +1 @@ +../../etc/openstack_deploy/env.d \ No newline at end of file diff --git a/tests/inventory/openstack_environment.yml b/tests/inventory/openstack_environment.yml new file mode 120000 index 0000000000..67f78ea9ae --- /dev/null +++ b/tests/inventory/openstack_environment.yml @@ -0,0 +1 @@ +../../etc/openstack_deploy/openstack_environment.yml \ No newline at end of file diff --git a/tests/inventory/openstack_user_config.yml b/tests/inventory/openstack_user_config.yml new file mode 120000 index 0000000000..58e3a7e854 --- /dev/null +++ b/tests/inventory/openstack_user_config.yml @@ -0,0 +1 @@ +../../etc/openstack_deploy/openstack_user_config.yml.aio \ No newline at end of file diff --git a/tests/test_inventory.py b/tests/test_inventory.py new file mode 100644 index 0000000000..50195e31d0 --- /dev/null +++ b/tests/test_inventory.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +import collections +import json +import os +from os import path +import subprocess +import unittest + + +INV_DIR = '../playbooks/inventory' +SCRIPT_FILENAME = 'dynamic_inventory.py' +INV_SCRIPT = path.join(INV_DIR, SCRIPT_FILENAME) + +# We'll use the test directory, and have tox do the cd command for us. +TARGET_DIR = path.join(os.getcwd(), 'inventory') + +# These files will be placed in TARGET_DIR by INV_SCRIPT. +# They should be cleaned up between each test. +CLEANUP = [ + 'openstack_inventory.json', + 'openstack_hostnames_ips.yml', + 'backup_openstack_inventory.tar' +] + + +def cleanup(): + for f_name in CLEANUP: + f_file = path.join(TARGET_DIR, f_name) + if os.path.exists(f_file): + os.remove(f_file) + + +def get_inventory(): + "Return the inventory mapping in a dict." + try: + cmd = [INV_SCRIPT, '--file', TARGET_DIR] + inventory_string = subprocess.check_output(cmd) + inventory = json.loads(inventory_string) + return inventory + finally: + # Remove the file system artifacts since we want to force fresh runs + cleanup() + + +class TestDuplicateIps(unittest.TestCase): + def setUp(self): + # Allow custom assertion errors. + self.longMessage = True + + def test_duplicates(self): + """Test that no duplicate IPs are made on any network.""" + + for i in xrange(0, 99): + inventory = get_inventory() + ips = collections.defaultdict(int) + hostvars = inventory['_meta']['hostvars'] + + for host, var_dict in hostvars.items(): + nets = var_dict['container_networks'] + for net, vals in nets.items(): + if 'address' in vals.keys(): + + addr = vals['address'] + ips[addr] += 1 + + self.assertEqual(1, ips[addr], + msg="IP %s duplicated." % addr) + + +if __name__ == '__main__': + unittest.main() diff --git a/tox.ini b/tox.ini index de4ee1b497..13d6cb7384 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] minversion = 1.6 skipsdist = True -envlist = docs,linters,releasenotes +envlist = docs,linters,releasenotes,inventory [testenv] usedevelop = True @@ -113,3 +113,8 @@ commands = {[testenv:bashate]commands} {[testenv:ansible-lint]commands} {[testenv:ansible-syntax]commands} + +[testenv:inventory] +changedir = {toxinidir}/tests +commands = + python test_inventory.py