Add export host option for inventory-manage script

The export host option allows users to export their existing host
information for import into a third party system. The output is
'pivoted', so that the key is the host/container name, and the values
are the group memberships and hostvar information. Some top level
networking information, such as load balancer IP addresses, as well as
provider networks, are also provided.

Test scaffolding was added to ensure the export does what is intended.

The tox inventory environment was modified to do coverage testing of
both the dynamic_inventory.py and manage_inventory.py files. The
coverage data is erased between runs, since combined output lead to
incorrect results for dynamic_inventory.py.

Change-Id: I2caa5a0c070b12a74ac26334c63ac8d0de704042
This commit is contained in:
Nolan Brubaker 2016-09-16 16:24:23 -04:00
parent 8255414408
commit 18c9e1faee
5 changed files with 117 additions and 1 deletions

View File

@ -142,6 +142,19 @@ Use the host's name as an argument.
.. _`dynamic inventory functionality`: http://docs.ansible.com/ansible/intro_dynamic_inventory.html .. _`dynamic inventory functionality`: http://docs.ansible.com/ansible/intro_dynamic_inventory.html
Exporting Host Information
^^^^^^^^^^^^^^^^^^^^^^^^^^
Information on a per-host basis can be obtained with the ``--export/-e``
parameter.
This JSON output has two top-level keys: ``hosts`` and ``all``.
``hosts`` contains a map of a host's name to its variable and group data.
``all`` contains global network information such as the load balancer IPs and
provider network metadata.
The lxc_hosts Group The lxc_hosts Group
------------------- -------------------

View File

@ -0,0 +1,6 @@
---
features:
- An export flag has been added to the ``inventory-manage.py`` script. This
flag allows exporting of host and network information from an
OpenStack-Ansible inventory for import into another system, or an
alternate view of the existing data. See the developer docs for more details.

View File

@ -143,6 +143,14 @@ def args():
default=False default=False
) )
exclusive_action.add_argument(
'-e',
'--export',
help='Export group and variable information per host in JSON.',
action='store_true',
default=False
)
return vars(parser.parse_args()) return vars(parser.parse_args())
@ -296,6 +304,38 @@ def print_inventory(inventory, sort_key):
return table return table
def export_host_info(inventory):
"""Pivot variable information to be a per-host dict
This command is meant for exporting an existing inventory's information.
The exported data re-arranges variable data so that the keys are the host,
and the values are hostvars and groups.
Two top level keys are present: 'hosts' and 'all'. 'hosts' is a dictonary
of the host information. 'all' represents global data, mostly the load
balancer and provider network values. It is taken from
inventory['all']['vars'].
"""
export_info = {'hosts': {}}
host_info = export_info['hosts']
export_info['all'] = inventory['all']['vars']
for host, hostvars in inventory['_meta']['hostvars'].items():
host_info[host] = {}
host_info[host]['hostvars'] = hostvars
for group_name, group_info in inventory.items():
if group_name in ('_meta', 'all'):
continue
for host in group_info['hosts']:
if 'groups' not in host_info[host]:
host_info[host]['groups'] = []
host_info[host]['groups'].append(group_name)
return export_info
def main(): def main():
"""Run the main application.""" """Run the main application."""
# Parse user args # Parse user args
@ -318,6 +358,8 @@ def main():
# Containers in the first column, groups for each container on the right # Containers in the first column, groups for each container on the right
elif user_args['list_containers'] is True: elif user_args['list_containers'] is True:
print(print_containers_per_group(inventory)) print(print_containers_per_group(inventory))
elif user_args['export'] is True:
print(json.dumps(export_host_info(inventory), indent=2))
else: else:
recursive_dict_removal(inventory, user_args['remove_item']) recursive_dict_removal(inventory, user_args['remove_item'])
with open(environment_file, 'wb') as f_handle: with open(environment_file, 'wb') as f_handle:

53
tests/test_manage.py Normal file
View File

@ -0,0 +1,53 @@
#!/usr/bin/env python
import os
from os import path
import sys
import test_inventory
import unittest
MANAGE_DIR = path.join(os.getcwd(), 'scripts')
sys.path.append(MANAGE_DIR)
import manage_inventory as mi
class TestExportFunction(unittest.TestCase):
def setUp(self):
self.inv = test_inventory.get_inventory()
def tearDown(self):
test_inventory.cleanup()
def test_host_is_present(self):
host_inv = mi.export_host_info(self.inv)['hosts']
self.assertIn('aio1', host_inv.keys())
def test_groups_added(self):
host_inv = mi.export_host_info(self.inv)['hosts']
self.assertIn('groups', host_inv['aio1'].keys())
def test_variables_added(self):
host_inv = mi.export_host_info(self.inv)['hosts']
self.assertIn('hostvars', host_inv['aio1'].keys())
def test_number_of_hosts(self):
host_inv = mi.export_host_info(self.inv)['hosts']
self.assertEqual(len(self.inv['_meta']['hostvars']),
len(host_inv))
def test_all_information_added(self):
all_info = mi.export_host_info(self.inv)['all']
self.assertIn('provider_networks', all_info)
def test_all_lb_information(self):
all_info = mi.export_host_info(self.inv)['all']
inv_all = self.inv['all']['vars']
self.assertEqual(inv_all['internal_lb_vip_address'],
all_info['internal_lb_vip_address'])
if __name__ == '__main__':
unittest.main()

View File

@ -140,7 +140,6 @@ commands =
-e 'force_containers_destroy=yes' \ -e 'force_containers_destroy=yes' \
{toxinidir}/playbooks/setup-everything.yml {toxinidir}/playbooks/setup-everything.yml
[testenv:inventory] [testenv:inventory]
# Use a fixed seed since some inventory tests rely on specific ordering # Use a fixed seed since some inventory tests rely on specific ordering
setenv = setenv =
@ -150,6 +149,9 @@ commands =
coverage erase coverage erase
coverage run {toxinidir}/tests/test_inventory.py coverage run {toxinidir}/tests/test_inventory.py
coverage report --show-missing --include={toxinidir}/playbooks/inventory/* coverage report --show-missing --include={toxinidir}/playbooks/inventory/*
coverage erase
coverage run {toxinidir}/tests/test_manage.py
coverage report --show-missing --include={toxinidir}/scripts/*
[testenv:linters] [testenv:linters]