| #!/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. |
| # |
| """A python script that converts TrafficOps information into an Ansible Inventory""" |
| |
| import json |
| import argparse |
| import os |
| import collections |
| from trafficops.tosession import TOSession |
| |
| |
| def empty_inventory(): |
| """Generate a valid empty inventory""" |
| return {'_meta': {'hostvars': {}}} |
| |
| |
| class AnsibleInventory(): |
| """Wrapper class for needed methods""" |
| |
| def __init__(self, user, password, url, verify_cert): |
| """Init base members""" |
| self.to_user = user |
| self.to_pass = password |
| self.to_url = url |
| self.verify_cert = verify_cert |
| |
| @staticmethod |
| def populate_server_profile_vars(api, profile_id): |
| """Generate the server profile variables once as we see it""" |
| server_vars = {} |
| server_vars['hosts'] = [] |
| server_vars['vars'] = {} |
| profile = api.get_profiles(id=profile_id)[0] |
| server_vars['vars']['server_profile_description'] = profile[0]['description'] |
| server_vars['vars']['server_profile_type'] = profile[0]['type'] |
| server_vars['vars']['server_profile_routingDisabled'] = profile[0]['routingDisabled'] |
| server_vars['vars']['server_profile_parameters'] = [] |
| params = api.get_parameters_by_profile_id(id=profile_id)[0] |
| for param in params: |
| tmp_param = { |
| 'name': param['name'], |
| 'value': param['value'], |
| 'configFile': param['configFile']} |
| server_vars['vars']['server_profile_parameters'].append(tmp_param) |
| return server_vars |
| |
| @staticmethod |
| def populate_cachegroups(api, cachegroup_id): |
| """Generate the values for cachegroups once on first sight""" |
| var_data = {} |
| cgdata = collections.namedtuple( |
| 'Cgdata', [ |
| 'cgvars', 'primary_parent_group_name', 'secondary_parent_group_name']) |
| var_data['hosts'] = [] |
| var_data['vars'] = {} |
| cachegroup = api.get_cachegroups(id=cachegroup_id)[0] |
| var_data['vars']['cachegroup_name'] = cachegroup[0]['name'] |
| var_data['vars']['cachegroup_shortName'] = cachegroup[0]['shortName'] |
| var_data['vars']['cachegroup_parentCachegroupName'] = \ |
| cachegroup[0]['parentCachegroupName'] |
| var_data['vars']['cachegroup_secondaryParentCachegroupName'] = \ |
| cachegroup[0]['secondaryParentCachegroupName'] |
| var_data['vars']['cachegroup_typeName'] = cachegroup[0]['typeName'] |
| if cachegroup[0]['parentCachegroupName'] is None: |
| flat_parent_cg = "parentCachegroup|None" |
| else: |
| flat_parent_cg = "parentCachegroup|" + \ |
| cachegroup[0]['parentCachegroupName'] |
| |
| if cachegroup[0]['secondaryParentCachegroupName'] is None: |
| flat_second_parent_cg = "secondaryParentCachegroup|None" |
| else: |
| flat_second_parent_cg = "secondaryParentCachegroup|" + \ |
| cachegroup[0]['secondaryParentCachegroupName'] |
| out = cgdata(cgvars=var_data, |
| primary_parent_group_name=flat_parent_cg, |
| secondary_parent_group_name=flat_second_parent_cg) |
| return out |
| |
| def generate_inventory_list(self, target_to): |
| """Generate the inventory list for the specified TrafficOps instance""" |
| with TOSession(self.to_url, verify_cert=self.verify_cert) as traffic_ops_api: |
| traffic_ops_api.login(self.to_user, self.to_pass) |
| servers = traffic_ops_api.get_servers()[0] |
| out = {} |
| out['_meta'] = {} |
| out['_meta']['hostvars'] = {} |
| out[target_to] = {} |
| out[target_to]['hosts'] = [] |
| out["ungrouped"] = {} |
| out['ungrouped']['hosts'] = [] |
| out['cachegroup'] = {} |
| out['cachegroup']['children'] = [] |
| out['server_type'] = {} |
| out['server_type']['children'] = [] |
| out['server_cdnName'] = {} |
| out['server_cdnName']['children'] = [] |
| out['server_profile'] = {} |
| out['server_profile']['children'] = [] |
| out['server_status'] = {} |
| out['server_status']['children'] = [] |
| for server in servers: |
| fqdn = server['hostName'] + '.' + server['domainName'] |
| out["ungrouped"]['hosts'].append(fqdn) |
| out[target_to]['hosts'].append(fqdn) |
| out['_meta']['hostvars'][fqdn] = {} |
| out['_meta']['hostvars'][fqdn]['server_toFQDN'] = target_to |
| out['_meta']['hostvars'][fqdn]['server_cachegroup'] = server['cachegroup'] |
| out['_meta']['hostvars'][fqdn]['server_cdnName'] = server['cdnName'] |
| out['_meta']['hostvars'][fqdn]['server_id'] = server['id'] |
| out['_meta']['hostvars'][fqdn]['server_ipAddress'] = server['ipAddress'] |
| out['_meta']['hostvars'][fqdn]['server_ip6Address'] = server['ip6Address'] |
| out['_meta']['hostvars'][fqdn]['server_offlineReason'] = server['offlineReason'] |
| out['_meta']['hostvars'][fqdn]['server_physLocation'] = server['physLocation'] |
| out['_meta']['hostvars'][fqdn]['server_profile'] = server['profile'] |
| out['_meta']['hostvars'][fqdn]['server_profileDesc'] = server['profileDesc'] |
| out['_meta']['hostvars'][fqdn]['server_status'] = server['status'] |
| out['_meta']['hostvars'][fqdn]['server_type'] = server['type'] |
| flat_server_profile = "server_profile|" + server['profile'] |
| flat_cachegroup = "cachegroup|" + server['cachegroup'] |
| flat_server_type = "server_type|" + server['type'] |
| flat_server_cdn_name = "server_cdnName|" + server['cdnName'] |
| flat_server_status = "server_status|" + server['status'] |
| if flat_server_profile not in out: |
| out['server_profile']['children'].append( |
| flat_server_profile) |
| out[flat_server_profile] = self.populate_server_profile_vars( |
| traffic_ops_api, server['profileId']) |
| out[flat_server_profile]['hosts'].append(fqdn) |
| if flat_cachegroup not in out: |
| out['cachegroup']['children'].append(flat_cachegroup) |
| cgdata = self.populate_cachegroups( |
| traffic_ops_api, |
| server['cachegroupId']) |
| out[flat_cachegroup] = cgdata.cgvars |
| flat_parent_cg = cgdata.primary_parent_group_name |
| flat_second_parent_cg = cgdata.secondary_parent_group_name |
| if flat_parent_cg not in out: |
| out[flat_parent_cg] = {} |
| out[flat_parent_cg]['children'] = [] |
| if flat_second_parent_cg not in out: |
| out[flat_second_parent_cg] = {} |
| out[flat_second_parent_cg]['children'] = [] |
| out[flat_parent_cg]['children'].append(flat_cachegroup) |
| out[flat_second_parent_cg]['children'].append( |
| flat_cachegroup) |
| out[flat_cachegroup]['hosts'].append(fqdn) |
| if flat_server_type not in out: |
| out['server_type']['children'].append(flat_server_type) |
| out[flat_server_type] = {} |
| out[flat_server_type]['hosts'] = [] |
| out[flat_server_type]['hosts'].append(fqdn) |
| if flat_server_cdn_name not in out: |
| out['server_cdnName']['children'].append( |
| flat_server_cdn_name) |
| out[flat_server_cdn_name] = {} |
| out[flat_server_cdn_name]['hosts'] = [] |
| out[flat_server_cdn_name]['hosts'].append(fqdn) |
| if flat_server_status not in out: |
| out['server_status']['children'].append(flat_server_status) |
| out[flat_server_status] = {} |
| out[flat_server_status]['hosts'] = [] |
| out[flat_server_status]['hosts'].append(fqdn) |
| return out |
| |
| def to_inventory(self): |
| """A wrapper function blending the target url in""" |
| return self.generate_inventory_list(self.to_url) |
| |
| # |
| # Thanks to Maxim for the snipit on handling bool parameters. |
| # https://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse |
| # |
| |
| |
| def str2bool(x): |
| """A helper function to help with truthiness""" |
| if isinstance(x, bool): |
| return x |
| if x.lower() in ('yes', 'true', 't', 'y', '1'): |
| return True |
| if x.lower() in ('no', 'false', 'f', 'n', '0'): |
| return False |
| raise argparse.ArgumentTypeError('Boolean value expected.') |
| |
| |
| if __name__ == "__main__": |
| PARSER = argparse.ArgumentParser( |
| description='Generate an Ansible inventory from TrafficOps') |
| |
| PARSER.add_argument( |
| '--username', |
| type=str, |
| metavar='username', |
| default=os.environ.get('TO_USER', None), |
| help='TrafficOps Username. Environment Var: TO_USER Default: None') |
| PARSER.add_argument( |
| '--password', |
| type=str, |
| metavar='password', |
| default=os.environ.get('TO_PASSWORD', None), |
| help='TrafficOps Password. Environment Var: TO_PASSWORD Default: None') |
| PARSER.add_argument( |
| '--url', |
| type=str, |
| metavar='to.kabletown.invalid:8443', |
| default=os.environ.get('TO_URL', None), |
| help='TrafficOps FQDN and optional HTTPS Port. Environment Var: TO_URL Default: None') |
| PARSER.add_argument( |
| '--verify_cert', |
| type=str2bool, |
| default=os.environ.get('TO_VERIFY_CERT', "true"), |
| metavar="(true, false, yes, no, t, f, y, n, 0, or 1)", |
| help='Perform SSL Certificate Verification. Environment Var: TO_VERIFY_CERT Default: true') |
| PARSER.add_argument( |
| '--list', |
| action='store_true', |
| help='Primary argument to enable retrieval of TO data. Required per calling convention.') |
| PARSER.add_argument( |
| '--host', |
| type=str, |
| metavar='do_not_use', |
| default=None, |
| help='Ignored parameter that must be present due to calling convention.') |
| ARGS = PARSER.parse_args() |
| |
| if ARGS.username and ARGS.password and ARGS.url: |
| if ARGS.list: |
| INVENTORY = AnsibleInventory( |
| ARGS.username, |
| ARGS.password, |
| ARGS.url, |
| ARGS.verify_cert).to_inventory() |
| # Since we're supplying hostvar metadata, --host support isn't required |
| else: |
| INVENTORY = empty_inventory() |
| else: |
| INVENTORY = empty_inventory() |
| |
| print(json.dumps(INVENTORY)) |