Package Products :: Package ZenUtils :: Module MultiPathIndex
[hide private]
[frames] | no frames]

Source Code for Module Products.ZenUtils.MultiPathIndex

  1  ########################################################################### 
  2  # 
  3  # This program is part of Zenoss Core, an open source monitoring platform. 
  4  # Copyright (C) 2008, Zenoss Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify it 
  7  # under the terms of the GNU General Public License version 2 as published by 
  8  # the Free Software Foundation. 
  9  # 
 10  # For complete information please visit: http://www.zenoss.com/oss/ 
 11  # 
 12  ########################################################################### 
 13  from types import ListType, TupleType 
 14  from BTrees.IIBTree import IISet 
 15  import logging 
 16  LOG = logging.getLogger('ZenUtils.MultiPathIndex') 
 17   
 18  from Globals import DTMLFile 
 19   
 20  from ExtendedPathIndex import ExtendedPathIndex 
 21  from Products.PluginIndexes.common import safe_callable 
 22  from BTrees.OOBTree import OOSet 
 23   
24 -def _isSequenceOfSequences(seq):
25 if not seq: 26 return False 27 if not isinstance(seq, (TupleType, ListType)): 28 return False 29 for item in seq: 30 if not isinstance(item, (TupleType, ListType)): 31 return False 32 return True
33
34 -def _recursivePathSplit(seq):
35 if isinstance(seq, (TupleType, ListType)): 36 return map(_recursivePathSplit, seq) 37 if '/' in seq: 38 return seq.split('/') 39 else: 40 return seq
41
42 -class MultiPathIndex(ExtendedPathIndex):
43 """ 44 A path index that is capable of indexing multiple paths per object. 45 """ 46 meta_type = "MultiPathIndex" 47
48 - def search(self, path, default_level=0, depth=-1, navtree=0, 49 navtree_start=0):
50 51 results = ExtendedPathIndex.search(self, path, default_level, depth, navtree, 52 navtree_start) 53 # navtree is a special mode where you get the parents as well, obviously 54 # we can not do our special path sanity check in this case 55 if navtree or default_level: 56 return results 57 return self._pathSanityCheck(path, results)
58
59 - def _pathSanityCheck(self, searchPath, pathset):
60 """Extended path index uses a series of set "buckets" to store which items belong at 61 which point on the search path, this can sometimes cause issues if two points on the path 62 have the same name and thus belong to the same bucket 63 For instance: 64 /zport/dmd/Devices/Server/Windows/WMI/devices/test-winxp-1.zenoss.loc/os/software/VMware Tools 65 has another path at 66 /zport/dmd/Manfacturers/VMware/... 67 68 and our search returns a false positive because we search for 69 /zport/dmd/Devices/VMware 70 71 because we have the id for that component returned at each point along the path. 72 73 So this function iterates through all the return paths and makes sure the path that we are searching 74 for is in the path that is returned 75 """ 76 # only workds for absolute paths not relative 77 if not isinstance(searchPath, basestring) or not searchPath.startswith('/'): 78 return pathset 79 rids = IISet() 80 if not pathset: 81 return 82 for rid in pathset: 83 paths = self._unindex[rid] 84 for path in paths: 85 if path.startswith(searchPath): 86 rids.insert(rid) 87 break 88 89 return rids
90
91 - def getIndexSourceNames(self):
92 """ return names of indexed attributes """ 93 return (self.id, )
94
95 - def index_object(self, docid, obj, threshold=100):
96 """ hook for (Z)Catalog """ 97 98 f = getattr(obj, self.id, None) 99 if f is not None: 100 if safe_callable(f): 101 try: 102 paths = f() 103 except AttributeError: 104 return 0 105 else: 106 paths = f 107 else: 108 try: 109 paths = obj.getPhysicalPath() 110 except AttributeError: 111 return 0 112 113 if not paths: return 0 114 paths = _recursivePathSplit(paths) 115 if not _isSequenceOfSequences(paths): 116 paths = [paths] 117 118 if docid in self._unindex: 119 unin = self._unindex[docid] 120 # Migrate old versions of the index to use OOSet 121 if isinstance(unin, set): 122 unin = self._unindex[docid] = OOSet(unin) 123 for oldpath in list(unin): 124 if list(oldpath.split('/')) not in paths: 125 self.unindex_paths(docid, (oldpath,)) 126 else: 127 self._unindex[docid] = OOSet() 128 self._length.change(1) 129 130 self.index_paths(docid, paths) 131 132 return 1
133 134
135 - def index_paths(self, docid, paths):
136 for path in paths: 137 if isinstance(path, (list, tuple)): 138 path = '/'+ '/'.join(path[1:]) 139 comps = filter(None, path.split('/')) 140 parent_path = '/' + '/'.join(comps[:-1]) 141 142 for i in range(len(comps)): 143 self.insertEntry(comps[i], docid, i) 144 145 # Add terminator 146 self.insertEntry(None, docid, len(comps)-1, parent_path, path) 147 148 self._unindex.setdefault(docid, OOSet()).insert(path)
149 150
151 - def unindex_paths(self, docid, paths):
152 153 if not self._unindex.has_key(docid): 154 return 155 156 def unindex(comp, level, docid=docid, parent_path=None, 157 object_path=None): 158 try: 159 self._index[comp][level].remove(docid) 160 161 if not self._index[comp][level]: 162 del self._index[comp][level] 163 164 if not self._index[comp]: 165 del self._index[comp] 166 # Remove parent_path and object path elements 167 if parent_path is not None: 168 self._index_parents[parent_path].remove(docid) 169 if not self._index_parents[parent_path]: 170 del self._index_parents[parent_path] 171 if object_path is not None: 172 del self._index_items[object_path] 173 except KeyError: 174 # Failure 175 pass
176 177 old = set(self._unindex.get(docid, ())) 178 mkstr = lambda path:'/'.join(path) if isinstance(path, tuple) else path 179 paths = map(mkstr, paths) 180 toremove = set(paths) & old 181 tokeep = old - toremove 182 for path in toremove: 183 if not path.startswith('/'): 184 path = '/'+path 185 comps = path.split('/') 186 parent_path = '/'.join(comps[:-1]) 187 188 for level in range(len(comps[1:])): 189 comp = comps[level+1] 190 unindex(comp, level, docid, parent_path, path) 191 # Remove the terminator 192 level = len(comps[1:]) 193 comp = None 194 unindex(comp, level-1, parent_path=parent_path, object_path=path) 195 196 self._unindex[docid].remove(path) 197 198 if tokeep: 199 self.index_paths(docid, tokeep) 200 else: 201 # Cleared out all paths for the object 202 self._length.change(-1) 203 del self._unindex[docid]
204 205
206 - def unindex_object(self, docid):
207 """ hook for (Z)Catalog """ 208 if not self._unindex.has_key(docid): 209 return 210 self.unindex_paths(docid, self._unindex[docid])
211 212 manage = manage_main = DTMLFile('dtml/manageMultiPathIndex', globals()) 213 manage_main._setName('manage_main') 214 215 216 manage_addMultiPathIndexForm = DTMLFile('dtml/addMultiPathIndex', globals()) 217
218 -def manage_addMultiPathIndex(self, id, REQUEST=None, RESPONSE=None, 219 URL3=None):
220 """ 221 Add a MultiPathIndex. 222 """ 223 return self.manage_addIndex(id, 'MultiPathIndex', extra=None, 224 REQUEST=REQUEST, RESPONSE=RESPONSE, 225 URL1=URL3)
226