Package Products :: Package ZenModel :: Module DeviceClass |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenModel.DeviceClass

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 or (at your
      8  # option) any later version as published by the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13
     14  __doc__="""DeviceClass
     15  The primary organizer of device objects, managing zProperties and
     16  their acquisition.
     17  """
     18
     19  import time
     20  from cStringIO import StringIO
     21  import transaction
     22  import logging
     23  log = logging.getLogger('zen.DeviceClass')
     24
     25  import DateTime
     26  from zope.event import notify
     27  from zope.container.contained import ObjectMovedEvent
     28  from Globals import DTMLFile
     29  from Globals import InitializeClass
     30  from Acquisition import aq_base, aq_chain
     31  from AccessControl import ClassSecurityInfo
     32  from AccessControl import Permissions as permissions
     33  from ZODB.transact import transact
     34
     35  from Products.AdvancedQuery import MatchGlob, Or, Eq, RankByQueries_Max
     36  from Products.CMFCore.utils import getToolByName
     37  from Products.ZenMessaging.ChangeEvents.events import DeviceClassMovedEvent
     38  from Products.ZenModel.ZenossSecurity import *
     39  from Products.ZenRelations.RelSchema import *
     40  from Products.ZenRelations.ZenPropertyManager import Z_PROPERTIES
     41  from Products.ZenUtils.Search import makeCaseInsensitiveFieldIndex, makeCaseInsensitiveFieldIndex, makeCaseSensitiveKeywordIndex
     42  from Products.ZenUtils.Search import makeCaseInsensitiveKeywordIndex
     43  from Products.ZenUtils.Search import makePathIndex, makeMultiPathIndex
     44  from Products.ZenUtils.Utils import importClass, zenPath
     45  from Products.ZenUtils.guid.interfaces import IGlobalIdentifier
     46  from Products.ZenWidgets import messaging
     47  from Products.ZenUtils.FakeRequest import FakeRequest
     48  from Products.Zuul.catalog.events import IndexingEvent
     49
     50  import RRDTemplate
     51  from DeviceOrganizer import DeviceOrganizer
     52  from ZenPackable import ZenPackable
     53  from TemplateContainer import TemplateContainer
     54
     55  _marker = "__MARKER___"
     56
     57 -def manage_addDeviceClass(context, id, title = None, REQUEST = None):
     58      """make a device class"""
     59      dc = DeviceClass(id, title)
     60      context._setObject(id, dc)
     61      if REQUEST is not None:
     62          REQUEST['RESPONSE'].redirect(context.absolute_url() + '/manage_main')
     63
     64
     65  addDeviceClass = DTMLFile('dtml/addDeviceClass',globals())
     66
     67
     68 -class DeviceClass(DeviceOrganizer, ZenPackable, TemplateContainer):
     69      """
     70      DeviceClass is a device organizer that manages the primary classification
     71      of device objects within the Zenoss system.  It manages properties
     72      that are inherited through acquisition that modify the behavior of
     73      many different sub systems within Zenoss.
     74      It also handles the creation of new devices in the system.
     75      """
     76
     77      # Organizer configuration
     78      dmdRootName = "Devices"
     79
     80      manageDeviceSearch = DTMLFile('dtml/manageDeviceSearch',globals())
     81      manageDeviceSearchResults = DTMLFile('dtml/manageDeviceSearchResults',
     82                                              globals())
     83
     84      portal_type = meta_type = event_key = "DeviceClass"
     85
     86      default_catalog = 'deviceSearch'
     87
     88      _properties = DeviceOrganizer._properties + (
     89                      {'id':'devtypes', 'type':'lines', 'mode':'w'},
     90                     )
     91
     92      _relations = DeviceOrganizer._relations + ZenPackable._relations + \
     93                  TemplateContainer._relations + (
     94          ("devices", ToManyCont(ToOne,"Products.ZenModel.Device","deviceClass")),
     95          )
     96
     97      # Screen action bindings (and tab definitions)
     98      factory_type_information = (
     99          {
    100              'id'             : 'DeviceClass',
    101              'meta_type'      : 'DeviceClass',
    102              'description'    : """Base class for all devices""",
    103              'icon'           : 'DeviceClass_icon.gif',
    104              'product'        : 'ZenModel',
    105              'factory'        : 'manage_addDeviceClass',
    106              'immediate_view' : 'deviceOrganizerStatus',
    107              'actions'        :
    108              (
    109                  { 'name'          : 'Classes'
    110                  , 'action'        : 'deviceOrganizerStatus'
    111                  , 'permissions'   : ( permissions.view, )
    112                  },
    113                  { 'name'          : 'Events'
    114                  , 'action'        : 'viewEvents'
    115                  , 'permissions'   : (  permissions.view, )
    116                  },
    117                  { 'name'          : 'Configuration Properties'
    118                  , 'action'        : 'zPropertyEdit'
    119                  , 'permissions'   : (permissions.view,)
    120                  },
    121                  { 'name'          : 'Templates'
    122                  , 'action'        : 'perfConfig'
    123                  , 'permissions'   : ('Manage DMD',)
    124                  },
    125              )
    126           },
    127          )
    128
    129      security = ClassSecurityInfo()
    130
    131 -    def getPeerDeviceClassNames(self, pyclass=None):
    132          """
    133          Return a list of all device paths that have the Python class pyclass
    134
    135          @param pyclass: Python class (default is this class)
    136          @type pyclass: Python class
    137          @return: list of device paths
    138          @rtype: list of strings
    139          """
    140          dcnames = []
    141          if pyclass == None:
    142              pyclass = self.getPythonDeviceClass()
    143          dclass = self.getDmdRoot("Devices")
    144          for orgname in dclass.getOrganizerNames():
    145              org = dclass.getOrganizer(orgname)
    146              if issubclass(org.getPythonDeviceClass(), pyclass):
    147                  dcnames.append(orgname)
    148          dcnames.sort(lambda a, b: cmp(a.lower(), b.lower()))
    149          return dcnames
    150
    151      deviceMoveTargets = getPeerDeviceClassNames
    152      childMoveTargets = getPeerDeviceClassNames
    153
    154
    155 -    def createInstance(self, id):
    156          """
    157          Create an instance based on its location in the device tree
    158          walk up the primary aq path looking for a python instance class that
    159          matches the name of the closest node in the device tree.
    160
    161          @param id: id in DMD path
    162          @type id: string
    163          @return: new device object
    164          @rtype: device object
    165          """
    166          pyClass = self.getPythonDeviceClass()
    167          dev = pyClass(id)
    168          self.devices._setObject(id, dev)
    169          return self.devices._getOb(id)
    170
    171
    172 -    def getPythonDeviceClass(self):
    173          """
    174          Return the Python class object to be used for device instances in this
    175          device class.  This is done by walking up the aq_chain of a deviceclass
    176          to find a node that has the same name as a Python class or has an
    177          attribute named zPythonClass that matches a Python class.
    178
    179          @return: device class
    180          @rtype: device class
    181          """
    182          from Device import Device
    183          cname = getattr(self, "zPythonClass", None)
    184          if cname:
    185              try:
    186                  return importClass(cname)
    187              except ImportError:
    188                  log.exception("Unable to import class " + cname)
    189          return Device
    190
    191      @transact
    192 -    def _moveDevice(self, devname, target, targetClass):
    193          dev = self.findDeviceByIdExact(devname)
    194          if not dev:
    195              return
    196          guid = IGlobalIdentifier(dev).create()
    197          source = dev.deviceClass().primaryAq()
    198
    199          notify(DeviceClassMovedEvent(dev, dev.deviceClass().primaryAq(), target))
    200
    201          exported = False
    202          oldPath = source.absolute_url_path() + '/'
    203          if dev.__class__ != targetClass:
    204              from Products.ZenRelations.ImportRM import NoLoginImportRM
    205
    206              def switchClass(o, module, klass):
    207                  """
    208                  Create an XML string representing the module in a
    209                  new class.
    210
    211                  @param o: file-type object
    212                  @type o: file-type object
    213                  @param module: location in DMD
    214                  @type module: string
    215                  @param klass: class name
    216                  @type klass: string
    217                  @return: XML representation of the class
    218                  @rtype: string
    219                  """
    220                  from xml.dom.minidom import parse
    221
    222                  # Note: strips almost everything out of the move
    223                  #       as the new class may not have the same relations etc.
    224                  #       For example, moving SNMP host to a VMware class.
    225                  o.seek(0)
    226                  dom = parse(o)
    227                  root = dom.childNodes[0]
    228                  root.setAttribute('module', module)
    229                  root.setAttribute('class', klass)
    230                  for obj in root.childNodes:
    231                      if obj.nodeType != obj.ELEMENT_NODE:
    232                          continue # Keep XML-tree baggage
    233
    234                      name = obj.getAttribute('id')
    235                      if obj.tagName == 'property':
    236                          # Only remove modeler plugins, templates
    237                          # and z*Ignore zprops
    238                          if name in ('zCollectorPlugins', 'zDeviceTemplates') or \
    239                             name.endswith('Ignore'):
    240                              root.removeChild(obj)
    241
    242                      elif obj.tagName == 'toone' and \
    243                           name in ('perfServer', 'location'):
    244                          pass # Preserve collector name and location
    245
    246                      elif obj.tagName == 'tomany' and \
    247                           name in ('systems', 'groups'):
    248                          pass # Preserve the Groups and Systems groupings
    249
    250                      elif obj.tagName == 'tomanycont' and \
    251                           name in ('maintenanceWindows',
    252                                    'adminRoles',
    253                                    'userCommands'):
    254                          pass # Preserve maintenance windows, admins, commands
    255
    256                      else:
    257                          log.debug("Removing %s element id='%s'",
    258                                       obj.tagName, name)
    259                          root.removeChild(obj)
    260
    261                  importFile = StringIO()
    262                  dom.writexml(importFile)
    263                  importFile.seek(0)
    264                  return importFile
    265
    266              def devExport(d, module, klass):
    267                  """
    268                  Create an XML string representing the device d
    269                  at the DMD location module of type klass.
    270
    271                  @param module: location in DMD
    272                  @type module: string
    273                  @param klass: class name
    274                  @type klass: string
    275                  @return: XML representation of the class
    276                  @rtype: string
    277                  """
    278                  o = StringIO()
    279                  d.exportXml(o)
    280                  return switchClass(o, module, klass)
    281
    282              def devImport(xmlfile):
    283                  """
    284                  Load a new device from a file.
    285
    286                  @param xmlfile: file type object
    287                  @type xmlfile: file type object
    288                  """
    289                  im = NoLoginImportRM(target.devices)
    290                  im.loadObjectFromXML(xmlfile)
    291                  im.processLinks()
    292
    293              module = target.zPythonClass
    294              if module:
    295                  klass = target.zPythonClass.split('.')[-1]
    296              else:
    297                  module = 'Products.ZenModel.Device'
    298                  klass = 'Device'
    299              log.debug('Exporting device %s from %s', devname, source)
    300              xmlfile = devExport(dev, module, klass)
    301              log.debug('Removing device %s from %s', devname, source)
    302              source.devices._delObject(devname)
    303              log.debug('Importing device %s to %s', devname, target)
    304              devImport(xmlfile)
    305              exported = True
    306          else:
    307              dev._operation = 1
    308              source.devices._delObject(devname)
    309              target.devices._setObject(devname, dev)
    310          dev = target.devices._getOb(devname)
    311          IGlobalIdentifier(dev).guid = guid
    312          dev.setLastChange()
    313          dev.setAdminLocalRoles()
    314          dev.index_object()
    315          notify(IndexingEvent(dev, idxs=('path', 'searchKeywords'),
    316                               update_metadata=True))
    317
    318          return exported
    319
    320 -    def moveDevices(self, moveTarget, deviceNames=None, REQUEST=None):
    321          """
    322          Override default moveDevices because this is a contained relation.
    323          If the Python class bound to a DeviceClass is different we convert to
    324          the new Python class adding / removing relationships as needed.
    325
    326          @param moveTarget: organizer in DMD path
    327          @type moveTarget: string
    328          @param deviceNames: devices to move
    329          @type deviceNames: list of stringa
    330          @param REQUEST: Zope REQUEST object
    331          @type REQUEST: Zope REQUEST object
    332          """
    333          if not moveTarget or not deviceNames: return self()
    334          target = self.getDmdRoot(self.dmdRootName).getOrganizer(moveTarget)
    335          if isinstance(deviceNames, basestring): deviceNames = (deviceNames,)
    336          targetClass = target.getPythonDeviceClass()
    337          numExports = 0
    338          for devname in deviceNames:
    339              devicewasExported = self._moveDevice(devname, target, targetClass)
    340              if devicewasExported:
    341                  numExports += 1
    342          return numExports
    343
    344
    345      security.declareProtected(ZEN_DELETE_DEVICE, 'removeDevices')
    346 -    def removeDevices(self, deviceNames=None, deleteStatus=False,
    347                      deleteHistory=False, deletePerf=False,REQUEST=None):
    348          """
    349          See IManageDevice overrides DeviceManagerBase.removeDevices
    350          """
    351          if not deviceNames: return self()
    352          if isinstance(deviceNames, basestring): deviceNames = (deviceNames,)
    353          for devname in deviceNames:
    354              dev = self.findDevice(devname)
    355              dev.deleteDevice(deleteStatus=deleteStatus,
    356                          deleteHistory=deleteHistory, deletePerf=deletePerf)
    357          if REQUEST:
    358              messaging.IMessageSender(self).sendToBrowser(
    359                  'Devices Deleted',
    360                  "Devices were deleted: %s." % ', '.join(deviceNames)
    361              )
    362              if REQUEST.has_key('oneKeyValueSoInstanceIsntEmptyAndEvalToFalse'):
    363                  return 'Devices were deleted: %s.' % ', '.join(deviceNames)
    364              else:
    365                  return self.callZenScreen(REQUEST)
    366
    367
    368      security.declareProtected('View', 'getEventDeviceInfo')
    369 -    def getEventDeviceInfo(self):
    370          """
    371          getEventDeviceInfo() -> return the info for NcoEventPopulator
    372          """
    373          deviceInfo = {}
    374          for device in self.getSubDevices():
    375              systemNames = []
    376              for sys in device.systems.objectValuesAll():
    377                  systemNames.append(sys.getOrganizerName())
    378              systemNames = "|".join(systemNames)
    379              location = device.getLocationName()
    380              if not location: location = "Unknown"
    381              deviceInfo[device.id] = (systemNames, location,
    382                                      device.productionState,
    383                                      device.getDeviceClassPath())
    384          return deviceInfo
    385
    386
    387      security.declareProtected('View', 'getDeviceWinInfo')
    388 -    def getDeviceWinInfo(self, lastPoll=0, eventlog=False):
    389          """
    390          Return list of (devname,user,passwd,url) for each device.
    391          user and passwd are used to connect via wmi.
    392          """
    393          ffunc = None
    394          starttime = time.time()
    395          if lastPoll > 0:
    396              lastPoll = DateTime.DateTime(lastPoll)
    397              ffunc = lambda x: x.getSnmpLastCollection() > lastPoll
    398          if eventlog:
    399              ffunc = lambda x: x.zWinEventlog
    400          devinfo = []
    401          for dev in self.getSubDevices(devfilter=ffunc):
    402              if not dev.monitorDevice(): continue
    403              if dev.getProperty('zWmiMonitorIgnore', False): continue
    404              user = dev.getProperty('zWinUser','')
    405              passwd = dev.getProperty( 'zWinPassword', '')
    406              sev = dev.getProperty( 'zWinEventlogMinSeverity', '')
    407              devinfo.append((dev.id, str(user), str(passwd), sev, dev.absolute_url()))
    408          return starttime, devinfo
    409
    410
    411 -    def getWinServices(self):
    412          """
    413          Return a list of (devname, user, passwd, {'EvtSys':0,'Exchange':0})
    414          """
    415          svcinfo = []
    416          allsvcs = {}
    417          for s in self.getSubComponents("WinService"):
    418              svcs=allsvcs.setdefault(s.hostname(),{})
    419              name = s.name()
    420              if isinstance(name, unicode):
    421                  name = name.encode(s.zCollectorDecoding)
    422              svcs[name] = (s.getStatus(), s.getAqProperty('zFailSeverity'))
    423          for dev in self.getSubDevices():
    424              if not dev.monitorDevice(): continue
    425              if dev.getProperty( 'zWmiMonitorIgnore', False): continue
    426              svcs = allsvcs.get(dev.getId(), {})
    427              if not svcs and not dev.zWinEventlog: continue
    428              user = dev.getProperty('zWinUser','')
    429              passwd = dev.getProperty( 'zWinPassword', '')
    430              svcinfo.append((dev.id, str(user), str(passwd), svcs))
    431          return svcinfo
    432
    433
    434      security.declareProtected('View', 'searchDeviceSummary')
    435 -    def searchDeviceSummary(self, query):
    436          """
    437          Search device summary index and return device objects
    438          """
    439          if not query: return []
    440          zcatalog = self._getCatalog()
    441          if not zcatalog: return []
    442          results = zcatalog({'summary':query})
    443          return self._convertResultsToObj(results)
    444
    445
    446      security.declareProtected('View', 'searchInterfaces')
    447 -    def searchInterfaces(self, query):
    448          """
    449          Search interfaces index and return interface objects
    450          """
    451          if not query: return []
    452          zcatalog = getattr(self, 'interfaceSearch', None)
    453          if not zcatalog: return []
    454          results = zcatalog(query)
    455          return self._convertResultsToObj(results)
    456
    457
    458 -    def _convertResultsToObj(self, results):
    459          devices = []
    460          for brain in results:
    461              try:
    462                  devobj = self.getObjByPath(brain.getPrimaryId)
    463                  devices.append(devobj)
    464              except KeyError:
    465                  log.warn("bad path '%s' in index" % brain.getPrimaryId)
    466
    467          return devices
    468
    469 -    def _findDevice(self, devicename, useTitle=True):
    470          """
    471          Returns all devices whose ip/id/title match devicename.
    472          ip/id matches are at the front of the list.
    473
    474          @rtype: list of brains
    475          """
    476          idIpQuery = Or( MatchGlob('id', devicename),
    477                          Eq('getDeviceIp', devicename) )
    478          if useTitle:
    479              titleOrIdQuery = MatchGlob('titleOrId', devicename)
    480              query = Or( idIpQuery, titleOrIdQuery )
    481              rankSort = RankByQueries_Max( ( idIpQuery, 16 ),
    482                                            ( titleOrIdQuery, 8 ) )
    483              devices = self._getCatalog().evalAdvancedQuery(query, (rankSort,))
    484          else:
    485              devices = self._getCatalog().evalAdvancedQuery(idIpQuery)
    486          return devices
    487
    488 -    def findDevicePath(self, devicename):
    489          """
    490          Look up a device and return its path
    491          """
    492          ret = self._findDevice(devicename)
    493          if not ret: return ""
    494          return ret[0].getPrimaryId
    495
    496 -    def findDevice(self, devicename):
    497          """
    498          Returns the first device whose ip/id matches devicename.  If
    499          there is no ip/id match, return the first device whose title
    500          matches devicename.
    501          """
    502          ret = self._findDevice(devicename)
    503          if ret: return ret[0].getObject()
    504
    505 -    def findDeviceByIdOrIp(self, devicename):
    506          """
    507          Returns the first device that has an ip/id that matches devicename
    508          """
    509          ret = self._findDevice( devicename, False )
    510          if ret: return ret[0].getObject()
    511
    512 -    def findDeviceByIdExact(self, devicename):
    513          """
    514          Look up device in catalog and return it.  devicename
    515          must match device id exactly
    516          """
    517          for brains in self._getCatalog()(id=devicename):
    518              dev = brains.getObject()
    519              if dev.id == devicename:
    520                  return dev
    521
    522 -    def findDevicePingStatus(self, devicename):
    523          """
    524          look up device in catalog and return its pingStatus
    525          """
    526          dev = self.findDevice(devicename)
    527          if dev: return dev.getPingStatusNumber()
    528
    529
    530 -    def getSubComponents(self, meta_type="", monitored=True):
    531          """
    532          Return generator of components, by meta_type if specified
    533          """
    534          zcat = self.componentSearch
    535          res = zcat({'meta_type': meta_type, 'monitored': monitored})
    536          for b in res:
    537              try:
    538                  c = self.getObjByPath(b.getPrimaryId)
    539                  if self.checkRemotePerm("View", c):
    540                      yield c
    541              except KeyError:
    542                  log.warn("bad path '%s' in index 'componentSearch'",
    543                              b.getPrimaryId)
    544
    545
    546      security.declareProtected("ZenCommon", "getMonitoredComponents")
    547 -    def getMonitoredComponents(self):
    548          """
    549          Return monitored components for devices within this DeviceDeviceClass
    550          """
    551          return self.getSubComponents()
    552
    553
    554      security.declareProtected('View', 'getRRDTemplates')
    555 -    def getRRDTemplates(self, context=None):
    556          """
    557          Return the actual RRDTemplate instances.
    558          """
    559          templates = {}
    560          if not context: context = self
    561          mychain = aq_chain(context)
    562          mychain.reverse()
    563          for obj in mychain:
    564              try:
    565                  templates.update(dict((t.id, t) for t in obj.rrdTemplates()))
    566              except AttributeError:
    567                  pass
    568          return templates.values()
    569
    570
    571 -    def getAvailableTemplates(self):
    572          """
    573          Returns all available templates
    574          """
    575          def cmpTemplates(a, b):
    576              return cmp(a.id.lower(), b.id.lower())
    577          templates = self.getRRDTemplates()
    578          templates.sort(cmpTemplates)
    579          pdc = self.getPythonDeviceClass()
    580          return [ t for t in templates
    581              if issubclass(pdc, t.getTargetPythonClass()) ]
    582
    583
    584 -    def bindTemplates(self, ids=(), REQUEST=None):
    585          """
    586          This will bind available templates to the zDeviceTemplates
    587          """
    588          return self.setZenProperty('zDeviceTemplates', ids, REQUEST)
    589
    590 -    def removeZDeviceTemplates(self, REQUEST=None):
    591          """
    592          Deletes the local zProperty, zDeviceTemplates
    593          """
    594          if self.getPrimaryPath()[-2:] == ('dmd', 'Devices'):
    595              self.setZenProperty('zDeviceTemplates', ['Device'])
    596          else:
    597              self.deleteZenProperty('zDeviceTemplates')
    598          messaging.IMessageSender(self).sendToBrowser(
    599              'Bindings Reset',
    600              'Template bindings for this class were reset.'
    601          )
    602          return self.callZenScreen(REQUEST)
    603
    604
    605 -    def getAllRRDTemplates(self, rrdts=None):
    606          """
    607          Return all RRDTemplates at this level and below in the object tree.
    608          If rrdts is provided then it must be a list of RRDTemplates which
    609          will be extended with the templates from here and returned.
    610
    611          The original getAllRRDTemplates() method has been renamed
    612          getAllRRDTemplatesPainfully().  It walks the object tree looking
    613          for templates which is a very slow way of going about things.
    614          The newer RRDTemplate.YieldAllRRDTemplate() method uses the
    615          searchRRDTemplates catalog to speed things up dramatically.
    616          YieldAllRRDTemplates is smart enough to revert to
    617          getAllRRDTemplatesPainfully if the catalog is not present.
    618
    619          The searchRRDTemplates catalog was added in 2.2
    620          """
    621          if rrdts is None:
    622              rrdts = []
    623          rrdts.extend(RRDTemplate.YieldAllRRDTemplates(self))
    624          return rrdts
    625
    626
    627 -    def getAllRRDTemplatesPainfully(self, rrdts=None):
    628          """
    629          RRDTemplate.YieldAllRRDTemplates() is probably what you want.
    630          It takes advantage of the searchRRDTemplates catalog to get
    631          much better performance.  This method iterates over objects looking
    632          for templates which is a slow, painful process.
    633          """
    634          if rrdts is None: rrdts = []
    635          rrdts.extend(self.rrdTemplates())
    636          for dev in self.devices():
    637              rrdts += dev.objectValues('RRDTemplate')
    638              for comps in dev.getDeviceComponents():
    639                  rrdts += comps.objectValues('RRDTemplate')
    640          for child in self.children():
    641              child.getAllRRDTemplatesPainfully(rrdts)
    642          return rrdts
    643
    644
    645      security.declareProtected('Add DMD Objects', 'manage_addRRDTemplate')
    646 -    def manage_addRRDTemplate(self, id, REQUEST=None):
    647          """
    648          Add an RRDTemplate to this DeviceClass.
    649          """
    650          if not id: return self.callZenScreen(REQUEST)
    651          id = self.prepId(id)
    652          org = RRDTemplate.RRDTemplate(id)
    653          self.rrdTemplates._setObject(org.id, org)
    654          if REQUEST:
    655              messaging.IMessageSender(self).sendToBrowser(
    656                  'Template Added',
    657                  'The "%s" template has been created.' % id
    658              )
    659              return self.callZenScreen(REQUEST)
    660
    661
    662      security.declareProtected(ZEN_EDIT_LOCAL_TEMPLATES,
    663                                'manage_copyRRDTemplates')
    664 -    def manage_copyRRDTemplates(self, ids=(), REQUEST=None):
    665          """
    666          Put a reference to the objects named in ids in the clip board
    667          """
    668          if not ids: return self.callZenScreen(REQUEST)
    669          ids = [ id for id in ids if self.rrdTemplates._getOb(id, None) is not None]
    670          if not ids: return self.callZenScreen(REQUEST)
    671          cp = self.rrdTemplates.manage_copyObjects(ids)
    672          if REQUEST:
    673              resp=REQUEST['RESPONSE']
    674              resp.setCookie('__cp', cp, path='/zport/dmd')
    675              REQUEST['__cp'] = cp
    676              messaging.IMessageSender(self).sendToBrowser(
    677                  'Templates Copied',
    678                  'Templates have been copied: %s' % ', '.join(ids)
    679              )
    680              return self.callZenScreen(REQUEST)
    681          return cp
    682
    683
    684      security.declareProtected(ZEN_EDIT_LOCAL_TEMPLATES,
    685                                'manage_pasteRRDTemplates')
    686 -    def manage_pasteRRDTemplates(self, moveTarget=None, cb_copy_data=None, REQUEST=None):
    687          """
    688          Paste RRDTemplates that have been copied before.
    689          """
    690          cp = None
    691          if cb_copy_data: cp = cb_copy_data
    692          elif REQUEST:
    693              cp = REQUEST.get("__cp",None)
    694
    695          if cp:
    696              if moveTarget:
    697                  target = self.getDmdRoot(self.dmdRootName).getOrganizer(moveTarget)
    698              else:
    699                  target = self
    700              target.rrdTemplates.manage_pasteObjects(cp)
    701          else:
    702              target = None
    703
    704          if REQUEST:
    705              REQUEST['RESPONSE'].setCookie('__cp', 'deleted', path='/zport/dmd',
    706                              expires='Wed, 31-Dec-97 23:59:59 GMT')
    707              REQUEST['__cp'] = None
    708              if target:
    709                  message = "Template(s) copied to %s" % moveTarget
    710              else:
    711                  message = None
    712              messaging.IMessageSender(self).sendToBrowser(
    713                  'Template(s) Copied',
    714                  message
    715              )
    716              if not isinstance(REQUEST, FakeRequest):
    717                  url = target.getPrimaryUrlPath() + '/perfConfig'
    718                  if message:
    719                      url += '?message=%s' % message
    720                  REQUEST['RESPONSE'].redirect(url)
    721              else:
    722                  return self.callZenScreen(REQUEST)
    723
    724
    725      security.declareProtected(ZEN_EDIT_LOCAL_TEMPLATES,
    726                                'manage_copyAndPasteRRDTemplates')
    727 -    def manage_copyAndPasteRRDTemplates(self, ids=(), copyTarget=None, REQUEST=None):
    728          """
    729          Copy the selected templates into the specified device class.
    730          """
    731          if not ids:
    732              messaging.IMessageSender(self).sendToBrowser(
    733                  'Invalid',
    734                  'No templates were selected.',
    735                  priority=messaging.WARNING
    736              )
    737              return self.callZenScreen(REQUEST)
    738          if copyTarget is None:
    739              messaging.IMessageSender(self).sendToBrowser(
    740                  'Invalid',
    741                  'No target was selected.',
    742                  priority=messaging.WARNING
    743              )
    744              return self.callZenScreen(REQUEST)
    745          cp = self.manage_copyRRDTemplates(ids)
    746          return self.manage_pasteRRDTemplates(copyTarget, cp, REQUEST)
    747
    748
    749      security.declareProtected(ZEN_EDIT_LOCAL_TEMPLATES,
    750                                'manage_deleteRRDTemplates')
    751 -    def manage_deleteRRDTemplates(self, ids=(), paths=(), REQUEST=None):
    752          """
    753          Delete RRDTemplates from this DeviceClass
    754          (skips ones in other Classes)
    755          """
    756          if not ids and not paths:
    757              return self.callZenScreen(REQUEST)
    758          for id in ids:
    759              if (getattr(aq_base(self), 'rrdTemplates', False)
    760                  and getattr(aq_base(self.rrdTemplates),id,False)):
    761                  self.rrdTemplates._delObject(id)
    762          for path in paths:
    763              temp = self.dmd.getObjByPath(path)
    764              if temp.deviceClass():
    765                  temp.deviceClass().rrdTemplates._delObject(temp.id)
    766              else:
    767                  temp.device()._delObject(temp.id)
    768          if REQUEST:
    769              messaging.IMessageSender(self).sendToBrowser(
    770                  'Templates Deleted',
    771                  'Templates were deleted: %s' % ", ".join(ids)
    772              )
    773              return self.callZenScreen(REQUEST)
    774
    775
    776 -    def createCatalog(self):
    777          """
    778          Make the catalog for device searching
    779          """
    780          from Products.ZCatalog.ZCatalog import manage_addZCatalog
    781
    782          # Make catalog for Devices
    783          manage_addZCatalog(self, self.default_catalog,
    784              self.default_catalog)
    785          zcat = self._getOb(self.default_catalog)
    786          cat = zcat._catalog
    787          for idxname in ['id',
    788              'getDeviceIp','getDeviceClassPath','getProdState','titleOrId']:
    789              cat.addIndex(idxname, makeCaseInsensitiveFieldIndex(idxname))
    790          cat.addIndex('getPhysicalPath', makePathIndex('getPhysicalPath'))
    791          cat.addIndex('path', makeMultiPathIndex('path'))
    792          zcat.addColumn('getPrimaryId')
    793          zcat.addColumn('id')
    794          zcat.addColumn('path')
    795          zcat.addColumn('details')
    796
    797          # add extra fields to the catalog
    798          fieldIndexes = ['getHWSerialNumber', 'getHWTag',
    799                  'getHWManufacturerName', 'getHWProductClass',
    800                  'getOSProductName', 'getOSManufacturerName',
    801                  'getPerformanceServerName', 'ipAddressAsInt',
    802                  'getProductionStateString', 'getPriorityString',
    803                  'getLocationName']
    804          keywordIndexes = ['getSystemNames', 'getDeviceGroupNames']
    805
    806          # field indexes
    807          for indexName in fieldIndexes:
    808              cat.addIndex(indexName, makeCaseInsensitiveFieldIndex(indexName))
    809
    810          # keyword indexes
    811          for indexName in keywordIndexes:
    812              cat.addIndex(indexName, makeCaseInsensitiveKeywordIndex(indexName))
    813
    814          # permissions
    815          cat.addIndex('allowedRolesAndUsers', makeCaseSensitiveKeywordIndex('allowedRolesAndUsers'))
    816
    817          # make catalog for device components
    818          manage_addZCatalog(self, "componentSearch", "componentSearch")
    819          zcat = self._getOb("componentSearch")
    820          cat = zcat._catalog
    821          cat.addIndex('meta_type', makeCaseInsensitiveFieldIndex('meta_type'))
    822          cat.addIndex('getParentDeviceName',
    823              makeCaseInsensitiveFieldIndex('getParentDeviceName'))
    824          cat.addIndex('getCollectors',
    825              makeCaseInsensitiveKeywordIndex('getCollectors'))
    826          # XXX still using regular FieldIndex here for now, since this contains
    827          # binary information
    828          zcat.addIndex('monitored', 'FieldIndex')
    829          zcat.addColumn('getPrimaryId')
    830          zcat.addColumn('meta_type')
    831
    832
    833 -    def reIndex(self):
    834          """
    835          Go through all devices in this tree and reindex them.
    836          """
    837          zcat = getToolByName(self, self.default_catalog)
    838          zcat.manage_catalogClear()
    839          self.componentSearch.manage_catalogClear()
    840          for dev in self.getSubDevicesGen_recursive():
    841              dev.index_object()
    842              notify(IndexingEvent(dev))
    843              for comp in dev.getDeviceComponentsNoIndexGen():
    844                  comp.index_object()
    845                  notify(IndexingEvent(comp))
    846
    847
    848 -    def buildDeviceTreeProperties(self):
    849          """
    850          Create a new device tree with a default configuration
    851          """
    852          devs = self.getDmdRoot("Devices")
    853          for id, value, type in Z_PROPERTIES:
    854              if not devs.hasProperty(id):
    855                  devs._setProperty(id, value, type)
    856
    857 -    def zenPropertyOptions(self, propname):
    858          """
    859          Provide a set of default options for a zProperty
    860
    861          @param propname: zProperty name
    862          @type propname: string
    863          @return: list of zProperty options
    864          @rtype: list
    865          """
    866          if propname == 'zCollectorPlugins':
    867              from Products.DataCollector.Plugins import loadPlugins
    868              names = [ldr.pluginName for ldr in loadPlugins(self.dmd)]
    869              names.sort()
    870              return names
    871          if propname == 'zCommandProtocol':
    872              return ['ssh', 'telnet']
    873          if propname == 'zSnmpVer':
    874              return ['v1', 'v2c', 'v3']
    875          if propname == 'zSnmpAuthType':
    876              return ['', 'MD5', 'SHA']
    877          if propname == 'zSnmpPrivType':
    878              return ['', 'DES', 'AES']
    879          return DeviceOrganizer.zenPropertyOptions(self, propname)
    880
    881
    882 -    def pushConfig(self, REQUEST=None):
    883          """
    884          This will result in a push of all the devices to live collectors
    885
    886          @param REQUEST: Zope REQUEST object
    887          @type REQUEST: Zope REQUEST object
    888          """
    889          self._p_changed = True
    890          if REQUEST:
    891              messaging.IMessageSender(self).sendToBrowser(
    892                  'Pushed Changes',
    893                  'Changes to %s were pushed to collectors.' % self.id
    894              )
    895              return self.callZenScreen(REQUEST)
    896
    897
    898      security.declareProtected('Change Device', 'setLastChange')
    899 -    def setLastChange(self, value=None):
    900          """
    901          Set the changed datetime for this device.
    902
    903          @param value: changed datetime. Default is now.
    904          @type value: number
    905          """
    906          if value is None:
    907              value = time.time()
    908          self._lastChange = float(value)
    909
    910 -    def register_devtype(self, description, protocol):
    911          """
    912          Define this class in terms of a description of the devices it should
    913          contain and the protocol by which they would normally be monitored.
    914          """
    915          t = (description, protocol)
    916          if not self.isLocal('devtypes'):
    917              self._setProperty('devtypes', [], 'lines')
    918          if t not in self.devtypes:
    919              self.devtypes.append(t)
    920              self._p_changed = True
    921
    922 -    def unregister_devtype(self, description, protocol):
    923          t = (description, protocol)
    924          if hasattr(self, 'devtypes'):
    925              if t in self.devtypes:
    926                  self.devtypes.remove(t)
    927                  self._p_changed = True
    928
    929
    930  InitializeClass(DeviceClass)
    931