2014-04-16 00:36:38 +02:00
|
|
|
#!/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
|
2017-01-28 00:20:31 +01:00
|
|
|
virtualdatecenter: Creates a host group for each virtualdatacenter containing all hosts defined on it
|
2014-04-16 00:36:38 +02:00
|
|
|
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 <daniel.beneyto@abiquo.com>
|
|
|
|
#
|
|
|
|
# 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import time
|
|
|
|
import ConfigParser
|
|
|
|
|
2018-08-10 18:13:29 +02:00
|
|
|
import json
|
2014-04-16 00:36:38 +02:00
|
|
|
|
2015-09-15 16:58:52 +02:00
|
|
|
from ansible.module_utils.urls import open_url
|
|
|
|
|
2017-05-09 23:38:08 +02:00
|
|
|
|
2014-04-16 00:36:38 +02:00
|
|
|
def api_get(link, config):
|
|
|
|
try:
|
2017-01-28 09:12:11 +01:00
|
|
|
if link is None:
|
2017-05-09 23:38:08 +02:00
|
|
|
url = config.get('api', 'uri') + config.get('api', 'login_path')
|
|
|
|
headers = {"Accept": config.get('api', 'login_type')}
|
2014-04-16 00:36:38 +02:00
|
|
|
else:
|
2015-09-15 16:58:52 +02:00
|
|
|
url = link['href'] + '?limit=0'
|
|
|
|
headers = {"Accept": link['type']}
|
2017-05-09 23:38:08 +02:00
|
|
|
result = open_url(url, headers=headers, url_username=config.get('auth', 'apiuser').replace('\n', ''),
|
|
|
|
url_password=config.get('auth', 'apipass').replace('\n', ''))
|
2014-04-16 00:36:38 +02:00
|
|
|
return json.loads(result.read())
|
2018-09-08 02:59:46 +02:00
|
|
|
except Exception:
|
2014-04-16 00:36:38 +02:00
|
|
|
return None
|
|
|
|
|
2017-05-09 23:38:08 +02:00
|
|
|
|
2014-04-16 00:36:38 +02:00
|
|
|
def save_cache(data, config):
|
|
|
|
''' saves item to cache '''
|
2017-05-09 23:38:08 +02:00
|
|
|
dpath = config.get('cache', 'cache_dir')
|
2014-04-16 00:36:38 +02:00
|
|
|
try:
|
2017-05-09 23:38:08 +02:00
|
|
|
cache = open('/'.join([dpath, 'inventory']), 'w')
|
2014-04-16 00:36:38 +02:00
|
|
|
cache.write(json.dumps(data))
|
|
|
|
cache.close()
|
2015-08-27 20:58:51 +02:00
|
|
|
except IOError as e:
|
2017-05-09 23:38:08 +02:00
|
|
|
pass # not really sure what to do here
|
2014-04-16 00:36:38 +02:00
|
|
|
|
|
|
|
|
|
|
|
def get_cache(cache_item, config):
|
|
|
|
''' returns cached item '''
|
2017-05-09 23:38:08 +02:00
|
|
|
dpath = config.get('cache', 'cache_dir')
|
2014-04-16 00:36:38 +02:00
|
|
|
inv = {}
|
|
|
|
try:
|
2017-05-09 23:38:08 +02:00
|
|
|
cache = open('/'.join([dpath, 'inventory']), 'r')
|
2014-04-16 00:36:38 +02:00
|
|
|
inv = cache.read()
|
|
|
|
cache.close()
|
2015-08-27 20:58:51 +02:00
|
|
|
except IOError as e:
|
2017-05-09 23:38:08 +02:00
|
|
|
pass # not really sure what to do here
|
2014-04-16 00:36:38 +02:00
|
|
|
|
|
|
|
return inv
|
|
|
|
|
2017-05-09 23:38:08 +02:00
|
|
|
|
2014-04-16 00:36:38 +02:00
|
|
|
def cache_available(config):
|
|
|
|
''' checks if we have a 'fresh' cache available for item requested '''
|
|
|
|
|
2017-05-09 23:38:08 +02:00
|
|
|
if config.has_option('cache', 'cache_dir'):
|
|
|
|
dpath = config.get('cache', 'cache_dir')
|
2014-04-16 00:36:38 +02:00
|
|
|
|
|
|
|
try:
|
2017-05-09 23:38:08 +02:00
|
|
|
existing = os.stat('/'.join([dpath, 'inventory']))
|
2018-09-08 02:59:46 +02:00
|
|
|
except Exception:
|
2014-04-16 00:36:38 +02:00
|
|
|
# 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')
|
2017-05-09 23:38:08 +02:00
|
|
|
if (int(time.time()) - int(existing.st_mtime)) <= int(maxage):
|
2014-04-16 00:36:38 +02:00
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
2017-05-09 23:38:08 +02:00
|
|
|
|
|
|
|
def generate_inv_from_api(enterprise_entity, config):
|
2014-04-16 00:36:38 +02:00
|
|
|
try:
|
|
|
|
inventory['all'] = {}
|
|
|
|
inventory['all']['children'] = []
|
|
|
|
inventory['all']['hosts'] = []
|
|
|
|
inventory['_meta'] = {}
|
|
|
|
inventory['_meta']['hostvars'] = {}
|
|
|
|
|
2017-05-09 23:38:08 +02:00
|
|
|
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)
|
2014-04-16 00:36:38 +02:00
|
|
|
for vmcollection in vms['collection']:
|
2017-02-07 16:49:55 +01:00
|
|
|
for link in vmcollection['links']:
|
|
|
|
if link['rel'] == 'virtualappliance':
|
2017-05-09 23:38:08 +02:00
|
|
|
vm_vapp = link['title'].replace('[', '').replace(']', '').replace(' ', '_')
|
2017-02-07 16:49:55 +01:00
|
|
|
elif link['rel'] == 'virtualdatacenter':
|
2017-05-09 23:38:08 +02:00
|
|
|
vm_vdc = link['title'].replace('[', '').replace(']', '').replace(' ', '_')
|
2017-02-07 16:49:55 +01:00
|
|
|
elif link['rel'] == 'virtualmachinetemplate':
|
2017-05-09 23:38:08 +02:00
|
|
|
vm_template = link['title'].replace('[', '').replace(']', '').replace(' ', '_')
|
2014-04-16 00:36:38 +02:00
|
|
|
|
|
|
|
# From abiquo.ini: Only adding to inventory VMs with public IP
|
2017-02-07 16:49:55 +01:00
|
|
|
if config.getboolean('defaults', 'public_ip_only') is True:
|
2014-04-16 00:36:38 +02:00
|
|
|
for link in vmcollection['links']:
|
2017-05-09 23:38:08 +02:00
|
|
|
if link['type'] == 'application/vnd.abiquo.publicip+json' and link['rel'] == 'ip':
|
2017-01-31 00:01:47 +01:00
|
|
|
vm_nic = link['title']
|
|
|
|
break
|
2014-04-16 00:36:38 +02:00
|
|
|
else:
|
2017-01-31 00:01:47 +01:00
|
|
|
vm_nic = None
|
2014-04-16 00:36:38 +02:00
|
|
|
# Otherwise, assigning defined network interface IP address
|
|
|
|
else:
|
|
|
|
for link in vmcollection['links']:
|
2017-02-07 16:49:55 +01:00
|
|
|
if link['rel'] == config.get('defaults', 'default_net_interface'):
|
2017-01-31 00:01:47 +01:00
|
|
|
vm_nic = link['title']
|
|
|
|
break
|
2014-04-16 00:36:38 +02:00
|
|
|
else:
|
2017-01-31 00:01:47 +01:00
|
|
|
vm_nic = None
|
2017-01-28 00:45:23 +01:00
|
|
|
|
2014-04-16 00:36:38 +02:00
|
|
|
vm_state = True
|
|
|
|
# From abiquo.ini: Only adding to inventory VMs deployed
|
2017-02-07 16:49:55 +01:00
|
|
|
if config.getboolean('defaults', 'deployed_only') is True and vmcollection['state'] == 'NOT_ALLOCATED':
|
2014-04-16 00:36:38 +02:00
|
|
|
vm_state = False
|
|
|
|
|
2017-01-28 09:12:11 +01:00
|
|
|
if vm_nic is not None and vm_state:
|
2016-11-17 15:08:12 +01:00
|
|
|
if vm_vapp not in inventory:
|
2014-04-16 00:36:38 +02:00
|
|
|
inventory[vm_vapp] = {}
|
|
|
|
inventory[vm_vapp]['children'] = []
|
|
|
|
inventory[vm_vapp]['hosts'] = []
|
2016-11-17 15:08:12 +01:00
|
|
|
if vm_vdc not in inventory:
|
2014-04-16 00:36:38 +02:00
|
|
|
inventory[vm_vdc] = {}
|
|
|
|
inventory[vm_vdc]['hosts'] = []
|
|
|
|
inventory[vm_vdc]['children'] = []
|
2016-11-17 15:08:12 +01:00
|
|
|
if vm_template not in inventory:
|
2014-04-16 00:36:38 +02:00
|
|
|
inventory[vm_template] = {}
|
|
|
|
inventory[vm_template]['children'] = []
|
|
|
|
inventory[vm_template]['hosts'] = []
|
2017-02-07 16:49:55 +01:00
|
|
|
if config.getboolean('defaults', 'get_metadata') is True:
|
2017-05-09 23:38:08 +02:00
|
|
|
meta_entity = next(link for link in vmcollection['links'] if link['rel'] == 'metadata')
|
2014-04-16 00:36:38 +02:00
|
|
|
try:
|
2017-05-09 23:38:08 +02:00
|
|
|
metadata = api_get(meta_entity, config)
|
|
|
|
if (config.getfloat("api", "version") >= 3.0):
|
2014-08-28 16:22:06 +02:00
|
|
|
vm_metadata = metadata['metadata']
|
|
|
|
else:
|
|
|
|
vm_metadata = metadata['metadata']['metadata']
|
2014-09-02 18:00:12 +02:00
|
|
|
inventory['_meta']['hostvars'][vm_nic] = vm_metadata
|
2015-08-27 20:58:51 +02:00
|
|
|
except Exception as e:
|
2014-04-16 00:36:38 +02:00
|
|
|
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
|
2015-08-27 20:58:51 +02:00
|
|
|
except Exception as e:
|
2014-04-16 00:36:38 +02:00
|
|
|
# Return empty hosts output
|
2017-05-09 23:38:08 +02:00
|
|
|
return {'all': {'hosts': []}, '_meta': {'hostvars': {}}}
|
|
|
|
|
2014-04-16 00:36:38 +02:00
|
|
|
|
|
|
|
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 #
|
2017-05-09 23:38:08 +02:00
|
|
|
inv = generate_inv_from_api(enterprise, config)
|
2014-04-16 00:36:38 +02:00
|
|
|
|
|
|
|
save_cache(inv, config)
|
|
|
|
return json.dumps(inv)
|
|
|
|
|
2017-05-09 23:38:08 +02:00
|
|
|
|
2014-04-16 00:36:38 +02:00
|
|
|
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:
|
2017-05-09 23:38:08 +02:00
|
|
|
login = api_get(None, config)
|
|
|
|
enterprise = next(link for link in login['links'] if link['rel'] == 'enterprise')
|
2015-08-27 20:58:51 +02:00
|
|
|
except Exception as e:
|
2014-04-16 00:36:38 +02:00
|
|
|
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()
|