|
#!/usr/bin/env python |
|
|
|
import argparse |
|
import os |
|
import re |
|
import json |
|
|
|
from ansible.inventory import InventoryParser |
|
|
|
|
|
class CombinedInventory(object): |
|
|
|
def __init__(self): |
|
""" Main execution path """ |
|
|
|
self.inventory = dict() # A list of groups and the hosts in that group |
|
self.var_names = set() |
|
# self.cache = dict() # Details about hosts in the inventory |
|
|
|
# Read settings and parse CLI arguments |
|
self.parse_cli_args() |
|
self.files = self.args.files # ['newleaf-demo', 'ams-test'] # todo, make dynamic |
|
|
|
# Cache |
|
self.update_cache() |
|
|
|
data_to_print = "" |
|
|
|
# Data to print |
|
if self.args.host: |
|
data_to_print = self.get_host_info() |
|
|
|
elif self.args.list: |
|
# Display list of instances for inventory |
|
data_to_print = self.json_format_dict(self.inventory, True) |
|
|
|
else: # default action with no options |
|
data_to_print = self.json_format_dict(self.inventory, True) |
|
|
|
print data_to_print |
|
|
|
|
|
def parse_cli_args(self): |
|
""" Command line argument processing """ |
|
|
|
parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on Cobbler') |
|
parser.add_argument('--list', action='store_true', default=True, help='List instances (default: True)') |
|
parser.add_argument('--host', action='store', help='Get all the variables about a specific instance') |
|
parser.add_argument("files",nargs="*", help='list of files to collect data from') |
|
# parser.add_argument('--files', type=str, ) |
|
# parser.add_argument('--refresh-cache', action='store_true', default=False, |
|
# help='Force refresh of cache by making API requests to cobbler (default: False - use cache files)') |
|
self.args = parser.parse_args() |
|
|
|
# OVERWRITE_UNSET_VARS = False |
|
|
|
def get_host_info(self): |
|
""" Get variables about a specific host """ |
|
|
|
default_vars = {} |
|
|
|
if not self.args.host in self.cache: |
|
# host might not exist anymore |
|
return self.json_format_dict(default_vars, True) |
|
|
|
host_info = self.cache[self.args.host] |
|
# if self.OVERWRITE_UNSET_VARS: |
|
# self._overwrite_unset_vars(host_info) |
|
return self.json_format_dict(host_info, True) |
|
|
|
|
|
# ! not true |
|
# def _overwrite_unset_vars(self, info): |
|
# """ due to bug in current version of ansible (v1.8.1) |
|
# undefined variables that are defined for other hosts of the same group |
|
# ends up in the context of the current task, this is a way to overwrite them""" |
|
# for name in self.var_names: |
|
# if not name in info: |
|
# info[name] = None |
|
|
|
def update_cache(self): |
|
""" Make calls to cobbler and save the output in a cache """ |
|
|
|
self.cache = dict() |
|
self.groups = dict() |
|
self.hosts = dict() |
|
self.var_names = set() |
|
|
|
for filename in self.files: |
|
parser = InventoryParser(filename) |
|
|
|
for groupname, group in parser.groups.items(): |
|
if groupname in ['all']: |
|
continue |
|
group_obj = {} |
|
group_obj['hosts'] = [h.name for h in group.get_hosts()] |
|
variables = group.get_variables() |
|
group_obj['vars'] = variables |
|
# groups_obj['vars'] is not assigned, since different inventories may have conflicting values |
|
group_obj['children'] = [g.name for g in group.child_groups] |
|
|
|
self._set_host_vars(group_obj, variables) |
|
|
|
if groupname in self.inventory: |
|
# merging values |
|
for key in ['hosts', 'children']: |
|
self.inventory[groupname][key] = list(set(self.inventory[groupname][key] + group_obj[key])) |
|
else: |
|
self.inventory[groupname] = group_obj |
|
|
|
|
|
def _set_host_vars(self, inv, variables): |
|
for dns_name in inv['hosts']: |
|
if not dns_name in self.cache: |
|
self.cache[dns_name] = dict() |
|
|
|
for key, value in variables.iteritems(): |
|
if key in self.cache[dns_name]: |
|
raise Exception("varaible {} is defined already for host={}".format(key,dns_name)) |
|
|
|
self.cache[dns_name][key] = value |
|
self.var_names.add(key) |
|
|
|
|
|
def push(self, my_dict, key, element): |
|
""" Pushed an element onto an array that may not have been defined in the dict """ |
|
|
|
if key in my_dict: |
|
my_dict[key].append(element) |
|
else: |
|
my_dict[key] = [element] |
|
|
|
def to_safe(self, word): |
|
""" Converts 'bad' characters in a string to underscores so they can be used as Ansible groups """ |
|
|
|
return re.sub("[^A-Za-z0-9\-]", "_", word) |
|
|
|
def json_format_dict(self, data, pretty=False): |
|
""" Converts a dict to a JSON object and dumps it as a formatted string """ |
|
|
|
if pretty: |
|
return json.dumps(data, sort_keys=True, indent=2) |
|
else: |
|
return json.dumps(data) |
|
|
|
CombinedInventory() |