Skip to content

Instantly share code, notes, and snippets.

@savishy
Created September 21, 2019 07:26
Show Gist options
  • Save savishy/51958a10a67a017e2f2308c3487de5fa to your computer and use it in GitHub Desktop.
Save savishy/51958a10a67a017e2f2308c3487de5fa to your computer and use it in GitHub Desktop.
Ansible Inventory Plugin for OneOps
[defaults]
# Search path for inventory plugin
inventory_plugins = ./inventory/
[inventory]
# enable plugin
enable_plugins = oneopsplugin, host_list, script, auto, yaml, ini
plugin: oneopsplugin
oo_root: https://oneops.your.org.com
oo_org: oneops_org_name
oo_env: oneops_env_name
oo_assembly: oneops_assembly_name
oo_platforms:
oneops_platform:
ansible_user: user_with_ssh_access
ansible_ssh_private_key_file: ~/.ssh/id_rsa
oo_group_children: []
import requests,json,urllib3,os
# disable SSL warnings
urllib3.disable_warnings()
# import lib for logging.
try:
from __main__ import display
except ImportError:
from ansible.utils.display import Display
display = Display()
from ansible.plugins.inventory import BaseInventoryPlugin
DOCUMENTATION = '''
name: oneopsplugin
plugin_type: inventory
short_description: OneOps inventory source
description:
- Use OneOps API to retrieve compute instances from the specified environment.
- Group computes by platform and create an Ansible inventory.
- NOTE: only specific platforms will be retrieved, those which match a dictionary.
- Takes a YAML configuration file ('oneops.yml') that specifies the OneOps coordinates.
options:
plugin:
description: token that ensures this is a source file for the 'oneopsplugin' plugin
required: True
choices: ['oneopsplugin']
oo_root:
description: OneOps Host URL.
type: string
required: True
oo_org:
description: OneOps Organization Name. Note that your API Token must have access to this organization.
type: string
required: True
oo_assembly:
description: OneOps Assembly Name. Note that your API token must have access to this assembly.
type: string
required: True
oo_env:
description: OneOps Environment that contains the platforms and associated computes.
type: string
required: True
oo_platforms:
description: A list of OneOps Platforms that you want to use to generate the inventory.
type: dictionary
required: True
oo_group_children:
description: A dictionary that contains lists of custom groupings (e.g. groups of groups) that you want to create.
type: dictionary
required: True
'''
class OneOpsInventory:
def __init__(self,oo_root,oo_org,oo_assembly,oo_env,oo_platforms):
self.oo_api_token = os.environ['ONEOPS_API_TOKEN']
self.oo_root = oo_root
self.oo_org = oo_org
self.oo_assembly = oo_assembly
self.oo_env = oo_env
self.oo_platforms = oo_platforms
def _get_oneops_platforms(self):
'''
Call OneOps API to get OneOps Platforms in Env.
Filter out by allowed platforms.
'''
endpoint = '/'.join([self.oo_root,self.oo_org,'assemblies',self.oo_assembly,
'operations','environments',self.oo_env,'platforms.json'])
raw = requests.get(endpoint,
auth=(self.oo_api_token,''),
verify=False)
raw.raise_for_status()
# return platform names which are present in dictionary.
return [r['ciName'] for r in json.loads(raw.content.decode('utf-8')) if r['ciName'] in self.oo_platforms.keys()]
def _get_oneops_computes(self,oo_platform):
'''
Call OneOps API to get OneOps Computes.
'''
oo_platform_fqdn = '.'.join([oo_platform,self.oo_env,self.oo_assembly,
self.oo_org,'stg-az-southcentralus-2',
'prod','us','walmart','net'
])
endpoint = '/'.join([self.oo_root,self.oo_org,'assemblies',self.oo_assembly,
'operations','environments',self.oo_env,'platforms',oo_platform,
'components','compute','instances.json?instances_state=all'])
r = requests.get(endpoint,
auth=(self.oo_api_token,''),
verify=False)
retval_hosts = []
retval_meta_hostvars = {}
for r in json.loads(r.content.decode('utf-8')):
hostname = r['ciAttributes']['instance_name']
display.vvvv(str(r['ciAttributes']))
ipaddr = r['ciAttributes']['public_ip']
display.vvvv('platform: {0} hostname: {1} ipaddr: {2}'.format(oo_platform,hostname,ipaddr))
retval_hosts.append(hostname)
retval_meta_hostvars[hostname] = {}
retval_meta_hostvars[hostname]['ansible_host'] = ipaddr
retval_meta_hostvars[hostname]['ansible_user'] = self.oo_platforms[oo_platform]['ansible_user']
retval_meta_hostvars[hostname]['ansible_ssh_private_key_file'] = self.oo_platforms[oo_platform]['ansible_ssh_private_key_file']
retval_meta_hostvars[hostname]['oo_platform_fqdn'] = oo_platform_fqdn
return (retval_hosts,retval_meta_hostvars)
def oneops_inventory(self):
'''
Call internal methods and return an Ansible inventory object.
'''
retval = {
'_meta': {
'hostvars': {}
},
'all': {
'children': []
}
}
oo_platforms = self._get_oneops_platforms()
for oo_platform in oo_platforms:
group_name = oo_platform.split('-')[-1]
retval[group_name] = {'hosts':[]}
(hosts,meta) = (self._get_oneops_computes(oo_platform=oo_platform))
# concat 'hosts' list.
retval[group_name]['hosts'] += hosts
# update '_meta' dictionary.
retval['_meta']['hostvars'].update(meta)
retval['all']['children'].append(group_name)
return retval
class InventoryModule(BaseInventoryPlugin):
NAME = 'oneopsplugin' # used internally by Ansible, it should match the file name but not required
def verify_file(self, path):
''' return true/false if this is possibly a valid file for this plugin to consume '''
valid = False
if super(InventoryModule, self).verify_file(path):
# base class verifies that file exists and is readable by current user
if path.endswith('.yml') or path.endswith('.yaml'):
valid = True
return valid
def parse(self, inventory, loader, path, cache=False):
# call base method to ensure properties are available for use with other helper method
super(InventoryModule, self).parse(inventory, loader, path, cache)
display.vvvv('inventory source: {}'.format(path))
# this method will parse 'common format' inventory sources and
# update any options declared in DOCUMENTATION as needed
config = self._read_config_data(path)
# if NOT using _read_config_data you should call set_options directly,
# to process any defined configuration for this plugin,
# if you dont define any options you can skip
# self.set_options()
# example consuming options from inventory source
oo_inventory = OneOpsInventory(oo_root=self.get_option('oo_root'),
oo_org=self.get_option('oo_org'),
oo_assembly=self.get_option('oo_assembly'),
oo_env=self.get_option('oo_env'),
oo_platforms=self.get_option('oo_platforms'))
inventory = oo_inventory.oneops_inventory()
display.vvvv(str(inventory))
#parse data and create inventory objects:
for group in inventory:
if group not in ['_meta','all']:
# update list of groups.
self.inventory.add_group(group)
if 'hosts' in inventory[group]:
for host in inventory[group]['hosts']:
# add host to inventory with group.
self.inventory.add_host(host,group=group)
# process hostvars for this host.
hostvars = inventory['_meta']['hostvars'][host]
for hostvar in hostvars:
self.inventory.set_variable(host,hostvar,inventory['_meta']['hostvars'][host][hostvar])
# create custom grouping as defined in inventory source.
oo_group_children = self.get_option('oo_group_children')
for group in oo_group_children:
self.inventory.add_group(group)
for child in oo_group_children[group]:
self.inventory.add_child(group,child)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment