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

# Source Code for Module Products.ZenModel.DeviceOrganizer

      1  ##############################################################################
      2  #
      3  # Copyright (C) Zenoss, Inc. 2007, all rights reserved.
      4  #
      5  # This content is made available according to terms specified in
      6  # License.zenoss under the directory where your Zenoss product is installed.
      7  #
      8  ##############################################################################
      9
     10
     11  __doc__ = """DeviceOrganizer
     12  Base class for device organizers
     13  """
     14
     15  from itertools import ifilter
     16  from zope.event import notify
     17  from zope.interface import implements
     18  from ZODB.transact import transact
     19  from AccessControl import ClassSecurityInfo
     20  from Globals import InitializeClass
     21
     22  from Organizer import Organizer
     23  from DeviceManagerBase import DeviceManagerBase
     24  from Commandable import Commandable
     25  from ZenMenuable import ZenMenuable
     26  from MaintenanceWindowable import MaintenanceWindowable
     27  from AdministrativeRoleable import AdministrativeRoleable
     28  from Products.Zuul.catalog.events import IndexingEvent
     29  from Products.CMFCore.utils import getToolByName
     30
     31  from Products.ZenRelations.RelSchema import *
     32  from Products.ZenWidgets.interfaces import IMessageSender
     33
     34  from ZenossSecurity import *
     35  from Products.ZenUtils.Utils import unused, getObjectsFromCatalog
     36  from Products.ZenUtils.guid.interfaces import IGloballyIdentifiable
     37  from Products.ZenWidgets import messaging
     38  from Products.Jobber.zenmodel import DeviceSetLocalRolesJob
     39
     40  import logging
     41  LOG = logging.getLogger('ZenModel.DeviceOrganizer')
     42
     43 -class DeviceOrganizer(Organizer, DeviceManagerBase, Commandable, ZenMenuable,
     44                          MaintenanceWindowable, AdministrativeRoleable):
     45      """
     46      DeviceOrganizer is the base class for device organizers.
     47      It has lots of methods for rolling up device statistics and information.
     48      """
     49      implements(IGloballyIdentifiable)
     50
     51      security = ClassSecurityInfo()
     52
     53      # Screen action bindings (and tab definitions)
     54      factory_type_information = (
     55          {
     56              'immediate_view' : 'deviceOrganizerStatus',
     57              'actions'        :
     58              (
     59                  { 'id'            : 'status'
     60                  , 'name'          : 'Status'
     61                  , 'action'        : 'deviceOrganizerStatus'
     62                  , 'permissions'   : (ZEN_VIEW, )
     63                  },
     64                  { 'id'            : 'events'
     65                  , 'name'          : 'Events'
     66                  , 'action'        : 'viewEvents'
     67                  , 'permissions'   : (ZEN_VIEW, )
     68                  },
     69  #                { 'id'            : 'historyEvents'
     70  #                , 'name'          : 'History'
     71  #                , 'action'        : 'viewHistoryEvents'
     72  #                , 'permissions'   : (ZEN_VIEW, )
     73  #                },
     74                  { 'id'            : 'manage'
     75                  , 'name'          : 'Administration'
     76                  , 'action'        : 'deviceOrganizerManage'
     77                  , 'permissions'   : (ZEN_MANAGE_DMD,)
     78                  },
     79              )
     80           },
     81          )
     82
     83      _relations =  Organizer._relations + (
     84          ("maintenanceWindows", ToManyCont(
     85              ToOne, "Products.ZenModel.MaintenanceWindow", "productionState")),
     86          ("adminRoles", ToManyCont(
     87              ToOne,"Products.ZenModel.AdministrativeRole","managedObject")),
     88          ('userCommands', ToManyCont(
     89              ToOne, 'Products.ZenModel.UserCommand', 'commandable')),
     90          ('zenMenus', ToManyCont(
     91              ToOne, 'Products.ZenModel.ZenMenu', 'menuable')),
     92         )
     93
     94      security.declareProtected(ZEN_COMMON, "getSubDevices")
     95 -    def getSubDevices(self, devfilter=None):
     96          """
     97          Get all the devices under an instance of a DeviceOrganizer
     98
     99          @param devfilter: Filter function applied to returned list
    100          @type devfilter: function
    101          @return: Devices
    102          @rtype: list
    103
    104          """
    105          catalog = getToolByName(self.dmd.Devices, self.dmd.Devices.default_catalog)
    106
    107          if not 'path' in catalog.indexes():
    108              LOG.warn('Please run zenmigrate to create device path indexes.')
    109              return self.getSubDevices_recursive(devfilter)
    110
    111          devices = getObjectsFromCatalog(catalog, {
    112              'path': "/".join(self.getPhysicalPath())}, LOG)
    113          devices = ifilter(lambda dev:self.checkRemotePerm(ZEN_VIEW, dev),
    114                            devices)
    115          devices = ifilter(devfilter, devices)
    116          return list(devices)
    117
    118      security.declareProtected(ZEN_VIEW, "getSubDevicesGen")
    119 -    def getSubDevicesGen(self):
    120          """get all the devices under and instance of a DeviceGroup"""
    121          catalog = getToolByName(self.dmd.Devices, self.dmd.Devices.default_catalog)
    122
    123          if not 'path' in catalog.indexes():
    124              LOG.warn('Please run zenmigrate to create device path indexes.')
    125              yield self.getSubDevicesGen_recursive(devfilter)
    126
    127          devices = getObjectsFromCatalog(catalog, {
    128              'path': "/".join(self.getPhysicalPath())}, LOG)
    129          devices = ifilter(lambda dev:self.checkRemotePerm(ZEN_VIEW, dev),
    130                            devices)
    131          for device in devices:
    132              yield device
    133
    134      security.declareProtected(ZEN_COMMON, "getSubDevices_recursive")
    135 -    def getSubDevices_recursive(self, devfilter=None, devrel="devices"):
    136          devrelobj = getattr(self, devrel, None)
    137          if not devrelobj:
    138              raise AttributeError( "%s not found on %s" % (devrel, self.id) )
    139          devices = filter(devfilter, devrelobj())
    140          devices = [ dev for dev in devices
    141                      if self.checkRemotePerm(ZEN_VIEW, dev)]
    142          for subgroup in self.children(checkPerm=False):
    143              devices.extend(subgroup.getSubDevices_recursive(devfilter, devrel))
    144          return devices
    145
    146      security.declareProtected(ZEN_VIEW, "getSubDevicesGen")
    147 -    def getSubDevicesGen_recursive(self, devrel="devices"):
    148          """get all the devices under and instance of a DeviceGroup"""
    149          devrelobj = getattr(self, devrel, None)
    150          if not devrelobj:
    151              raise AttributeError( "%s not found on %s" % (devrel, self.id) )
    152          for dev in devrelobj.objectValuesGen():
    153              yield dev
    154          for subgroup in self.children():
    155              for dev in subgroup.getSubDevicesGen_recursive(devrel):
    156                  yield dev
    157
    158 -    def getSubDevicesGenTest(self, devrel="devices"):
    159          """get all the devices under and instance of a DeviceGroup"""
    160          devices = getattr(self, devrel, None)
    161          if not devices:
    162              raise AttributeError( "%s not found on %s" % (devrel, self.id) )
    163
    164
    165 -    def getMonitoredComponents(self):
    166          """Return monitored components for devices within this DeviceOrganizer.
    167          """
    168          cmps = []
    169          for dev in self.getSubDevicesGen():
    170              cmps.extend(dev.getMonitoredComponents())
    171          return cmps
    172
    173
    174 -    def getAllCounts(self, devrel="devices"):
    175          """Count all devices within a device group and get the
    176          ping and snmp counts as well"""
    177          devices = getattr(self, devrel)
    178          pingStatus = 0
    179          snmpStatus = 0
    180          devCount = devices.countObjects()
    181          for dev in devices():
    182              if dev.getPingStatusNumber() > 0:
    183                  pingStatus += 1
    184              if dev.getSnmpStatusNumber() > 0:
    185                  snmpStatus += 1
    186          counts = [devCount, pingStatus, snmpStatus]
    187          for group in self.children():
    188              sc = group.getAllCounts()
    189              for i in range(3): counts[i] += sc[i]
    190          return counts
    191
    192
    193 -    def countDevices(self, devrel="devices"):
    194          """count all devices with in a device group"""
    195          unused(devrel)
    196          count = self.devices.countObjects()
    197          for group in self.children():
    198              count += group.countDevices()
    199          return count
    200
    201
    202 -    def pingStatus(self, devrel="devices"):
    203          """aggrigate ping status for all devices in this group and below"""
    204          status = self._status("Ping", devrel)
    205          for group in self.children():
    206              status += group.pingStatus()
    207          return status
    208
    209
    210 -    def snmpStatus(self, devrel="devices"):
    211          """aggrigate snmp status for all devices in this group and below"""
    212          status = self._status("Snmp", devrel)
    213          for group in self.children():
    214              status += group.snmpStatus()
    215          return status
    216
    217
    218 -    def _buildDeviceList(self, deviceNames):
    219          """Build a device list for set methods"""
    220          if isinstance(deviceNames, basestring):
    221              deviceNames = [deviceNames]
    222          if deviceNames is not None:
    223              deviceNames = set(deviceNames)
    224          return [d.primaryAq() for d in self.getSubDevices()
    225                  if deviceNames is None or d.id in deviceNames
    226                  or d.getPrimaryId() in deviceNames]
    227
    228
    229 -    def deviceClassMoveTargets(self):
    230          """Return list of all organizers excluding our self."""
    231          targets = filter(lambda x: x != self.getOrganizerName(),
    232                              self.dmd.Devices.getOrganizerNames())
    233          return sorted(targets, key=lambda x: x.lower())
    234
    235
    236 -    def moveDevicesToClass(self, moveTarget, deviceNames=None, REQUEST=None):
    237          """Move Devices from one DeviceClass to Another"""
    238          if deviceNames is None:
    239              if REQUEST:
    240                  messaging.IMessageSender(self).sendToBrowser(
    241                      'Error',
    242                      'No devices were selected',
    243                      priority=messaging.WARNING
    244                  )
    245              return self.callZenScreen(REQUEST)
    246          deviceNames = [ x.split('/')[-1] for x in deviceNames ]
    247          return self.dmd.Devices.moveDevices(moveTarget, deviceNames, REQUEST)
    248
    249
    250 -    def _handleOrganizerCall(self, arg=None, deviceNames=None, \
    251                                  isOrganizer=False, REQUEST=None, \
    252                                  deviceMethod=None):
    253          """ Handle the many many methods that simply call one
    254          method on device differently"""
    255          #check to see if we have the essentials to work with
    256          if not deviceMethod: return
    257          if deviceNames is None and not isOrganizer:
    258              if REQUEST:
    259                  messaging.IMessageSender(self).sendToBrowser(
    260                      'Error',
    261                      'No devices were selected',
    262                      priority=messaging.WARNING
    263                  )
    264              return self.callZenScreen(REQUEST)
    265          for dev in self._buildDeviceList(deviceNames):
    266              devMethod = getattr(dev, deviceMethod, None)
    267              if devMethod and arg:
    268                  devMethod(arg)
    269              elif devMethod:
    270                  devMethod()
    271
    272
    273 -    def _buildReturnMessage(self, title, message, paths=None, \
    274                                 checkPaths=False):
    275          """build the standard return message for the various set
    276          methods"""
    277          if checkPaths:
    278              if paths:
    279                  if not isinstance(paths, basestring):
    280                      paths = ", ".join(paths)
    281                  message += paths
    282              else:
    283                  message = "%s unset" % message.split(" ")[0]
    284          if self.REQUEST.has_key('oneKeyValueSoInstanceIsntEmptyAndEvalToFalse'):
    285              return message
    286          else:
    287              IMessageSender(self).sendToBrowser(title, message)
    288              return self.callZenScreen(self.REQUEST)
    289
    290
    291      security.declareProtected(ZEN_CHANGE_DEVICE_PRODSTATE, 'setProdState')
    292 -    def setProdState(self, state, deviceNames=None,
    293                          isOrganizer=False, REQUEST=None):
    294          """Set production state of all devices in this Organizer.
    295          """
    296          self._handleOrganizerCall(state, deviceNames, isOrganizer, \
    297                                      REQUEST, "setProdState")
    298          if REQUEST:
    299              statename = self.convertProdState(state)
    300              msg = "Production state set to %s for %s." % (statename,
    301                                                            " ".join(deviceNames))
    302              return self._buildReturnMessage("Production State Changed", msg)
    303
    304
    305 -    def setPriority(self, priority, deviceNames=None,
    306                      isOrganizer=False, REQUEST=None):
    307          """Set prioirty of all devices in this Organizer.
    308          """
    309          self._handleOrganizerCall(priority, deviceNames, isOrganizer, \
    310                                      REQUEST, "setPriority")
    311          if REQUEST:
    312              priname = self.convertPriority(priority)
    313              msg = "Priority set to %s for %s." % (priname,
    314                                                    " ".join(deviceNames))
    315              return self._buildReturnMessage('Priority Changed', msg)
    316
    317
    318 -    def setPerformanceMonitor(self, performanceMonitor=None, deviceNames=None,
    319                                  isOrganizer=False, REQUEST=None):
    320          """ Provide a method to set performance monitor from any organizer """
    321          if not performanceMonitor:
    322              if REQUEST:
    323                  messaging.IMessageSender(self).sendToBrowser(
    324                      'Error',
    325                      'No monitor was selected',
    326                      priority=messaging.WARNING
    327                  )
    328              return self.callZenScreen(REQUEST)
    329          self._handleOrganizerCall(performanceMonitor, deviceNames, isOrganizer, \
    330                                      REQUEST, "setPerformanceMonitor")
    331          if REQUEST:
    332              msg = "Collector set to %s" % (performanceMonitor)
    333              return self._buildReturnMessage('Collector Set', msg)
    334
    335
    336 -    def setGroups(self, groupPaths=None, deviceNames=None,
    337                      isOrganizer=False, REQUEST=None):
    338          """ Provide a method to set device groups from any organizer """
    339          if not groupPaths: groupPaths = []
    340          self._handleOrganizerCall(groupPaths, deviceNames, isOrganizer, \
    341                                      REQUEST, "setGroups")
    342          if REQUEST:
    343              msg = "Groups set to"
    344              return self._buildReturnMessage('Groups Set', msg, groupPaths, True)
    345
    346
    347 -    def setSystems(self, systemPaths=None, deviceNames=None,
    348                      isOrganizer=False, REQUEST=None):
    349          """ Provide a method to set device systems from any organizer """
    350          if not systemPaths: systemPaths = []
    351          self._handleOrganizerCall(systemPaths, deviceNames, isOrganizer, \
    352                                      REQUEST, "setSystems")
    353          if REQUEST:
    354              msg = "Systems set to"
    355              return self._buildReturnMessage('Systems Set', msg, systemPaths, True)
    356
    357 -    def setLocation(self, locationPath="", deviceNames=None,
    358                      isOrganizer=False, REQUEST=None):
    359          """ Provide a method to set device location from any organizer """
    360          self._handleOrganizerCall(locationPath, deviceNames, isOrganizer, \
    361                                      REQUEST, "setLocation")
    362          if REQUEST:
    363              msg = "Location set to %s" % locationPath
    364              return self._buildReturnMessage('Location Set', msg)
    365
    366 -    def unlockDevices(self, deviceNames=None, isOrganizer=False, REQUEST=None):
    367          """Unlock devices"""
    368          self._handleOrganizerCall(None, deviceNames, isOrganizer, \
    369                                      REQUEST, "unlock")
    370          if REQUEST:
    371              msg = "Devices unlocked"
    372              return self._buildReturnMessage('Devices Unlocked', msg)
    373
    374 -    def lockDevicesFromDeletion(self, deviceNames=None,
    375                      sendEventWhenBlocked=None, isOrganizer=False, REQUEST=None):
    376          """Lock devices from being deleted"""
    377          self._handleOrganizerCall(sendEventWhenBlocked, deviceNames, isOrganizer, \
    378                                      REQUEST, "lockFromDeletion")
    379          if REQUEST:
    380              msg = "Devices locked from deletion"
    381              return self._buildReturnMessage('Devices Locked', msg)
    382
    383 -    def lockDevicesFromUpdates(self, deviceNames=None,
    384                  sendEventWhenBlocked=None, isOrganizer=False, REQUEST=None):
    385          """Lock devices from being deleted or updated"""
    386          self._handleOrganizerCall(sendEventWhenBlocked, deviceNames, isOrganizer, \
    387                                      REQUEST, "lockFromUpdates")
    388          if REQUEST:
    389              msg = "Devices locked from updates and deletion"
    390              return self._buildReturnMessage('Devices Locked', msg)
    391
    392
    393 -    def index_object(self, idxs=None):
    394          """No action.
    395          Index of subdevices will happen in manage_addAdministrativeRole
    396          """
    397          pass
    398
    399 -    def unindex_object(self):
    400          """No action.
    401          Unindex of subdevices will happen in manage_deleteAdministrativeRole
    402          """
    403          pass
    404
    405 -    def _setDeviceLocalRoles(self):
    406          def deviceChunk(devices, chunksize=10):
    407              i = 0
    408              maxi = len(devices)
    409              while i < maxi:
    410                  nexti = i+chunksize
    411                  yield devices[i:nexti]
    412                  i = nexti
    413
    414          @transact
    415          def setLocalRoles(devices):
    416              for device in devices:
    417                  device = device.primaryAq()
    418                  device.setAdminLocalRoles()
    419
    420          devices = self.getSubDevices()
    421          total = len(devices)
    422          count = 0
    423          for chunk in deviceChunk(devices):
    424              count += len(chunk)
    425              LOG.info("Setting admin roles on %d of total %d", count, total)
    426              setLocalRoles(chunk)
    427
    428 -    def _maybeCreateLocalRolesJob(self):
    429          """
    430          Look at our total number of devices if it is above the threshold
    431          then submit a job for updating the admin roles.
    432          """
    433          path = "/".join(self.getPhysicalPath())
    434          threshold = getattr(self.dmd.UserInterfaceSettings, "deviceMoveJobThreshold", 5)
    435
    436          # find out how many devices we have by just looking at the brains
    437          catalog = getToolByName(self.dmd.Devices, self.dmd.Devices.default_catalog)
    438          brains = catalog({ 'path' : path})
    439
    440          if len(brains) > threshold:
    441              job = self.dmd.JobManager.addJob(
    442                  DeviceSetLocalRolesJob, description="Update Local Roles on %s" % path,
    443                  kwargs=dict(organizerUid=path))
    444              href = "/zport/dmd/joblist#jobs:%s" % (job.getId())
    445              messaging.IMessageSender(self).sendToBrowser(
    446                  'Job Added',
    447                  'Job Added for setting the roles on the organizer %s, view the <a href="%s"> job log</a>' % (path, href)
    448              )
    449              return job
    450          return self._setDeviceLocalRoles()
    451
    452 -    def manage_addAdministrativeRole(self, newId, REQUEST=None):
    453          """
    454          Overrides AdministrativeRoleable.manage_addAdministrativeRole
    455          Adds an administrator to this DeviceOrganizer
    456
    457          @param userid: User to make an administrator of this Organizer
    458          @type userid: string
    459          """
    460
    461          AdministrativeRoleable.manage_addAdministrativeRole(self, newId)
    462          notify(IndexingEvent(self, ('allowedRolesAndUsers',), False))
    463          self._maybeCreateLocalRolesJob()
    464          if REQUEST:
    465              messaging.IMessageSender(self).sendToBrowser(
    466                  'Role Added',
    467                  'Administrative role %s was added.' % newId
    468              )
    469              return self.callZenScreen(REQUEST)
    470
    471
    472 -    def manage_editAdministrativeRoles(self, ids=(), role=(), REQUEST=None):
    473          """
    474          Overrides AdministrativeRoleable.manage_editAdministrativeRoles
    475          Edit the administrators to this DeviceOrganizer
    476          """
    477          AdministrativeRoleable.manage_editAdministrativeRoles(
    478                                           self,ids,role)
    479          notify(IndexingEvent(self, ('allowedRolesAndUsers',), False))
    480          self._maybeCreateLocalRolesJob()
    481          if REQUEST:
    482              messaging.IMessageSender(self).sendToBrowser(
    483                  'Role Added',
    484                  'Administrative roles were updated: %s' % ', '.join(ids)
    485              )
    486              return self.callZenScreen(REQUEST)
    487
    488
    489 -    def manage_deleteAdministrativeRole(self, delids=(), REQUEST=None):
    490          """
    491          Overrides AdministrativeRoleable.manage_deleteAdministrativeRole
    492          Deletes administrators to this DeviceOrganizer
    493
    494          @param delids: Users to delete from this Organizer
    495          @type delids: tuple of strings
    496          """
    497          AdministrativeRoleable.manage_deleteAdministrativeRole(self, delids)
    498          notify(IndexingEvent(self, ('allowedRolesAndUsers',), False))
    499          self._maybeCreateLocalRolesJob()
    500          if REQUEST:
    501              if delids:
    502                  messaging.IMessageSender(self).sendToBrowser(
    503                      'Roles Deleted',
    504                      'Administrative roles were deleted: %s' % ', '.join(delids)
    505                  )
    506              return self.callZenScreen(REQUEST)
    507
    508
    509 -    def manage_snmpCommunity(self, REQUEST=None):
    510          """reset Community on all devices in this Organizer.
    511          """
    512          [ d.manage_snmpCommunity() for d in self.getSubDevices() ]
    513          if REQUEST:
    514              return self.callZenScreen(REQUEST)
    515
    516 -    def setManageIp(self, REQUEST=None):
    517          """reset ip on all devices in this Organizer.
    518          """
    519          [ d.setManageIp() for d in self.getSubDevices() ]
    520          if REQUEST:
    521              return self.callZenScreen(REQUEST)
    522
    523 -    def collectDevice(self, REQUEST=None):
    524          """model all devices in this Organizer.
    525          """
    526          [ d.collectDevice() for d in self.getSubDevices() ]
    527          if REQUEST:
    528              return self.callZenScreen(REQUEST)
    529
    530 -    def _status(self, type, devrel="devices"):
    531          """build status info for device in this device group"""
    532          status = 0
    533          statatt = "get%sStatusNumber" % type
    534          devices = getattr(self, devrel, None)
    535          if not devices:
    536              raise AttributeError( "%s not found on %s" % (devrel, self.id) )
    537          for device in devices():
    538              if getattr(device, statatt, -1)() > 0:
    539                  status += 1
    540          return status
    541
    542 -    def statusColor(self, status):
    543          """colors for status fields for device groups"""
    544          retval = '#00ff00'
    545          if status == -1:
    546              retval = "#d02090"
    547          elif status == 1:
    548              retval = '#ffff00'
    549          elif status == 2:
    550              retval = '#ff9900'
    551          elif status > 2:
    552              retval = '#ff0000'
    553          return retval
    554
    555 -    def getUserCommandTargets(self):
    556          ''' Called by Commandable.doCommand() to ascertain objects on which
    557          a UserCommand should be executed.
    558          '''
    559          return self.getSubDevices()
    560
    561 -    def getUrlForUserCommands(self):
    562          return self.getPrimaryUrlPath() + '/deviceOrganizerManage'
    563
    564 -    def getLinks(self, recursive=True):
    565          """ Return all Links on all interfaces on all
    566              Devices in this Organizer
    567          """
    568          alllinks = []
    569          if recursive:
    570              devices = self.getSubDevicesGen()
    571          else:
    572              devices = self.devices.objectValuesGen()
    573          for device in devices:
    574              alllinks.extend(list(device.getLinks()))
    575          return alllinks
    576
    577      security.declareProtected(ZEN_VIEW, 'getIconPath')
    578 -    def getIconPath(self):
    579          """ Override the zProperty icon path and return a folder
    580          """
    581          return "/zport/dmd/img/icons/folder.png"
    582
    583      security.declareProtected(ZEN_VIEW, 'getPrettyLink')
    584 -    def getPrettyLink(self, noicon=False, shortDesc=False):
    585          """ Gets a link to this object, plus an icon """
    586          href = self.getPrimaryUrlPath().replace('%','%%')
    587          linktemplate = "<a href='"+href+"' class='prettylink'>%s</a>"
    588          icon = ("<div class='device-icon-container'> "
    589                  "<img class='device-icon' src='%s'/> "
    590                  "</div>") % self.getIconPath()
    591          name = self.getPrimaryDmdId()
    592          if noicon: icon=''
    593          if shortDesc: name = self.id
    594          rendered = icon + name
    595          if not self.checkRemotePerm("View", self):
    596              return rendered
    597          else:
    598              return linktemplate % rendered
    599
    600  InitializeClass(DeviceOrganizer)
    601