bump vmware inventory script

This commit is contained in:
Chris Meyers
2017-01-26 16:01:57 -05:00
parent 7488e09763
commit 608975ff2f

View File

@@ -42,6 +42,7 @@ HAS_PYVMOMI = False
try: try:
from pyVmomi import vim from pyVmomi import vim
from pyVim.connect import SmartConnect, Disconnect from pyVim.connect import SmartConnect, Disconnect
HAS_PYVMOMI = True HAS_PYVMOMI = True
except ImportError: except ImportError:
pass pass
@@ -54,15 +55,17 @@ except ImportError:
hasvcr = False hasvcr = False
try: try:
import vcr import vcr
hasvcr = True hasvcr = True
except ImportError: except ImportError:
pass pass
class VMwareMissingHostException(Exception): class VMwareMissingHostException(Exception):
pass pass
class VMWareInventory(object):
class VMWareInventory(object):
__name__ = 'VMWareInventory' __name__ = 'VMWareInventory'
guest_props = False guest_props = False
@@ -76,28 +79,30 @@ class VMWareInventory(object):
cache_max_age = None cache_max_age = None
cache_path_cache = None cache_path_cache = None
cache_path_index = None cache_path_index = None
cache_dir = None
server = None server = None
port = None port = None
username = None username = None
password = None password = None
validate_certs = True
host_filters = [] host_filters = []
skip_keys = []
groupby_patterns = [] groupby_patterns = []
if (sys.version_info > (3, 0)): if sys.version_info > (3, 0):
safe_types = [int, bool, str, float, None] safe_types = [int, bool, str, float, None]
else: else:
safe_types = [int, long, bool, str, float, None] safe_types = [int, long, bool, str, float, None]
iter_types = [dict, list] iter_types = [dict, list]
bad_types = ['Array', 'disabledMethod', 'declaredAlarmState'] bad_types = ['Array', 'disabledMethod', 'declaredAlarmState']
skip_keys = ['declaredalarmstate',
'disabledmethod', vimTableMaxDepth = {
'dynamicproperty', "vim.HostSystem": 2,
'dynamictype', "vim.VirtualMachine": 2,
'environmentbrowser', }
'managedby',
'parent', custom_fields = {}
'childtype']
# translation table for attributes to fetch for known vim types # translation table for attributes to fetch for known vim types
if not HAS_PYVMOMI: if not HAS_PYVMOMI:
@@ -106,14 +111,15 @@ class VMWareInventory(object):
vimTable = { vimTable = {
vim.Datastore: ['_moId', 'name'], vim.Datastore: ['_moId', 'name'],
vim.ResourcePool: ['_moId', 'name'], vim.ResourcePool: ['_moId', 'name'],
vim.HostSystem: ['_moId', 'name'],
} }
def _empty_inventory(self): @staticmethod
def _empty_inventory():
return {"_meta": {"hostvars": {}}} return {"_meta": {"hostvars": {}}}
def __init__(self, load=True): def __init__(self, load=True):
self.inventory = self._empty_inventory() self.inventory = VMWareInventory._empty_inventory()
if load: if load:
# Read settings and parse CLI arguments # Read settings and parse CLI arguments
@@ -149,7 +155,6 @@ class VMWareInventory(object):
data_to_print = self.inventory data_to_print = self.inventory
return json.dumps(data_to_print, indent=2) return json.dumps(data_to_print, indent=2)
def is_cache_valid(self): def is_cache_valid(self):
''' Determines if the cache files have expired, or if it is still valid ''' ''' Determines if the cache files have expired, or if it is still valid '''
@@ -164,25 +169,20 @@ class VMWareInventory(object):
return valid return valid
def do_api_calls_update_cache(self): def do_api_calls_update_cache(self):
''' Get instances and cache the data ''' ''' Get instances and cache the data '''
instances = self.get_instances() self.inventory = self.instances_to_inventory(self.get_instances())
self.instances = instances self.write_to_cache(self.inventory)
self.inventory = self.instances_to_inventory(instances)
self.write_to_cache(self.inventory, self.cache_path_cache)
def write_to_cache(self, data):
def write_to_cache(self, data, cache_path):
''' Dump inventory to json file ''' ''' Dump inventory to json file '''
with open(self.cache_path_cache, 'wb') as f: with open(self.cache_path_cache, 'wb') as f:
f.write(json.dumps(data)) f.write(json.dumps(data))
def get_inventory_from_cache(self): def get_inventory_from_cache(self):
''' Read in jsonified inventory ''' ''' Read in jsonified inventory '''
@@ -192,7 +192,6 @@ class VMWareInventory(object):
jdata = f.read() jdata = f.read()
return json.loads(jdata) return json.loads(jdata)
def read_settings(self): def read_settings(self):
''' Reads the settings from the vmware_inventory.ini file ''' ''' Reads the settings from the vmware_inventory.ini file '''
@@ -212,11 +211,22 @@ class VMWareInventory(object):
'cache_path': '~/.ansible/tmp', 'cache_path': '~/.ansible/tmp',
'cache_max_age': 3600, 'cache_max_age': 3600,
'max_object_level': 1, 'max_object_level': 1,
'skip_keys': 'declaredalarmstate,'
'disabledmethod,'
'dynamicproperty,'
'dynamictype,'
'environmentbrowser,'
'managedby,'
'parent,'
'childtype,'
'resourceconfig',
'alias_pattern': '{{ config.name + "_" + config.uuid }}', 'alias_pattern': '{{ config.name + "_" + config.uuid }}',
'host_pattern': '{{ guest.ipaddress }}', 'host_pattern': '{{ guest.ipaddress }}',
'host_filters': '{{ guest.gueststate == "running" }}', 'host_filters': '{{ guest.gueststate == "running" }}',
'groupby_patterns': '{{ guest.guestid }},{{ "templates" if config.template else "guests"}}', 'groupby_patterns': '{{ guest.guestid }},{{ "templates" if config.template else "guests"}}',
'lower_var_keys': True } 'lower_var_keys': True,
'custom_field_group_prefix': 'vmware_tag_',
'groupby_custom_field': False}
} }
if six.PY3: if six.PY3:
@@ -255,8 +265,7 @@ class VMWareInventory(object):
self.validate_certs = os.environ.get('VMWARE_VALIDATE_CERTS', config.get('vmware', 'validate_certs')) self.validate_certs = os.environ.get('VMWARE_VALIDATE_CERTS', config.get('vmware', 'validate_certs'))
if self.validate_certs in ['no', 'false', 'False', False]: if self.validate_certs in ['no', 'false', 'False', False]:
self.validate_certs = False self.validate_certs = False
else:
self.validate_certs = True
self.debugl('cert validation is %s' % self.validate_certs) self.debugl('cert validation is %s' % self.validate_certs)
# behavior control # behavior control
@@ -269,7 +278,8 @@ class VMWareInventory(object):
else: else:
self.lowerkeys = False self.lowerkeys = False
self.debugl('lower keys is %s' % self.lowerkeys) self.debugl('lower keys is %s' % self.lowerkeys)
self.skip_keys = list(config.get('vmware', 'skip_keys').split(','))
self.debugl('skip keys is %s' % self.skip_keys)
self.host_filters = list(config.get('vmware', 'host_filters').split(',')) self.host_filters = list(config.get('vmware', 'host_filters').split(','))
self.debugl('host filters are %s' % self.host_filters) self.debugl('host filters are %s' % self.host_filters)
self.groupby_patterns = list(config.get('vmware', 'groupby_patterns').split(',')) self.groupby_patterns = list(config.get('vmware', 'groupby_patterns').split(','))
@@ -286,7 +296,6 @@ class VMWareInventory(object):
# save the config # save the config
self.config = config self.config = config
def parse_cli_args(self): def parse_cli_args(self):
''' Command line argument processing ''' ''' Command line argument processing '''
@@ -304,13 +313,9 @@ class VMWareInventory(object):
help='maximum number of instances to retrieve') help='maximum number of instances to retrieve')
self.args = parser.parse_args() self.args = parser.parse_args()
def get_instances(self): def get_instances(self):
''' Get a list of vm instances with pyvmomi ''' ''' Get a list of vm instances with pyvmomi '''
instances = []
kwargs = {'host': self.server, kwargs = {'host': self.server,
'user': self.username, 'user': self.username,
'pwd': self.password, 'pwd': self.password,
@@ -321,9 +326,7 @@ class VMWareInventory(object):
context.verify_mode = ssl.CERT_NONE context.verify_mode = ssl.CERT_NONE
kwargs['sslContext'] = context kwargs['sslContext'] = context
instances = self._get_instances(kwargs) return self._get_instances(kwargs)
return instances
def _get_instances(self, inkwargs): def _get_instances(self, inkwargs):
@@ -350,7 +353,7 @@ class VMWareInventory(object):
for child in children: for child in children:
# If requested, limit the total number of instances # If requested, limit the total number of instances
if self.args.max_instances: if self.args.max_instances:
if len(instances) >= (self.args.max_instances): if len(instances) >= self.args.max_instances:
break break
instances.append(child) instances.append(child)
self.debugl("%s total instances in container view" % len(instances)) self.debugl("%s total instances in container view" % len(instances))
@@ -360,27 +363,30 @@ class VMWareInventory(object):
instance_tuples = [] instance_tuples = []
for instance in sorted(instances): for instance in sorted(instances):
if self.guest_props != False: if self.guest_props:
ifacts = self.facts_from_proplist(instance) ifacts = self.facts_from_proplist(instance)
else: else:
ifacts = self.facts_from_vobj(instance) ifacts = self.facts_from_vobj(instance)
instance_tuples.append((instance, ifacts)) instance_tuples.append((instance, ifacts))
self.debugl('facts collected for all instances') self.debugl('facts collected for all instances')
return instance_tuples
cfm = content.customFieldsManager
if cfm is not None and cfm.field:
for f in cfm.field:
if f.managedObjectType == vim.VirtualMachine:
self.custom_fields[f.key] = f.name;
self.debugl('%d custom fieds collected' % len(self.custom_fields))
return instance_tuples
def instances_to_inventory(self, instances): def instances_to_inventory(self, instances):
''' Convert a list of vm objects into a json compliant inventory ''' ''' Convert a list of vm objects into a json compliant inventory '''
self.debugl('re-indexing instances based on ini settings') self.debugl('re-indexing instances based on ini settings')
inventory = self._empty_inventory() inventory = VMWareInventory._empty_inventory()
inventory['all'] = {} inventory['all'] = {}
inventory['all']['hosts'] = [] inventory['all']['hosts'] = []
last_idata = None
total = len(instances)
for idx, instance in enumerate(instances): for idx, instance in enumerate(instances):
# make a unique id for this object to avoid vmware's # make a unique id for this object to avoid vmware's
# numerous uuid's which aren't all unique. # numerous uuid's which aren't all unique.
thisid = str(uuid.uuid4()) thisid = str(uuid.uuid4())
@@ -392,13 +398,16 @@ class VMWareInventory(object):
inventory['_meta']['hostvars'][thisid]['ansible_uuid'] = thisid inventory['_meta']['hostvars'][thisid]['ansible_uuid'] = thisid
# Make a map of the uuid to the alias the user wants # Make a map of the uuid to the alias the user wants
name_mapping = self.create_template_mapping(inventory, name_mapping = self.create_template_mapping(
self.config.get('vmware', 'alias_pattern')) inventory,
self.config.get('vmware', 'alias_pattern')
)
# Make a map of the uuid to the ssh hostname the user wants # Make a map of the uuid to the ssh hostname the user wants
host_mapping = self.create_template_mapping(inventory, host_mapping = self.create_template_mapping(
self.config.get('vmware', 'host_pattern')) inventory,
self.config.get('vmware', 'host_pattern')
)
# Reset the inventory keys # Reset the inventory keys
for k, v in name_mapping.items(): for k, v in name_mapping.items():
@@ -411,7 +420,7 @@ class VMWareInventory(object):
inventory['_meta']['hostvars'][k]['ansible_host'] = host_mapping[k] inventory['_meta']['hostvars'][k]['ansible_host'] = host_mapping[k]
# 1.9.x backwards compliance # 1.9.x backwards compliance
inventory['_meta']['hostvars'][k]['ansible_ssh_host'] = host_mapping[k] inventory['_meta']['hostvars'][k]['ansible_ssh_host'] = host_mapping[k]
except Exception as e: except Exception:
continue continue
if k == v: if k == v:
@@ -454,8 +463,34 @@ class VMWareInventory(object):
if k not in inventory[v]['hosts']: if k not in inventory[v]['hosts']:
inventory[v]['hosts'].append(k) inventory[v]['hosts'].append(k)
return inventory if self.config.get('vmware', 'groupby_custom_field'):
for k, v in inventory['_meta']['hostvars'].items():
if 'customvalue' in v:
for tv in v['customvalue']:
if not isinstance(tv['value'], str) and not isinstance(tv['value'], unicode):
continue
newkey = None
field_name = self.custom_fields[tv['key']] if tv['key'] in self.custom_fields else tv['key']
values = []
keylist = map(lambda x: x.strip(), tv['value'].split(','))
for kl in keylist:
try:
newkey = self.config.get('vmware', 'custom_field_group_prefix') + field_name + '_' + kl
newkey = newkey.strip()
except Exception as e:
self.debugl(e)
values.append(newkey)
for tag in values:
if not tag:
continue
if tag not in inventory:
inventory[tag] = {}
inventory[tag]['hosts'] = []
if k not in inventory[tag]['hosts']:
inventory[tag]['hosts'].append(k)
return inventory
def create_template_mapping(self, inventory, pattern, dtype='string'): def create_template_mapping(self, inventory, pattern, dtype='string'):
@@ -494,7 +529,7 @@ class VMWareInventory(object):
if self.lowerkeys: if self.lowerkeys:
key = key.lower() key = key.lower()
if not '.' in prop: if '.' not in prop:
# props without periods are direct attributes of the parent # props without periods are direct attributes of the parent
rdata[key] = getattr(vm, prop) rdata[key] = getattr(vm, prop)
else: else:
@@ -533,7 +568,6 @@ class VMWareInventory(object):
return rdata return rdata
def facts_from_vobj(self, vobj, level=0): def facts_from_vobj(self, vobj, level=0):
''' Traverse a VM object and return a json compliant data structure ''' ''' Traverse a VM object and return a json compliant data structure '''
@@ -556,7 +590,7 @@ class VMWareInventory(object):
methods = dir(vobj) methods = dir(vobj)
methods = [str(x) for x in methods if not x.startswith('_')] methods = [str(x) for x in methods if not x.startswith('_')]
methods = [x for x in methods if not x in self.bad_types] methods = [x for x in methods if x not in self.bad_types]
methods = [x for x in methods if not x.lower() in self.skip_keys] methods = [x for x in methods if not x.lower() in self.skip_keys]
methods = sorted(methods) methods = sorted(methods)
@@ -577,19 +611,20 @@ class VMWareInventory(object):
rdata[method] = self._process_object_types( rdata[method] = self._process_object_types(
methodToCall, methodToCall,
thisvm=vobj, thisvm=vobj,
inkey=method inkey=method,
) )
return rdata return rdata
def _process_object_types(self, vobj, thisvm=None, inkey=None, level=0): def _process_object_types(self, vobj, thisvm=None, inkey=None, level=0):
''' Serialize an object ''' ''' Serialize an object '''
rdata = {} rdata = {}
if type(vobj).__name__ in self.vimTableMaxDepth and level >= self.vimTableMaxDepth[type(vobj).__name__]:
return rdata
if vobj is None: if vobj is None:
rdata = None rdata = None
elif type(vobj) in self.vimTable: elif type(vobj) in self.vimTable:
rdata = {} rdata = {}
for key in self.vimTable[type(vobj)]: for key in self.vimTable[type(vobj)]:
@@ -612,13 +647,11 @@ class VMWareInventory(object):
rdata = [] rdata = []
try: try:
vobj = sorted(vobj) vobj = sorted(vobj)
except Exception as e: except Exception:
pass pass
for idv, vii in enumerate(vobj): for idv, vii in enumerate(vobj):
if level + 1 <= self.maxlevel:
if (level+1 <= self.maxlevel):
vid = self._process_object_types( vid = self._process_object_types(
vii, vii,
thisvm=thisvm, thisvm=thisvm,
@@ -635,8 +668,8 @@ class VMWareInventory(object):
elif issubclass(type(vobj), object): elif issubclass(type(vobj), object):
methods = dir(vobj) methods = dir(vobj)
methods = [str(x) for x in methods if not x.startswith('_')] methods = [str(x) for x in methods if not x.startswith('_')]
methods = [x for x in methods if not x in self.bad_types] methods = [x for x in methods if x not in self.bad_types]
methods = [x for x in methods if not x.lower() in self.skip_keys] methods = [x for x in methods if not inkey + '.' + x.lower() in self.skip_keys]
methods = sorted(methods) methods = sorted(methods)
for method in methods: for method in methods:
@@ -645,11 +678,13 @@ class VMWareInventory(object):
methodToCall = getattr(vobj, method) methodToCall = getattr(vobj, method)
except Exception as e: except Exception as e:
continue continue
if callable(methodToCall): if callable(methodToCall):
continue continue
if self.lowerkeys: if self.lowerkeys:
method = method.lower() method = method.lower()
if (level+1 <= self.maxlevel): if level + 1 <= self.maxlevel:
rdata[method] = self._process_object_types( rdata[method] = self._process_object_types(
methodToCall, methodToCall,
thisvm=thisvm, thisvm=thisvm,
@@ -668,10 +703,8 @@ class VMWareInventory(object):
if host in self.inventory['_meta']['hostvars']: if host in self.inventory['_meta']['hostvars']:
return self.inventory['_meta']['hostvars'][host] return self.inventory['_meta']['hostvars'][host]
elif self.args.host and self.inventory['_meta']['hostvars']: elif self.args.host and self.inventory['_meta']['hostvars']:
# check if the machine has the name requested
keys = self.inventory['_meta']['hostvars'].keys()
match = None match = None
for k,v in self.inventory['_meta']['hostvars'].items(): for k, v in self.inventory['_meta']['hostvars']:
if self.inventory['_meta']['hostvars'][k]['name'] == self.args.host: if self.inventory['_meta']['hostvars'][k]['name'] == self.args.host:
match = k match = k
break break