diff --git a/plugins/inventory/abiquo.ini b/plugins/inventory/abiquo.ini new file mode 100644 index 0000000000..e59be95c02 --- /dev/null +++ b/plugins/inventory/abiquo.ini @@ -0,0 +1,23 @@ +# Ansible external inventory script settings for Abiquo +# +[auth] +apiuser = admin +apipass = xabiquo + +[api] +uri = http://192.168.2.211:80/api +# You probably won't need to modify login preferences, but just in case +login_path = /login +login_type = application/vnd.abiquo.user+json + +[cache] +cache_max_age = 30 +cache_dir = /var/tmp + +[defaults] +public_ip_only = false +# default_net_interface only is used if public_ip_only = false +default_net_interface = nic0 +# Only deployed VM are added to output +deployed_only = true +get_metadata = false diff --git a/plugins/inventory/abiquo.py b/plugins/inventory/abiquo.py new file mode 100755 index 0000000000..c3c4e3a58b --- /dev/null +++ b/plugins/inventory/abiquo.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +''' +External inventory script for Abiquo +==================================== + +Shamelessly copied from an existing inventory script. + +This script generates an inventory that Ansible can understand by making API requests to Abiquo API +Requires some python libraries, ensure to have them installed when using this script. + +This script has been tested in Abiquo 3.0 but it may work also for Abiquo 2.6. + +Before using this script you may want to modify abiquo.ini config file. + +This script generates an Ansible hosts file with these host groups: + +ABQ_xxx: Defines a hosts itself by Abiquo VM name label +all: Contains all hosts defined in Abiquo user's enterprise +virtualdatecenter: Creates a host group for each virtualdatacenter containing all hosts defined on it +virtualappliance: Creates a host group for each virtualappliance containing all hosts defined on it +imagetemplate: Creates a host group for each image template containing all hosts using it + +''' + +# (c) 2014, Daniel Beneyto +# +# This file is part of Ansible, +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +import os +import sys +import time +import ConfigParser +import urllib2 +import base64 + +try: + import json +except ImportError: + import simplejson as json + +def api_get(link, config): + try: + if link == None: + request = urllib2.Request(config.get('api','uri')+config.get('api','login_path')) + request.add_header("Accept",config.get('api','login_type')) + else: + request = urllib2.Request(link['href']+'?limit=0') + request.add_header("Accept",link['type']) + # Auth + base64string = base64.encodestring('%s:%s' % (config.get('auth','apiuser'),config.get('auth','apipass'))).replace('\n', '') + request.add_header("Authorization", "Basic %s" % base64string) + result = urllib2.urlopen(request) + return json.loads(result.read()) + except: + return None + +def save_cache(data, config): + ''' saves item to cache ''' + dpath = config.get('cache','cache_dir') + try: + cache = open('/'.join([dpath,'inventory']), 'w') + cache.write(json.dumps(data)) + cache.close() + except IOError, e: + pass # not really sure what to do here + + +def get_cache(cache_item, config): + ''' returns cached item ''' + dpath = config.get('cache','cache_dir') + inv = {} + try: + cache = open('/'.join([dpath,'inventory']), 'r') + inv = cache.read() + cache.close() + except IOError, e: + pass # not really sure what to do here + + return inv + +def cache_available(config): + ''' checks if we have a 'fresh' cache available for item requested ''' + + if config.has_option('cache','cache_dir'): + dpath = config.get('cache','cache_dir') + + try: + existing = os.stat( '/'.join([dpath,'inventory'])) + except: + # cache doesn't exist or isn't accessible + return False + + if config.has_option('cache', 'cache_max_age'): + maxage = config.get('cache', 'cache_max_age') + if ((int(time.time()) - int(existing.st_mtime)) <= int(maxage)): + return True + + return False + +def generate_inv_from_api(enterprise_entity,config): + try: + inventory['all'] = {} + inventory['all']['children'] = [] + inventory['all']['hosts'] = [] + inventory['_meta'] = {} + inventory['_meta']['hostvars'] = {} + + enterprise = api_get(enterprise_entity,config) + vms_entity = next(link for link in (enterprise['links']) if (link['rel']=='virtualmachines')) + vms = api_get(vms_entity,config) + for vmcollection in vms['collection']: + vm_vapp = next(link for link in (vmcollection['links']) if (link['rel']=='virtualappliance'))['title'].replace('[','').replace(']','').replace(' ','_') + vm_vdc = next(link for link in (vmcollection['links']) if (link['rel']=='virtualdatacenter'))['title'].replace('[','').replace(']','').replace(' ','_') + vm_template = next(link for link in (vmcollection['links']) if (link['rel']=='virtualmachinetemplate'))['title'].replace('[','').replace(']','').replace(' ','_') + + # From abiquo.ini: Only adding to inventory VMs with public IP + if (config.getboolean('defaults', 'public_ip_only')) == True: + for link in vmcollection['links']: + if (link['type']=='application/vnd.abiquo.publicip+json' and link['rel']=='ip'): + vm_nic = link['title'] + break + else: + vm_nic = None + # Otherwise, assigning defined network interface IP address + else: + for link in vmcollection['links']: + if (link['rel']==config.get('defaults', 'default_net_interface')): + vm_nic = link['title'] + break + else: + vm_nic = None + + vm_state = True + # From abiquo.ini: Only adding to inventory VMs deployed + if ((config.getboolean('defaults', 'deployed_only') == True) and (vmcollection['state'] == 'NOT_ALLOCATED')): + vm_state = False + + if not vm_nic == None and vm_state: + if not vm_vapp in inventory.keys(): + inventory[vm_vapp] = {} + inventory[vm_vapp]['children'] = [] + inventory[vm_vapp]['hosts'] = [] + if not vm_vdc in inventory.keys(): + inventory[vm_vdc] = {} + inventory[vm_vdc]['hosts'] = [] + inventory[vm_vdc]['children'] = [] + if not vm_template in inventory.keys(): + inventory[vm_template] = {} + inventory[vm_template]['children'] = [] + inventory[vm_template]['hosts'] = [] + if config.getboolean('defaults', 'get_metadata') == True: + meta_entity = next(link for link in (vmcollection['links']) if (link['rel']=='metadata')) + try: + metadata = api_get(meta_entity,config) + inventory['_meta']['hostvars'][vm_nic] = metadata['metadata']['metadata'] + except Exception, e: + pass + + inventory[vm_vapp]['children'].append(vmcollection['name']) + inventory[vm_vdc]['children'].append(vmcollection['name']) + inventory[vm_template]['children'].append(vmcollection['name']) + inventory['all']['children'].append(vmcollection['name']) + inventory[vmcollection['name']] = [] + inventory[vmcollection['name']].append(vm_nic) + + return inventory + except Exception, e: + # Return empty hosts output + return { 'all': {'hosts': []}, '_meta': { 'hostvars': {} } } + +def get_inventory(enterprise, config): + ''' Reads the inventory from cache or Abiquo api ''' + + if cache_available(config): + inv = get_cache('inventory', config) + else: + default_group = os.path.basename(sys.argv[0]).rstrip('.py') + # MAKE ABIQUO API CALLS # + inv = generate_inv_from_api(enterprise,config) + + save_cache(inv, config) + return json.dumps(inv) + +if __name__ == '__main__': + inventory = {} + enterprise = {} + + # Read config + config = ConfigParser.SafeConfigParser() + for configfilename in [os.path.abspath(sys.argv[0]).rstrip('.py') + '.ini', 'abiquo.ini']: + if os.path.exists(configfilename): + config.read(configfilename) + break + + try: + login = api_get(None,config) + enterprise = next(link for link in (login['links']) if (link['rel']=='enterprise')) + except Exception, e: + enterprise = None + + if cache_available(config): + inventory = get_cache('inventory', config) + else: + inventory = get_inventory(enterprise, config) + + # return to ansible + sys.stdout.write(str(inventory)) + sys.stdout.flush()