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