# VMware vSphere Python SDK # Copyright (c) 2008-2016 VMware, Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ## Diff any two objects import six from six.moves import zip from pyVmomi import VmomiSupport, types import logging from VmomiSupport import GetWsdlName, Type __Log__ = logging.getLogger('ObjDiffer') def LogIf(condition, message): """Log a message if the condition is met""" if condition: __Log__.debug(message) def IsPrimitiveType(obj): """See if the passed in type is a Primitive Type""" return (isinstance(obj, types.bool) or isinstance(obj, types.byte) or isinstance(obj, types.short) or isinstance(obj, six.integer_types) or isinstance(obj, types.double) or isinstance(obj, types.float) or isinstance(obj, six.string_types) or isinstance(obj, types.PropertyPath) or isinstance(obj, types.ManagedMethod) or isinstance(obj, types.datetime) or isinstance(obj, types.URI) or isinstance(obj, type)) class Differ: """Class for comparing two Objects""" def __init__(self, looseMatch=False, ignoreArrayOrder=True): self._looseMatch = looseMatch self._ignoreArrayOrder = ignoreArrayOrder def DiffAnyObjects(self, oldObj, newObj, isObjLink=False): """Diff any two Objects""" if oldObj == newObj: return True if not oldObj or not newObj: __Log__.debug('DiffAnyObjects: One of the objects is unset.') return self._looseMatch oldObjInstance = oldObj newObjInstance = newObj if isinstance(oldObj, list): oldObjInstance = oldObj[0] if isinstance(newObj, list): newObjInstance = newObj[0] # Need to see if it is a primitive type first since type information # will not be available for them. if (IsPrimitiveType(oldObj) and IsPrimitiveType(newObj) and oldObj.__class__.__name__ == newObj.__class__.__name__): if oldObj == newObj: return True elif oldObj == None or newObj == None: __Log__.debug('DiffAnyObjects: One of the objects in None') return False oldType = Type(oldObjInstance) newType = Type(newObjInstance) if oldType != newType: __Log__.debug('DiffAnyObjects: Types do not match %s != %s' % (repr(GetWsdlName(oldObjInstance.__class__)), repr(GetWsdlName(newObjInstance.__class__)))) return False elif isinstance(oldObj, list): return self.DiffArrayObjects(oldObj, newObj, isObjLink) elif isinstance(oldObjInstance, types.ManagedObject): return (not oldObj and not newObj) or (oldObj and newObj and oldObj._moId == newObj._moId) elif isinstance(oldObjInstance, types.DataObject): if isObjLink: bMatch = oldObj.GetKey() == newObj.GetKey() LogIf(not bMatch, 'DiffAnyObjects: Keys do not match %s != %s' % (oldObj.GetKey(), newObj.GetKey())) return bMatch return self.DiffDataObjects(oldObj, newObj) else: raise TypeError("Unknown type: "+repr(GetWsdlName(oldObj.__class__))) def DiffDoArrays(self, oldObj, newObj, isElementLinks): """Diff two DataObject arrays""" if len(oldObj) != len(newObj): __Log__.debug('DiffDoArrays: Array lengths do not match %d != %d' % (len(oldObj), len(newObj))) return False for i, j in zip(oldObj, newObj): if isElementLinks: if i.GetKey() != j.GetKey(): __Log__.debug('DiffDoArrays: Keys do not match %s != %s' % (i.GetKey(), j.GetKey())) return False else: if not self.DiffDataObjects(i, j): __Log__.debug( 'DiffDoArrays: one of the elements do not match') return False return True def DiffAnyArrays(self, oldObj, newObj, isElementLinks): """Diff two arrays which contain Any objects""" if len(oldObj) != len(newObj): __Log__.debug('DiffAnyArrays: Array lengths do not match. %d != %d' % (len(oldObj), len(newObj))) return False for i, j in zip(oldObj, newObj): if not self.DiffAnyObjects(i, j, isElementLinks): __Log__.debug('DiffAnyArrays: One of the elements do not match.') return False return True def DiffPrimitiveArrays(self, oldObj, newObj): """Diff two primitive arrays""" if len(oldObj) != len(newObj): __Log__.debug('DiffDoArrays: Array lengths do not match %d != %d' % (len(oldObj), len(newObj))) return False match = True if self._ignoreArrayOrder: oldSet = oldObj and frozenset(oldObj) or frozenset() newSet = newObj and frozenset(newObj) or frozenset() match = (oldSet == newSet) else: for i, j in zip(oldObj, newObj): if i != j: match = False break if not match: __Log__.debug( 'DiffPrimitiveArrays: One of the elements do not match.') return False return True def DiffArrayObjects(self, oldObj, newObj, isElementLinks=False): """Method which deligates the diffing of arrays based on the type""" if oldObj == newObj: return True if not oldObj or not newObj: return False if len(oldObj) != len(newObj): __Log__.debug('DiffArrayObjects: Array lengths do not match %d != %d' % (len(oldObj), len(newObj))) return False firstObj = oldObj[0] if IsPrimitiveType(firstObj): return self.DiffPrimitiveArrays(oldObj, newObj) elif isinstance(firstObj, types.ManagedObject): return self.DiffAnyArrays(oldObj, newObj, isElementLinks) elif isinstance(firstObj, types.DataObject): return self.DiffDoArrays(oldObj, newObj, isElementLinks) else: raise TypeError("Unknown type: %s" % oldObj.__class__) def DiffDataObjects(self, oldObj, newObj): """Diff Data Objects""" if oldObj == newObj: return True if not oldObj or not newObj: __Log__.debug('DiffDataObjects: One of the objects in None') return False oldType = Type(oldObj) newType = Type(newObj) if oldType != newType: __Log__.debug( 'DiffDataObjects: Types do not match for dataobjects. %s != %s' % (oldObj._wsdlName, newObj._wsdlName)) return False for prop in oldObj._GetPropertyList(): oldProp = getattr(oldObj, prop.name) newProp = getattr(newObj, prop.name) propType = oldObj._GetPropertyInfo(prop.name).type if not oldProp and not newProp: continue elif ((prop.flags & VmomiSupport.F_OPTIONAL) and self._looseMatch and (not newProp or not oldProp)): continue elif not oldProp or not newProp: __Log__.debug( 'DiffDataObjects: One of the objects has property %s unset' % prop.name) return False bMatch = True if IsPrimitiveType(oldProp): bMatch = oldProp == newProp elif isinstance(oldProp, types.ManagedObject): bMatch = self.DiffAnyObjects(oldProp, newProp, prop.flags & VmomiSupport.F_LINK) elif isinstance(oldProp, types.DataObject): if prop.flags & VmomiSupport.F_LINK: bMatch = oldObj.GetKey() == newObj.GetKey() LogIf(not bMatch, 'DiffDataObjects: Key match failed %s != %s' % (oldObj.GetKey(), newObj.GetKey())) else: bMatch = self.DiffAnyObjects(oldProp, newProp, prop.flags & VmomiSupport.F_LINK) elif isinstance(oldProp, list): bMatch = self.DiffArrayObjects(oldProp, newProp, prop.flags & VmomiSupport.F_LINK) else: raise TypeError("Unknown type: "+repr(propType)) if not bMatch: __Log__.debug('DiffDataObjects: Objects differ in property %s' % prop.name) return False return True def DiffAnys(obj1, obj2, looseMatch=False, ignoreArrayOrder=True): """Diff any two objects. Objects can either be primitive type or DataObjects""" differ = Differ(looseMatch = looseMatch, ignoreArrayOrder = ignoreArrayOrder) return differ.DiffAnyObjects(obj1, obj2)