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

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