1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# 4# (c) 2015, René Moser <mail@renemoser.net> 5# 6# This file is part of Ansible, 7# 8# Ansible is free software: you can redistribute it and/or modify 9# it under the terms of the GNU General Public License as published by 10# the Free Software Foundation, either version 3 of the License, or 11# (at your option) any later version. 12# 13# Ansible is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16# GNU General Public License for more details. 17# 18# You should have received a copy of the GNU General Public License 19# along with Ansible. If not, see <http://www.gnu.org/licenses/>. 20 21###################################################################### 22 23""" 24Ansible CloudStack external inventory script. 25============================================= 26 27Generates Ansible inventory from CloudStack. Configuration is read from 28'cloudstack.ini'. If you need to pass the project, write a simple wrapper 29script, e.g. project_cloudstack.sh: 30 31 #!/bin/bash 32 cloudstack.py --project <your_project> $@ 33 34 35When run against a specific host, this script returns the following attributes 36based on the data obtained from CloudStack API: 37 38 "web01": { 39 "cpu_number": 2, 40 "nic": [ 41 { 42 "ip": "10.102.76.98", 43 "mac": "02:00:50:99:00:01", 44 "type": "Isolated", 45 "netmask": "255.255.255.0", 46 "gateway": "10.102.76.1" 47 }, 48 { 49 "ip": "10.102.138.63", 50 "mac": "06:b7:5a:00:14:84", 51 "type": "Shared", 52 "netmask": "255.255.255.0", 53 "gateway": "10.102.138.1" 54 } 55 ], 56 "default_ip": "10.102.76.98", 57 "zone": "ZUERICH", 58 "created": "2014-07-02T07:53:50+0200", 59 "hypervisor": "VMware", 60 "memory": 2048, 61 "state": "Running", 62 "tags": [], 63 "cpu_speed": 1800, 64 "affinity_group": [], 65 "service_offering": "Small", 66 "cpu_used": "62%" 67 } 68 69 70usage: cloudstack.py [--list] [--host HOST] [--project PROJECT] [--domain DOMAIN] 71""" 72 73from __future__ import print_function 74 75import sys 76import argparse 77import json 78 79try: 80 from cs import CloudStack, CloudStackException, read_config 81except ImportError: 82 print("Error: CloudStack library must be installed: pip install cs.", 83 file=sys.stderr) 84 sys.exit(1) 85 86 87class CloudStackInventory(object): 88 def __init__(self): 89 90 parser = argparse.ArgumentParser() 91 parser.add_argument('--host') 92 parser.add_argument('--list', action='store_true') 93 parser.add_argument('--tag', help="Filter machines by a tag. Should be in the form key=value.") 94 parser.add_argument('--project') 95 parser.add_argument('--domain') 96 97 options = parser.parse_args() 98 try: 99 self.cs = CloudStack(**read_config()) 100 except CloudStackException: 101 print("Error: Could not connect to CloudStack API", file=sys.stderr) 102 103 domain_id = None 104 if options.domain: 105 domain_id = self.get_domain_id(options.domain) 106 107 project_id = None 108 if options.project: 109 project_id = self.get_project_id(options.project, domain_id) 110 111 if options.host: 112 data = self.get_host(options.host, project_id, domain_id) 113 print(json.dumps(data, indent=2)) 114 115 elif options.list: 116 tags = dict() 117 if options.tag: 118 tags['tags[0].key'], tags['tags[0].value'] = options.tag.split('=') 119 data = self.get_list(project_id, domain_id, **tags) 120 print(json.dumps(data, indent=2)) 121 else: 122 print("usage: --list [--tag <tag>] | --host <hostname> [--project <project>] [--domain <domain_path>]", 123 file=sys.stderr) 124 sys.exit(1) 125 126 def get_domain_id(self, domain): 127 domains = self.cs.listDomains(listall=True) 128 if domains: 129 for d in domains['domain']: 130 if d['path'].lower() == domain.lower(): 131 return d['id'] 132 print("Error: Domain %s not found." % domain, file=sys.stderr) 133 sys.exit(1) 134 135 def get_project_id(self, project, domain_id=None): 136 projects = self.cs.listProjects(domainid=domain_id) 137 if projects: 138 for p in projects['project']: 139 if p['name'] == project or p['id'] == project: 140 return p['id'] 141 print("Error: Project %s not found." % project, file=sys.stderr) 142 sys.exit(1) 143 144 def get_host(self, name, project_id=None, domain_id=None, **kwargs): 145 hosts = self.cs.listVirtualMachines(projectid=project_id, domainid=domain_id, fetch_list=True, **kwargs) 146 data = {} 147 if not hosts: 148 return data 149 for host in hosts: 150 host_name = host['displayname'] 151 if name == host_name: 152 data['zone'] = host['zonename'] 153 if 'group' in host: 154 data['group'] = host['group'] 155 data['state'] = host['state'] 156 data['service_offering'] = host['serviceofferingname'] 157 data['affinity_group'] = host['affinitygroup'] 158 data['security_group'] = host['securitygroup'] 159 data['cpu_number'] = host['cpunumber'] 160 if 'cpu_speed' in host: 161 data['cpu_speed'] = host['cpuspeed'] 162 if 'cpuused' in host: 163 data['cpu_used'] = host['cpuused'] 164 data['memory'] = host['memory'] 165 data['tags'] = host['tags'] 166 if 'hypervisor' in host: 167 data['hypervisor'] = host['hypervisor'] 168 data['created'] = host['created'] 169 data['nic'] = [] 170 for nic in host['nic']: 171 nicdata = { 172 'ip': nic['ipaddress'], 173 'mac': nic['macaddress'], 174 'netmask': nic['netmask'], 175 'gateway': nic['gateway'], 176 'type': nic['type'], 177 } 178 if 'ip6address' in nic: 179 nicdata['ip6'] = nic['ip6address'] 180 if 'gateway' in nic: 181 nicdata['gateway'] = nic['gateway'] 182 if 'netmask' in nic: 183 nicdata['netmask'] = nic['netmask'] 184 data['nic'].append(nicdata) 185 if nic['isdefault']: 186 data['default_ip'] = nic['ipaddress'] 187 if 'ip6address' in nic: 188 data['default_ip6'] = nic['ip6address'] 189 break 190 return data 191 192 def get_list(self, project_id=None, domain_id=None, **kwargs): 193 data = { 194 'all': { 195 'hosts': [], 196 }, 197 '_meta': { 198 'hostvars': {}, 199 }, 200 } 201 202 groups = self.cs.listInstanceGroups(projectid=project_id, domainid=domain_id) 203 if groups: 204 for group in groups['instancegroup']: 205 group_name = group['name'] 206 if group_name and group_name not in data: 207 data[group_name] = { 208 'hosts': [] 209 } 210 211 hosts = self.cs.listVirtualMachines(projectid=project_id, domainid=domain_id, fetch_list=True, **kwargs) 212 if not hosts: 213 return data 214 for host in hosts: 215 host_name = host['displayname'] 216 data['all']['hosts'].append(host_name) 217 data['_meta']['hostvars'][host_name] = {} 218 219 # Make a group per zone 220 data['_meta']['hostvars'][host_name]['zone'] = host['zonename'] 221 group_name = host['zonename'] 222 if group_name not in data: 223 data[group_name] = { 224 'hosts': [] 225 } 226 data[group_name]['hosts'].append(host_name) 227 228 if 'group' in host: 229 data['_meta']['hostvars'][host_name]['group'] = host['group'] 230 data['_meta']['hostvars'][host_name]['state'] = host['state'] 231 data['_meta']['hostvars'][host_name]['service_offering'] = host['serviceofferingname'] 232 data['_meta']['hostvars'][host_name]['affinity_group'] = host['affinitygroup'] 233 data['_meta']['hostvars'][host_name]['security_group'] = host['securitygroup'] 234 data['_meta']['hostvars'][host_name]['cpu_number'] = host['cpunumber'] 235 if 'cpuspeed' in host: 236 data['_meta']['hostvars'][host_name]['cpu_speed'] = host['cpuspeed'] 237 if 'cpuused' in host: 238 data['_meta']['hostvars'][host_name]['cpu_used'] = host['cpuused'] 239 data['_meta']['hostvars'][host_name]['created'] = host['created'] 240 data['_meta']['hostvars'][host_name]['memory'] = host['memory'] 241 data['_meta']['hostvars'][host_name]['tags'] = host['tags'] 242 if 'hypervisor' in host: 243 data['_meta']['hostvars'][host_name]['hypervisor'] = host['hypervisor'] 244 data['_meta']['hostvars'][host_name]['created'] = host['created'] 245 data['_meta']['hostvars'][host_name]['nic'] = [] 246 for nic in host['nic']: 247 nicdata = { 248 'ip': nic['ipaddress'], 249 'mac': nic['macaddress'], 250 'netmask': nic['netmask'], 251 'gateway': nic['gateway'], 252 'type': nic['type'], 253 } 254 if 'ip6address' in nic: 255 nicdata['ip6'] = nic['ip6address'] 256 if 'gateway' in nic: 257 nicdata['gateway'] = nic['gateway'] 258 if 'netmask' in nic: 259 nicdata['netmask'] = nic['netmask'] 260 data['_meta']['hostvars'][host_name]['nic'].append(nicdata) 261 if nic['isdefault']: 262 data['_meta']['hostvars'][host_name]['default_ip'] = nic['ipaddress'] 263 if 'ip6address' in nic: 264 data['_meta']['hostvars'][host_name]['default_ip6'] = nic['ip6address'] 265 266 group_name = '' 267 if 'group' in host: 268 group_name = host['group'] 269 270 if group_name and group_name in data: 271 data[group_name]['hosts'].append(host_name) 272 return data 273 274 275if __name__ == '__main__': 276 CloudStackInventory() 277