# VMware vSphere Python SDK # Copyright (c) 2008-2013 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 from pyVmomi import VmomiSupport, types import itertools 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, types.int) or isinstance(obj, types.double) or isinstance(obj, types.float) or isinstance(obj, types.long) or isinstance(obj, types.str) or isinstance(obj, unicode) 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 itertools.izip(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 itertools.izip(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 itertools.izip(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)