Package ZenRelations :: Module RelationshipManager
[hide private]
[frames] | no frames]

Source Code for Module ZenRelations.RelationshipManager

  1   
  2  ########################################################################### 
  3  # 
  4  # This program is part of Zenoss Core, an open source monitoring platform. 
  5  # Copyright (C) 2007, Zenoss Inc. 
  6  # 
  7  # This program is free software; you can redistribute it and/or modify it 
  8  # under the terms of the GNU General Public License version 2 as published by 
  9  # the Free Software Foundation. 
 10  # 
 11  # For complete information please visit: http://www.zenoss.com/oss/ 
 12  # 
 13  ########################################################################### 
 14   
 15  __doc__ = """RelationshipManager 
 16   
 17  RelationshipManager is a mix in class to manage relationships 
 18  defined by the SchemaManager.   
 19  """ 
 20   
 21  from xml.sax import saxutils 
 22   
 23  import logging 
 24  log = logging.getLogger("zen.Relations") 
 25   
 26  import types 
 27   
 28  # Base classes for RelationshipManager 
 29  from PrimaryPathObjectManager import PrimaryPathObjectManager 
 30  from ZenPropertyManager import ZenPropertyManager 
 31   
 32  from Globals import DTMLFile 
 33  from Globals import InitializeClass 
 34  from AccessControl import ClassSecurityInfo 
 35  from Acquisition import aq_base 
 36  from App.Management import Tabs 
 37   
 38  from RelSchema import * 
 39  from Exceptions import * 
 40   
 41  from Products.ZenUtils.Utils import unused 
 42   
 43  zenmarker = "__ZENMARKER__" 
 44   
