1#!/usr/bin/env python 2""" 3SoftLayer external inventory script. 4 5The SoftLayer Python API client is required. Use `pip install softlayer` to install it. 6You have a few different options for configuring your username and api_key. You can pass 7environment variables (SL_USERNAME and SL_API_KEY). You can also write INI file to 8~/.softlayer or /etc/softlayer.conf. For more information see the SL API at: 9- https://softlayer-python.readthedocs.io/en/latest/config_file.html 10 11The SoftLayer Python client has a built in command for saving this configuration file 12via the command `sl config setup`. 13""" 14 15# Copyright (C) 2014 AJ Bourg <aj@ajbourg.com> 16# 17# This program is free software: you can redistribute it and/or modify 18# it under the terms of the GNU General Public License as published by 19# the Free Software Foundation, either version 3 of the License, or 20# (at your option) any later version. 21# 22# This program is distributed in the hope that it will be useful, 23# but WITHOUT ANY WARRANTY; without even the implied warranty of 24# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25# GNU General Public License for more details. 26# 27# You should have received a copy of the GNU General Public License 28# along with this program. If not, see <http://www.gnu.org/licenses/>. 29 30# 31# I found the structure of the ec2.py script very helpful as an example 32# as I put this together. Thanks to whoever wrote that script! 33# 34 35import SoftLayer 36import re 37import argparse 38import itertools 39import json 40 41 42class SoftLayerInventory(object): 43 common_items = [ 44 'id', 45 'globalIdentifier', 46 'hostname', 47 'domain', 48 'fullyQualifiedDomainName', 49 'primaryBackendIpAddress', 50 'primaryIpAddress', 51 'datacenter', 52 'tagReferences', 53 'userData.value', 54 ] 55 56 vs_items = [ 57 'lastKnownPowerState.name', 58 'powerState', 59 'maxCpu', 60 'maxMemory', 61 'activeTransaction.transactionStatus[friendlyName,name]', 62 'status', 63 ] 64 65 hw_items = [ 66 'hardwareStatusId', 67 'processorPhysicalCoreAmount', 68 'memoryCapacity', 69 ] 70 71 def _empty_inventory(self): 72 return {"_meta": {"hostvars": {}}} 73 74 def __init__(self): 75 '''Main path''' 76 77 self.inventory = self._empty_inventory() 78 79 self.parse_options() 80 81 if self.args.list: 82 self.get_all_servers() 83 print(self.json_format_dict(self.inventory, True)) 84 elif self.args.host: 85 self.get_all_servers() 86 print(self.json_format_dict(self.inventory["_meta"]["hostvars"][self.args.host], True)) 87 88 def to_safe(self, word): 89 '''Converts 'bad' characters in a string to underscores so they can be used as Ansible groups''' 90 91 return re.sub(r"[^A-Za-z0-9\-\.]", "_", word) 92 93 def push(self, my_dict, key, element): 94 '''Push an element onto an array that may not have been defined in the dict''' 95 96 if key in my_dict: 97 my_dict[key].append(element) 98 else: 99 my_dict[key] = [element] 100 101 def parse_options(self): 102 '''Parse all the arguments from the CLI''' 103 104 parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on SoftLayer') 105 parser.add_argument('--list', action='store_true', default=False, 106 help='List instances (default: False)') 107 parser.add_argument('--host', action='store', 108 help='Get all the variables about a specific instance') 109 self.args = parser.parse_args() 110 111 def json_format_dict(self, data, pretty=False): 112 '''Converts a dict to a JSON object and dumps it as a formatted string''' 113 114 if pretty: 115 return json.dumps(data, sort_keys=True, indent=2) 116 else: 117 return json.dumps(data) 118 119 def process_instance(self, instance, instance_type="virtual"): 120 '''Populate the inventory dictionary with any instance information''' 121 122 # only want active instances 123 if 'status' in instance and instance['status']['name'] != 'Active': 124 return 125 126 # and powered on instances 127 if 'powerState' in instance and instance['powerState']['name'] != 'Running': 128 return 129 130 # 5 is active for hardware... see https://forums.softlayer.com/forum/softlayer-developer-network/general-discussion/2955-hardwarestatusid 131 if 'hardwareStatusId' in instance and instance['hardwareStatusId'] != 5: 132 return 133 134 # if there's no IP address, we can't reach it 135 if 'primaryIpAddress' not in instance: 136 return 137 138 instance['userData'] = instance['userData'][0]['value'] if instance['userData'] else '' 139 140 dest = instance['primaryIpAddress'] 141 142 instance['tags'] = list() 143 for tag in instance['tagReferences']: 144 instance['tags'].append(tag['tag']['name']) 145 146 del instance['tagReferences'] 147 148 self.inventory["_meta"]["hostvars"][dest] = instance 149 150 # Inventory: group by memory 151 if 'maxMemory' in instance: 152 self.push(self.inventory, self.to_safe('memory_' + str(instance['maxMemory'])), dest) 153 elif 'memoryCapacity' in instance: 154 self.push(self.inventory, self.to_safe('memory_' + str(instance['memoryCapacity'])), dest) 155 156 # Inventory: group by cpu count 157 if 'maxCpu' in instance: 158 self.push(self.inventory, self.to_safe('cpu_' + str(instance['maxCpu'])), dest) 159 elif 'processorPhysicalCoreAmount' in instance: 160 self.push(self.inventory, self.to_safe('cpu_' + str(instance['processorPhysicalCoreAmount'])), dest) 161 162 # Inventory: group by datacenter 163 self.push(self.inventory, self.to_safe('datacenter_' + instance['datacenter']['name']), dest) 164 165 # Inventory: group by hostname 166 self.push(self.inventory, self.to_safe(instance['hostname']), dest) 167 168 # Inventory: group by FQDN 169 self.push(self.inventory, self.to_safe(instance['fullyQualifiedDomainName']), dest) 170 171 # Inventory: group by domain 172 self.push(self.inventory, self.to_safe(instance['domain']), dest) 173 174 # Inventory: group by type (hardware/virtual) 175 self.push(self.inventory, instance_type, dest) 176 177 for tag in instance['tags']: 178 self.push(self.inventory, tag, dest) 179 180 def get_virtual_servers(self): 181 '''Get all the CCI instances''' 182 vs = SoftLayer.VSManager(self.client) 183 mask = "mask[%s]" % ','.join(itertools.chain(self.common_items, self.vs_items)) 184 instances = vs.list_instances(mask=mask) 185 186 for instance in instances: 187 self.process_instance(instance) 188 189 def get_physical_servers(self): 190 '''Get all the hardware instances''' 191 hw = SoftLayer.HardwareManager(self.client) 192 mask = "mask[%s]" % ','.join(itertools.chain(self.common_items, self.hw_items)) 193 instances = hw.list_hardware(mask=mask) 194 195 for instance in instances: 196 self.process_instance(instance, 'hardware') 197 198 def get_all_servers(self): 199 self.client = SoftLayer.Client() 200 self.get_virtual_servers() 201 self.get_physical_servers() 202 203 204SoftLayerInventory() 205