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