# Copyright (C) 2005-2023 Splunk Inc. All Rights Reserved. import inspect import sys import vim25.suds_resolver import suds import json import datetime import re from hydra import six isviminstance = lambda obj, name: isinstance(obj, suds.sudsobject.Object) and obj.__class__.__name__==name python_version = '2' if sys.version_info[0] == 2 else '3' # This fixes a bug from suds that it returns the result only based on xml elements # we need to convert it into vim25 object def ConvertObjectToDict(tempObject): ''' Returns a dictionary with all the keys as attributes of the object. ''' tempDict = {} for name in dir(tempObject): if not name.startswith('__'): value = getattr(tempObject, name) value = CheckAttribute(value) tempDict[name] = value return tempDict def FlattenTaskEventData(taskSet): ''' Takes a taskSet or eventSet object and formats the output for splunk. Returns a list of json objects. ''' tempCombinedData = [] for objectItem in taskSet: tempFullObject=dict(CheckAttribute(objectItem)) tempCombinedData.append(json.dumps(tempFullObject)) return tempCombinedData def FlattenSingleTaskEvent(task): tempFullTask=dict(CheckAttribute(task)) flatTask=json.dumps(tempFullTask) return flatTask def ConvertDotStringToDict(dotstring, value): ''' Will take an input that has periods in the name, explode the periods into a list, then create an n-depth dictionary with the last item being set to value ''' lst = dotstring.split(".") expanded_list={} current_level=expanded_list for item in lst: if item not in current_level: current_level[item] = {} previous_level=current_level current_level = current_level[item] previous_level[lst[-1]] = value return expanded_list def CombineDicts(oldDict, newDict): targetDict = defaultdict(oldDict) for key, value in six.iteritems(newDict): targetDict[key].update(value) return newDict def Folderize(input): output = None if isinstance(input, list): is_complex = False output = [] merged_dicts = {} for item in input: if isinstance(item, dict): for k, v in six.iteritems(item): if hasattr(v, '__iter__') and not isinstance(v, (str, bytes)): is_complex = True curr = merged_dicts.setdefault(k, []) curr.append(v) else: if item == None: pass else: output.append(item) if merged_dicts and is_complex: # recurse for the dict contents output.append(Folderize(merged_dicts)) else: # No complex dict objects were found. output = input if len(output) == 1: # flatten one level output = output[0] elif isinstance(input, dict): # recurse for the dict contents output = {k: Folderize(input[k]) for k in input} else: output = input return output def ProcessInventoryHighVersionFix(splitPeriodDict): ''' Based on newer revesions of inventory data, occasionally we'll get data bake in a host['host-10'] format for a key instead of a dict of the moid and type. This function will split on those names and return back the data identical to changeVersion=1 ''' if len(splitPeriodDict) == 1: newDict={} mor=None key=list(splitPeriodDict.keys())[0] value=list(splitPeriodDict.values())[0] matchedRegEx=re.search('\w+\["(\w+-\d+)"\]', key) if matchedRegEx: mor=matchedRegEx.group(1) if mor: #first adjust any host mor blocks into the proper locations: if key.startswith('host['): newDict['host'] = {} #Fix Datastore host.mountInfo if 'mountInfo' in value: #newDict['host'] newDict['host']['DatastoreHostMount'] = { 'key':{ 'moid':mor , 'type':'HostSystem'}, 'mountInfo':value['mountInfo']} elif key.startswith('vm['): newDict['vm'] = { 'ManagedObjectReference' : { 'moid':mor, 'type':'VirtualMachine' }} else: newDict=splitPeriodDict return newDict else: return splitPeriodDict def CheckIfObjectForPython3(tempObject): try: if tempObject.__module__ and not isinstance(tempObject, (int, str, bool, dict, list, float)): return True except: return False return False def CheckIfObjectForPython2(tempObject): if type(tempObject).__name__ == 'instance': return True else: return False function_switch = {'2': CheckIfObjectForPython2, '3': CheckIfObjectForPython3} def CheckIfPropertyObject(tempObject): if hasattr(tempObject, 'name') and hasattr(tempObject, 'val'): return True else: return False def CheckIfAnyKey(tempObject): if (tempObject.__class__.__name__) == 'KeyAnyValue': return True else: return False def CheckIfMOR(tempObject): if hasattr(tempObject, 'value') and hasattr(tempObject, '_type'): return True else: return False def CheckIfOp(tempObject): if hasattr(tempObject, 'name') and hasattr(tempObject, 'op') and not hasattr(tempObject, 'val'): return True else: return False def CheckAttribute(tempAttribute): ''' Checks the value of the attribute for a type, then returns the proper output of the attribute ''' if CheckIfMOR(tempAttribute): return {"moid":str(tempAttribute.value), "type":str(tempAttribute._type)} elif CheckIfAnyKey(tempAttribute): value = CheckAttribute(tempAttribute.value) return {tempAttribute.key : value} elif CheckIfPropertyObject(tempAttribute): value = CheckAttribute(tempAttribute.val) itemTarget = {} if not "." in tempAttribute.name: itemTarget[tempAttribute.name] = value else: itemTarget = ConvertDotStringToDict(tempAttribute.name, value) itemTarget = ProcessInventoryHighVersionFix(itemTarget) return itemTarget elif CheckIfOp(tempAttribute): return None elif function_switch[python_version](tempAttribute): return ConvertObjectToDict(tempAttribute) elif type(tempAttribute) == list: tempList = [] for attribute in tempAttribute: tempList.append(CheckAttribute(attribute)) return tempList elif type(tempAttribute) == dict: return tempAttribute else: return str(tempAttribute) def ConvertIsoUtcDate(d, format_string="%Y-%m-%dT%H:%M:%SZ"): '''Grabs date represented as e.g.: 2013-04-01T23:06:00Z (or other formats given by the format_string argument) and generates a datetime object in the local timezone, with the offset between local and utc computed via datetime.datetime.utcnow() - datetime.datetime.now()''' offset = datetime.datetime.utcnow() - datetime.datetime.now() return datetime.datetime.strptime(d, format_string) - offset def ConvertToServerTime(times, svc_instance, zone="UTC"): '''Given a list of UTC or local datetime objects that reference the local machine's clock, query the server for its current time and compute the difference between the server clock and the local clock. Return the list of datetime objects corresponding to the input list but referencing the server clock.''' if zone == "UTC": offset_t = svc_instance.currentTime().replace(tzinfo=None) - datetime.datetime.utcnow() elif zone == "local": offset_t = svc_instance.currentTime().replace(tzinfo=None) - datetime.datetime.now() else: raise ValueError("zone has to be 'local' or 'UTC'") return [x + offset_t for x in times] def ConvertFromUtc(time): offset = datetime.datetime.utcnow() - datetime.datetime.now() offset = datetime.timedelta(hours=round(offset.seconds / float(3600))) return time - offset def AddUtcTzinfo(times): ''' Given a list of datetime objects, add UTC timezone info to them and returns the list of modified objects. Does not modify objects if they already have tzinfo. Note that the argument and return value are always lists. ''' res = [] for a in times: if isinstance(a,datetime.datetime) and a.tzinfo is None: res.append(a.replace(tzinfo=suds.sax.date.UtcTimezone())) return res