From 76f473cd5d5a8ed1c6c5deb173587ce01e5b8f29 Mon Sep 17 00:00:00 2001 From: Mathieu GAUTHIER-LAFAYE Date: Mon, 6 Oct 2014 17:12:03 +0200 Subject: [PATCH 1/4] add a proxmox inventory plugin --- plugins/inventory/proxmox.py | 131 +++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100755 plugins/inventory/proxmox.py diff --git a/plugins/inventory/proxmox.py b/plugins/inventory/proxmox.py new file mode 100755 index 00000000000..ceb41110278 --- /dev/null +++ b/plugins/inventory/proxmox.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +# Copyright (C) 2014 Mathieu GAUTHIER-LAFAYE +# +# This program 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. +# +# This program 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 this program. If not, see . + +import urllib +import urllib2 +try: + import json +except ImportError: + import simplejson as json +import os +import sys +from optparse import OptionParser + +class ProxmoxNodeList(list): + def get_names(self): + return [node['node'] for node in self] + +class ProxmoxQemuList(list): + def get_names(self): + return [qemu['name'] for qemu in self if qemu['template'] != 1] + +class ProxmoxPoolList(list): + def get_names(self): + return [pool['poolid'] for pool in self] + +class ProxmoxPool(dict): + def get_members_name(self): + return [member['name'] for member in self['members'] if member['template'] != 1] + +class ProxmoxAPI(object): + def __init__(self, options): + self.options = options + self.credentials = None + + if not options.url: + raise Exception('Missing mandatory parameter --url (or PROXMOX_URL).') + elif not options.username: + raise Exception('Missing mandatory parameter --username (or PROXMOX_USERNAME).') + elif not options.password: + raise Exception('Missing mandatory parameter --password (or PROXMOX_PASSWORD).') + + def auth(self): + request_path = '{}api2/json/access/ticket'.format(self.options.url) + + request_params = urllib.urlencode({ + 'username': self.options.username, + 'password': self.options.password, + }) + + data = json.load(urllib2.urlopen(request_path, request_params)) + + self.credentials = { + 'ticket': data['data']['ticket'], + 'CSRFPreventionToken': data['data']['CSRFPreventionToken'], + } + + def get(self, url, data=None): + opener = urllib2.build_opener() + opener.addheaders.append(('Cookie', 'PVEAuthCookie={}'.format(self.credentials['ticket']))) + + request_path = '{}{}'.format(self.options.url, url) + request = opener.open(request_path, data) + + response = json.load(request) + return response['data'] + + def nodes(self): + return ProxmoxNodeList(self.get('api2/json/nodes')) + + def node_qemu(self, node): + return ProxmoxQemuList(self.get('api2/json/nodes/{}/qemu'.format(node))) + + def pools(self): + return ProxmoxPoolList(self.get('api2/json/pools')) + + def pool(self, poolid): + return ProxmoxPool(self.get('api2/json/pools/{}'.format(poolid))) + +def main_list(options): + result = {} + + proxmox_api = ProxmoxAPI(options) + proxmox_api.auth() + + # all + result['all'] = [] + for node in proxmox_api.nodes().get_names(): + result['all'] += proxmox_api.node_qemu(node).get_names() + + # pools + for pool in proxmox_api.pools().get_names(): + result[pool] = proxmox_api.pool(pool).get_members_name() + + print json.dumps(result) + +def main_host(): + print json.dumps({}) + +def main(): + parser = OptionParser(usage='%prog [options] --list | --host HOSTNAME') + parser.add_option('--list', action="store_true", default=False, dest="list") + parser.add_option('--host', dest="host") + parser.add_option('--url', default=os.environ.get('PROXMOX_URL'), dest='url') + parser.add_option('--username', default=os.environ.get('PROXMOX_USERNAME'), dest='username') + parser.add_option('--password', default=os.environ.get('PROXMOX_PASSWORD'), dest='password') + (options, args) = parser.parse_args() + + if options.list: + main_list(options) + elif options.host: + main_host() + else: + parser.print_help() + sys.exit(1) + +if __name__ == '__main__': + main() From 3d62e55abe14be12292186760413ce641f852c09 Mon Sep 17 00:00:00 2001 From: Mathieu GAUTHIER-LAFAYE Date: Tue, 7 Oct 2014 13:10:10 +0200 Subject: [PATCH 2/4] add host variables (proxmox_vmid, proxmox_uptime, proxmox_maxmem, ...) --- plugins/inventory/proxmox.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/plugins/inventory/proxmox.py b/plugins/inventory/proxmox.py index ceb41110278..590949a4c66 100755 --- a/plugins/inventory/proxmox.py +++ b/plugins/inventory/proxmox.py @@ -33,6 +33,10 @@ class ProxmoxQemuList(list): def get_names(self): return [qemu['name'] for qemu in self if qemu['template'] != 1] + def get_by_name(self, name): + results = [qemu for qemu in self if qemu['name'] == name] + return results[0] if len(results) > 0 else None + class ProxmoxPoolList(list): def get_names(self): return [pool['poolid'] for pool in self] @@ -107,8 +111,24 @@ def main_list(options): print json.dumps(result) -def main_host(): - print json.dumps({}) +def main_host(options): + results = {} + + proxmox_api = ProxmoxAPI(options) + proxmox_api.auth() + + host = None + for node in proxmox_api.nodes().get_names(): + qemu_list = proxmox_api.node_qemu(node) + qemu = qemu_list.get_by_name(options.host) + if qemu: + break + + if qemu: + for key, value in qemu.iteritems(): + results['proxmox_' + key] = value + + print json.dumps(results) def main(): parser = OptionParser(usage='%prog [options] --list | --host HOSTNAME') @@ -122,7 +142,7 @@ def main(): if options.list: main_list(options) elif options.host: - main_host() + main_host(options) else: parser.print_help() sys.exit(1) From 7c094c93798eeae5af92961031125de83d6ec91d Mon Sep 17 00:00:00 2001 From: Mathieu GAUTHIER-LAFAYE Date: Tue, 7 Oct 2014 13:45:41 +0200 Subject: [PATCH 3/4] add _meta in the list json --- plugins/inventory/proxmox.py | 56 +++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/plugins/inventory/proxmox.py b/plugins/inventory/proxmox.py index 590949a4c66..c9d5e82a623 100755 --- a/plugins/inventory/proxmox.py +++ b/plugins/inventory/proxmox.py @@ -29,7 +29,18 @@ class ProxmoxNodeList(list): def get_names(self): return [node['node'] for node in self] +class ProxmoxQemu(dict): + def get_variables(self): + variables = {} + for key, value in self.iteritems(): + variables['proxmox_' + key] = value + return variables + class ProxmoxQemuList(list): + def __init__(self, data=[]): + for item in data: + self.append(ProxmoxQemu(item)) + def get_names(self): return [qemu['name'] for qemu in self if qemu['template'] != 1] @@ -37,6 +48,13 @@ class ProxmoxQemuList(list): results = [qemu for qemu in self if qemu['name'] == name] return results[0] if len(results) > 0 else None + def get_variables(self): + variables = {} + for qemu in self: + variables[qemu['name']] = qemu.get_variables() + + return variables + class ProxmoxPoolList(list): def get_names(self): return [pool['poolid'] for pool in self] @@ -95,40 +113,42 @@ class ProxmoxAPI(object): return ProxmoxPool(self.get('api2/json/pools/{}'.format(poolid))) def main_list(options): - result = {} + results = { + 'all': { + 'hosts': [], + }, + '_meta': { + 'hostvars': {}, + } + } proxmox_api = ProxmoxAPI(options) proxmox_api.auth() - # all - result['all'] = [] for node in proxmox_api.nodes().get_names(): - result['all'] += proxmox_api.node_qemu(node).get_names() + qemu_list = proxmox_api.node_qemu(node) + results['all']['hosts'] += qemu_list.get_names() + results['_meta']['hostvars'].update(qemu_list.get_variables()) # pools for pool in proxmox_api.pools().get_names(): - result[pool] = proxmox_api.pool(pool).get_members_name() + results[pool] = { + 'hosts': proxmox_api.pool(pool).get_members_name(), + } - print json.dumps(result) + return json.dumps(results) def main_host(options): - results = {} - proxmox_api = ProxmoxAPI(options) proxmox_api.auth() - host = None for node in proxmox_api.nodes().get_names(): qemu_list = proxmox_api.node_qemu(node) qemu = qemu_list.get_by_name(options.host) if qemu: - break + return json.dumps(qemu.get_variables()) - if qemu: - for key, value in qemu.iteritems(): - results['proxmox_' + key] = value - - print json.dumps(results) + print json.dumps({}) def main(): parser = OptionParser(usage='%prog [options] --list | --host HOSTNAME') @@ -140,12 +160,14 @@ def main(): (options, args) = parser.parse_args() if options.list: - main_list(options) + json = main_list(options) elif options.host: - main_host(options) + json = main_host(options) else: parser.print_help() sys.exit(1) + print json + if __name__ == '__main__': main() From d20ef3a10af5dada0a3e3b3c1f7b15fee3839990 Mon Sep 17 00:00:00 2001 From: Mathieu GAUTHIER-LAFAYE Date: Tue, 7 Oct 2014 13:58:01 +0200 Subject: [PATCH 4/4] add --pretty for debuging purpose --- plugins/inventory/proxmox.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/plugins/inventory/proxmox.py b/plugins/inventory/proxmox.py index c9d5e82a623..80f6628d973 100755 --- a/plugins/inventory/proxmox.py +++ b/plugins/inventory/proxmox.py @@ -136,7 +136,7 @@ def main_list(options): 'hosts': proxmox_api.pool(pool).get_members_name(), } - return json.dumps(results) + return results def main_host(options): proxmox_api = ProxmoxAPI(options) @@ -146,9 +146,9 @@ def main_host(options): qemu_list = proxmox_api.node_qemu(node) qemu = qemu_list.get_by_name(options.host) if qemu: - return json.dumps(qemu.get_variables()) + return qemu.get_variables() - print json.dumps({}) + return {} def main(): parser = OptionParser(usage='%prog [options] --list | --host HOSTNAME') @@ -157,17 +157,22 @@ def main(): parser.add_option('--url', default=os.environ.get('PROXMOX_URL'), dest='url') parser.add_option('--username', default=os.environ.get('PROXMOX_USERNAME'), dest='username') parser.add_option('--password', default=os.environ.get('PROXMOX_PASSWORD'), dest='password') + parser.add_option('--pretty', action="store_true", default=False, dest='pretty') (options, args) = parser.parse_args() if options.list: - json = main_list(options) + data = main_list(options) elif options.host: - json = main_host(options) + data = main_host(options) else: parser.print_help() sys.exit(1) - print json + indent = None + if options.pretty: + indent = 2 + + print json.dumps(data, indent=indent) if __name__ == '__main__': main()