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