Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cloudstack: add dynamic inventory #9835

Merged
merged 4 commits into from
May 4, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions plugins/inventory/cloudstack.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[cloudstack]
#endpoint = https://api.exoscale.ch/compute
endpoint = https://cloud.example.com/client/api
key = cloudstack api key
secret = cloudstack api secret
232 changes: 232 additions & 0 deletions plugins/inventory/cloudstack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# 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/>.

######################################################################

"""
Ansible CloudStack external inventory script.
=============================================

Generates Ansible inventory from CloudStack. Configuration is read from
'cloudstack.ini'. If you need to pass the project, write a simple wrapper
script, e.g. project_cloudstack.sh:

#!/bin/bash
cloudstack.py --project <your_project> $@


When run against a specific host, this script returns the following attributes
based on the data obtained from CloudStack API:

"web01": {
"cpu_number": 2,
"nic": [
{
"ip": "10.102.76.98",
"mac": "02:00:50:99:00:01",
"type": "Isolated",
"netmask": "255.255.255.0",
"gateway": "10.102.76.1"
},
{
"ip": "10.102.138.63",
"mac": "06:b7:5a:00:14:84",
"type": "Shared",
"netmask": "255.255.255.0",
"gateway": "10.102.138.1"
}
],
"default_ip": "10.102.76.98",
"zone": "ZUERICH",
"created": "2014-07-02T07:53:50+0200",
"hypervisor": "VMware",
"memory": 2048,
"state": "Running",
"tags": [],
"cpu_speed": 1800,
"affinity_group": [],
"service_offering": "Small",
"cpu_used": "62%"
}


usage: cloudstack.py [--list] [--host HOST] [--project PROJECT]
"""

import os
import sys
import argparse

try:
import json
except:
import simplejson as json


try:
from cs import CloudStack, CloudStackException, read_config
except ImportError:
print >> sys.stderr, "Error: CloudStack library must be installed: pip install cs."
sys.exit(1)


class CloudStackInventory(object):
def __init__(self):

parser = argparse.ArgumentParser()
parser.add_argument('--host')
parser.add_argument('--list', action='store_true')
parser.add_argument('--project')

options = parser.parse_args()
try:
self.cs = CloudStack(**read_config())
except CloudStackException, e:
print >> sys.stderr, "Error: Could not connect to CloudStack API"

project_id = ''
if options.project:
project_id = self.get_project_id(options.project)

if options.host:
data = self.get_host(options.host)
print json.dumps(data, indent=2)

elif options.list:
data = self.get_list()
print json.dumps(data, indent=2)
else:
print >> sys.stderr, "usage: --list | --host <hostname> [--project <project>]"
sys.exit(1)


def get_project_id(self, project):
projects = self.cs.listProjects()
if projects:
for p in projects['project']:
if p['name'] == project or p['id'] == project:
return p['id']
print >> sys.stderr, "Error: Project %s not found." % project
sys.exit(1)


def get_host(self, name, project_id=''):
hosts = self.cs.listVirtualMachines(projectid=project_id)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work when list of projects is empty (example: exoscale.ch). It results with following error:

Traceback (most recent call last): File "./cloudstack.py", line 232, in <module> CloudStackInventory() File "./cloudstack.py", line 113, in __init__ data = self.get_list() File "./cloudstack.py", line 178, in get_list groups = self.cs.listInstanceGroups(projectid=project_id) File "/Library/Python/2.7/site-packages/cs/client.py", line 105, in handler return self._request(command, **kwargs) File "/Library/Python/2.7/site-packages/cs/client.py", line 157, in _request response.status_code), response, data) cs.client.CloudStackException: ('HTTP 431 response from CloudStack', <Response [431]>, {u'errorcode': 431, u'uuidList': [], u'cserrorcode': 9999, u'errortext': u'Unable to execute API command listinstancegroups due to invalid value. Invalid parameter projectid value= due to incorrect long value format, or entity does not exist or due to incorrect parameter annotation for the field in api cmd class.'})

It works, when I remove projectid=project_id form the API calls.

data = {}
if not hosts:
return data
for host in hosts['virtualmachine']:
host_name = host['displayname']
if name == host_name:
data['zone'] = host['zonename']
if 'group' in host:
data['group'] = host['group']
data['state'] = host['state']
data['service_offering'] = host['serviceofferingname']
data['affinity_group'] = host['affinitygroup']
data['security_group'] = host['securitygroup']
data['cpu_number'] = host['cpunumber']
data['cpu_speed'] = host['cpuspeed']
if 'cpuused' in host:
data['cpu_used'] = host['cpuused']
data['memory'] = host['memory']
data['tags'] = host['tags']
data['hypervisor'] = host['hypervisor']
data['created'] = host['created']
data['nic'] = []
for nic in host['nic']:
data['nic'].append({
'ip': nic['ipaddress'],
'mac': nic['macaddress'],
'netmask': nic['netmask'],
'gateway': nic['gateway'],
'type': nic['type'],
})
if nic['isdefault']:
data['default_ip'] = nic['ipaddress']
break;
return data


def get_list(self, project_id=''):
data = {
'all': {
'hosts': [],
},
'_meta': {
'hostvars': {},
},
}

groups = self.cs.listInstanceGroups(projectid=project_id)
if groups:
for group in groups['instancegroup']:
group_name = group['name']
if group_name and not group_name in data:
data[group_name] = {
'hosts': []
}

hosts = self.cs.listVirtualMachines(projectid=project_id)
if not hosts:
return data
for host in hosts['virtualmachine']:
host_name = host['displayname']
data['all']['hosts'].append(host_name)
data['_meta']['hostvars'][host_name] = {}
data['_meta']['hostvars'][host_name]['zone'] = host['zonename']
if 'group' in host:
data['_meta']['hostvars'][host_name]['group'] = host['group']
data['_meta']['hostvars'][host_name]['state'] = host['state']
data['_meta']['hostvars'][host_name]['service_offering'] = host['serviceofferingname']
data['_meta']['hostvars'][host_name]['affinity_group'] = host['affinitygroup']
data['_meta']['hostvars'][host_name]['security_group'] = host['securitygroup']
data['_meta']['hostvars'][host_name]['cpu_number'] = host['cpunumber']
data['_meta']['hostvars'][host_name]['cpu_speed'] = host['cpuspeed']
if 'cpuused' in host:
data['_meta']['hostvars'][host_name]['cpu_used'] = host['cpuused']
data['_meta']['hostvars'][host_name]['created'] = host['created']
data['_meta']['hostvars'][host_name]['memory'] = host['memory']
data['_meta']['hostvars'][host_name]['tags'] = host['tags']
data['_meta']['hostvars'][host_name]['hypervisor'] = host['hypervisor']
data['_meta']['hostvars'][host_name]['created'] = host['created']
data['_meta']['hostvars'][host_name]['nic'] = []
for nic in host['nic']:
data['_meta']['hostvars'][host_name]['nic'].append({
'ip': nic['ipaddress'],
'mac': nic['macaddress'],
'netmask': nic['netmask'],
'gateway': nic['gateway'],
'type': nic['type'],
})
if nic['isdefault']:
data['_meta']['hostvars'][host_name]['default_ip'] = nic['ipaddress']

group_name = ''
if 'group' in host:
group_name = host['group']

if group_name and group_name in data:
data[group_name]['hosts'].append(host_name)
return data


if __name__ == '__main__':
CloudStackInventory()