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