45 -def manage_addRelationshipManager(context, id, title=None, REQUEST = None):
46 """Relationship factory""" 47 rm = RelationshipManager(id) 48 context._setObject(id, rm) 49 if REQUEST: 50 REQUEST['RESPONSE'].redirect(context.absolute_url()+'/manage_main')
51 52 53 addRelationshipManager = DTMLFile('dtml/addRelationshipManager',globals()) 54 55
56 -class RelationshipManager(PrimaryPathObjectManager, ZenPropertyManager):
57 """ 58 RelationshipManger is an ObjectManager like class that can contain 59 relationships (in fact relationships can only be added to a 60 RelationshipManager). 61 62 Relationships are defined on an RM by the hash _relations. It 63 should be defined on the class so that it isn't stored in the database. 64 If there is inheritance involved remember to add the base class _relations 65 definition to the current class so that all relationships for the class 66 are defined on it. 67 68 remoteClassStr - is a string that represents the full path to the remote 69 class. Its a string because in most cases the classes 70 will be in different modules which would cause a recursive 71 import of the two modules. 72 73 _relations = ( 74 ("toonename", ToOne(ToMany, remoteClassStr, remoteName)), 75 ("tomanyname", ToMany(ToMany, remoteClassStr, remoteName)), 76 ) 77 """ 78 79 _relations = () 80 81 meta_type = 'Relationship Manager' 82 83 security = ClassSecurityInfo() 84 85 manage_options = ( 86 PrimaryPathObjectManager.manage_options + 87 ZenPropertyManager.manage_options 88 ) 89 90 manage_main=DTMLFile('dtml/RelationshipManagerMain', globals()) 91 92 # are we being deleted or moved 93 _operation = -1 94
95 - def __init__(self, id, title=None, buildRelations=True):
96 unused(title) 97 self.id = id 98 if buildRelations: self.buildRelations()
99 100
101 - def getRelationshipManagerId(self):
102 """ 103 Return our simple id if we are called from our primary path 104 else return the full primary id. 105 """ 106 if self.getPhysicalPath() == self.getPrimaryPath(): return self.id 107 return self.getPrimaryId()
108 109 110 ########################################################################## 111 # 112 # Methods for relationship management. 113 # 114 ########################################################################## 115 116
117 - def addRelation(self, name, obj):
118 """Form a bi-directional relationship.""" 119 rel = getattr(self, name, None) 120 if rel == None: 121 raise AttributeError("Relationship %s, not found" % name) 122 rel.addRelation(obj)
123 124
125 - def removeRelation(self, name, obj = None):
126 """ 127 Remove an object from a relationship. 128 If no object is passed all objects are removed. 129 """ 130 rel = getattr(self, name, None) 131 if rel == None: 132 raise AttributeError("Relationship %s, not found" % name) 133 rel.removeRelation(obj)
134 135
136 - def _setObject(self,id,object,roles=None,user=None,set_owner=1):
137 if object.meta_type in RELMETATYPES: 138 schema = self.lookupSchema(id) 139 if not schema.checkType(object): 140 raise ZenSchemaError("Relaitonship %s type %s != %s" % 141 (id, object.meta_type, schema.__class__.__name__)) 142 return PrimaryPathObjectManager._setObject(self, id, object, roles, 143 user, set_owner)
144 145
146 - def manage_beforeDelete(self, item, container):
147 """ 148 handle cut/past vs. delete 149 If we are being moved (cut/past) don't clear relationshp 150 if we are being deleted set all relationship to None so 151 that our related object don't have dangling references 152 """ 153 PrimaryPathObjectManager.manage_beforeDelete(self, item, container) 154 if self._operation > -1: self._operation = -1
155 156 157 ########################################################################## 158 # 159 # Methods for copy management 160 # 161 ########################################################################## 162
163 - def _getCopy(self, container):
164 """ 165 Create a copy of this relationship manager. This involes copying 166 relationships and removing invalid relations (ie ones with ToOne) 167 and performing copies of any contained objects. 168 Properties are also set on the new object. 169 """ 170 id = self.id 171 if getattr(aq_base(container), id, zenmarker) is not zenmarker: 172 id = "copy_of_" + id 173 cobj = self.__class__(id, buildRelations=False) #make new instance 174 cobj = cobj.__of__(container) #give the copy container's aq chain 175 for objid, sobj in self.objectItems(): 176 #if getattr(aq_base(self), objid, None): continue 177 csobj = sobj._getCopy(cobj) 178 cobj._setObject(csobj.id, csobj) 179 for name, value in self.propertyItems(): 180 cobj._updateProperty(name, value) 181 return aq_base(cobj)
182 183
184 - def _notifyOfCopyTo(self, container, op=0):
185 """Manage copy/move/rename state for use in manage_beforeDelete.""" 186 unused(container) 187 self._operation = op # 0 == copy, 1 == move, 2 == rename
188 189
190 - def cb_isMoveable(self):
191 """Prevent move unless we are being called from our primary path.""" 192 if (self.getPhysicalPath() == self.getPrimaryPath()): 193 return PrimaryPathObjectManager.cb_isMoveable(self) 194 return 0
195 196
197 - def moveMeBetweenRels(self, srcRelationship, destRelationship):
198 """ 199 Move a relationship manager without deleting its relationships. 200 """ 201 self._operation = 1 202 srcRelationship._delObject(self.id) 203 self = aq_base(self) 204 destRelationship._setObject(self.id, self) 205 return destRelationship._getOb(self.id)
206 207 208
209 - def moveObject(self, obj, destination):
210 """ 211 Move obj from this RM to the destination RM 212 """ 213 self._operation = 1 214 self._delObject(obj.id) 215 obj = aq_base(obj) 216 destination._setObject(obj.id, obj) 217 return destination._getOb(obj.id)
218 219 220 221 ########################################################################## 222 # 223 # Functions for examining a RelationshipManager's schema 224 # 225 ########################################################################## 226 227
228 - def buildRelations(self):
229 """build our relations based on the schema defined in _relations""" 230 if not getattr(self, "_relations", False): return 231 relnames = self.getRelationshipNames() 232 for name, schema in self._relations: 233 if name not in relnames: 234 self._setObject(name, schema.createRelation(name)) 235 if name in relnames: relnames.remove(name) 236 for rname in relnames: 237 self._delObject(rname)
238 239
240 - def lookupSchema(cls, relname):
241 """ 242 Lookup the schema definition for a relationship. 243 All base classes are checked until RelationshipManager is found. 244 """ 245 for name, schema in cls._relations: 246 if name == relname: return schema 247 raise ZenSchemaError("Schema for relation %s not found on %s" % 248 (relname, cls.__name__))
249 lookupSchema = classmethod(lookupSchema) 250 251
252 - def getRelationships(self):
253 """Returns a dictionary of relationship objects keyed by their names""" 254 return self.objectValues(spec=RELMETATYPES)
255 256
257 - def getRelationshipNames(self):
258 """Return our relationship names""" 259 return self.objectIds(spec=RELMETATYPES)
260 261
262 - def checkRelations(self, repair=False):
263 """Confirm the integrity of all relations on this object""" 264 log.info("checking relations on object %s", self.getPrimaryId()) 265 for rel in self.getRelationships(): 266 rel.checkRelation(repair)
267 268 269 ########################################################################## 270 # 271 # Functions for exporting RelationshipManager to XML 272 # 273 ########################################################################## 274
275 - def exportXml(self, ofile, ignorerels=[], root=False):
276 """Return an xml based representation of a RelationshipManager 277 <object id='/Devices/Servers/Windows/dhcp160.confmon.loc' 278 module='Products.Confmon.IpInterface' class='IpInterface'> 279 <property id='name'>jim</property> 280 <toone></toone> 281 <tomany></tomany> 282 <tomanycont></tomanycont> 283 </object> 284 """ 285 modname = self.__class__.__module__ 286 classname = self.__class__.__name__ 287 id = root and self.getPrimaryId() or self.id 288 stag = "<object id='%s' module='%s' class='%s'>\n" % ( 289 id , modname, classname) 290 ofile.write(stag) 291 self.exportXmlProperties(ofile) 292 self.exportXmlRelationships(ofile, ignorerels) 293 exportHook = getattr(aq_base(self), 'exportXmlHook', None) 294 if exportHook and callable(exportHook): 295 self.exportXmlHook(ofile, ignorerels) 296 ofile.write("</object>\n")
297 298
299 - def exportXmlProperties(self,ofile):
300 """Return an xml representation of a RelationshipManagers properties 301 <property id='name' type='type' mode='w' select_variable='selectvar'> 302 value 303 </property> 304 value will be converted to is correct python type on import 305 """ 306 for prop in self._properties: 307 if not prop.has_key('id'): continue 308 id = prop['id'] 309 ptype = prop['type'] 310 value = getattr(aq_base(self), id, None) # use aq_base? 311 if not value and ptype not in ("int","float","boolean"): continue 312 stag = [] 313 stag.append('<property') 314 for k, v in prop.items(): 315 if ptype != 'selection' and k == 'select_variable': continue 316 v = saxutils.quoteattr(str(v)) 317 stag.append('%s=%s' % (k, v)) 318 stag.append('>') 319 ofile.write(' '.join(stag)+"\n") 320 if type(value) not in types.StringTypes: 321 value = unicode(value) 322 elif type(value) == types.StringType: 323 value = value.decode('latin-1') 324 ofile.write(saxutils.escape(value).encode('utf-8')+"\n") 325 ofile.write("</property>\n")
326 327
328 - def exportXmlRelationships(self, ofile, ignorerels=[]):
329 """Return an xml representation of Relationships""" 330 for rel in self.getRelationships(): 331 if rel.id in ignorerels: continue 332 rel.exportXml(ofile, ignorerels)
333 334 335 ########################################################################## 336 # 337 # Methods called from UI code. 338 # 339 ########################################################################## 340 341 security.declareProtected('Manage Relations', 'manage_addRelation')
342 - def manage_addRelation(self, name, obj, REQUEST=None):
343 """make a relationship""" 344 self.addRelation(name, obj) 345 if REQUEST: return self.callZenScreen(REQUEST)
346 347 348 security.declareProtected('Manage Relations', 'manage_removeRelation')
349 - def manage_removeRelation(self, name, id=None, REQUEST=None):
350 """remove a relationship to be called from UI""" 351 rel = getattr(self, name, None) 352 if rel == None: 353 raise AttributeError("Relationship %s, not found" % name) 354 rel._delObject(id) 355 if REQUEST: return self.callZenScreen(REQUEST)
356 357
358 - def manage_workspace(self, REQUEST):
359 """return the workspace of the related object using its primary path""" 360 url = REQUEST['URL'] 361 myp = self.getPrimaryUrlPath() 362 if url.find(myp) > 0: 363 Tabs.manage_workspace(self, REQUEST) 364 else: 365 from zExceptions import Redirect 366 raise Redirect( myp+'/manage_workspace' )
367 368 369 370 InitializeClass(RelationshipManager) 371