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

# Source Code for Module Products.ZenModel.ZDeviceLoader

      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__="""ZDeviceLoader.py
     15
     16  load devices from a GUI screen in the ZMI
     17
     18  """
     19
     20  import socket
     21  from logging import StreamHandler, Formatter, getLogger
     22  log = getLogger("zen.DeviceLoader")
     23
     24  from ipaddr import IPAddress
     25
     26  import transaction
     27  from ZODB.transact import transact
     28  from zope.interface import implements
     29  from AccessControl import ClassSecurityInfo
     30  from AccessControl import Permissions as permissions
     31
     32  from OFS.SimpleItem import SimpleItem
     33
     34  from Products.ZenUtils.Utils import isXmlRpc, setupLoggingHeader
     35  from Products.ZenUtils.Utils import binPath, clearWebLoggingStream
     36  from Products.ZenUtils.IpUtil import getHostByName, ipwrap
     37
     38  from Products.ZenUtils.Exceptions import ZentinelException
     39  from Products.ZenModel.Exceptions import DeviceExistsError, NoSnmp
     40  from Products.ZenModel.Device import manage_createDevice
     41  from Products.ZenWidgets import messaging
     42  from Products.Jobber.interfaces import IJobStatus
     43  from Products.Jobber.jobs import ShellCommandJob
     44  from Products.Jobber.status import SUCCESS, FAILURE
     45  from ZenModelItem import ZenModelItem
     46  from zExceptions import BadRequest
     47  from Products.ZenModel.interfaces import IDeviceLoader
     48  from Products.ZenEvents.Event import Event
     49
     50
     51 -def manage_addZDeviceLoader(context, id="", REQUEST = None):
     52      """make a DeviceLoader"""
     53      if not id: id = "DeviceLoader"
     54      d = ZDeviceLoader(id)
     55      context._setObject(id, d)
     56
     57      if REQUEST is not None:
     58          REQUEST['RESPONSE'].redirect(context.absolute_url()
     59                                       +'/manage_main')
     60
     61 -class BaseDeviceLoader(object):
     62      implements(IDeviceLoader)
     63
     64      context = None
     65      request = None
     66      deviceobj = None
     67
     68 -    def __init__(self, context):
     69          self.context = context
     70
     71 -    def run_zendisc(self, deviceName, devicePath, performanceMonitor):
     72          """
     73          Various ways of doing this should be implemented in subclasses.
     74          """
     75          raise NotImplementedError
     76
     77 -    def cleanup(self):
     78          """
     79          Delete the device object, presumably because discovery failed.
     80          """
     81          if self.deviceobj is not None:
     82              try:
     83                  self.deviceobj._p_jar.sync()
     84              except AttributeError:
     85                  pass
     86              else:
     87                  if self.deviceobj.isTempDevice():
     88                      # Flag's still True, so discovery failed somehow.  Clean up
     89                      # the device object.
     90                      self.deviceobj.deleteDevice(True, True, True)
     91                      self.deviceobj = None
     92
     93 -    def load_device(self, deviceName, devicePath='/Discovered',
     94                      discoverProto='snmp', performanceMonitor='localhost',
     95                      manageIp="", zProperties=None, deviceProperties=None):
     96          """
     97          Load a single device into the database.
     98          """
     99          # Make the config dictionaries the proper type
    100          try:
    101              if zProperties is None:
    102                  zProperties = {}
    103              if deviceProperties is None:
    104                  deviceProperties = {}
    105
    106              # Remove spaces from the name
    107              deviceName = deviceName.replace(' ', '')
    108              manageIp = manageIp.replace(' ', '')
    109
    110              # Check to see if we got passed in an IPv6 address
    111              try:
    112                  ipv6addr = IPAddress(deviceName)
    113                  manageIp = deviceName
    114                  deviceName = ipwrap(deviceName)
    115                  deviceProperties.setdefault('title', manageIp)
    116              except ValueError:
    117                  pass
    118
    119              # If we're not discovering and we have no IP, attempt the IP lookup
    120              # locally
    121              if discoverProto=='none':
    122                  if not manageIp:
    123                      try:
    124                          manageIp = getHostByName(deviceName)
    125                      except socket.error:
    126                          pass
    127
    128              # move the zProperties required by manage_createDevice to
    129              # deviceProperties
    130              for key in 'zSnmpCommunity', 'zSnmpPort', 'zSnmpVer':
    131                  if key in zProperties:
    132                      deviceProperties[key] = zProperties.pop(key)
    133
    134              # Make a device object in the database
    135              self.deviceobj = manage_createDevice(self.context, deviceName,
    136                                   devicePath,
    137                                   performanceMonitor=performanceMonitor,
    138                                   manageIp=manageIp,
    139                                   zProperties=zProperties,
    140                                   **deviceProperties)
    141
    142              # Flag this device as temporary. If discovery goes well, zendisc will
    143              # flip this to False.
    144              self.deviceobj._temp_device = True
    145
    146              # If we're not discovering, we're done
    147              if discoverProto=='none':
    148                  return self.deviceobj
    149
    150              # Otherwise, time for zendisc to do its thing
    151              self.run_zendisc(deviceName, devicePath, performanceMonitor)
    152
    153          finally:
    154              # Check discovery's success and clean up accordingly
    155              self.cleanup()
    156
    157          return self.deviceobj
    158
    159
    160 -class JobDeviceLoader(BaseDeviceLoader):
    161      implements(IDeviceLoader)
    162
    163 -    def run_zendisc(self, deviceName, devicePath, performanceMonitor):
    164          """
    165          In this subclass, just commit to database,
    166          so everybody can find the new device
    167          """
    168          pass
    169
    170 -    def cleanup(self):
    171          """
    172          Delegate cleanup to the Job itself.
    173          """
    174          pass
    175
    176
    177 -class DeviceCreationJob(ShellCommandJob):
    178 -    def __init__(self, jobid, deviceName, devicePath="/Discovered", tag="",
    179                   serialNumber="", rackSlot=0, productionState=1000,
    180                   comments="", hwManufacturer="", hwProductName="",
    181                   osManufacturer="", osProductName="", locationPath="",
    182                   groupPaths=[], systemPaths=[], performanceMonitor="localhost",
    183                   discoverProto="snmp", priority=3, manageIp="",
    184                   zProperties=None, title=None, zendiscCmd=[]):
    185
    186          # Store device name for later finding
    187          self.deviceName = deviceName
    188          self.devicePath = devicePath
    189          self.performanceMonitor = performanceMonitor
    190          self.discoverProto = discoverProto
    191          self.manageIp = manageIp.replace(' ', '')
    192
    193          # Save the device stuff to set after adding
    194          self.zProperties = zProperties
    195          self.deviceProps = dict(tag=tag,
    196                            serialNumber=serialNumber,
    197                            rackSlot=rackSlot,
    198                            productionState=productionState,
    199                            comments=comments,
    200                            hwManufacturer=hwManufacturer,
    201                            hwProductName = hwProductName,
    202                            osManufacturer = osManufacturer,
    203                            osProductName = osProductName,
    204                            locationPath = locationPath,
    205                            groupPaths = groupPaths,
    206                            systemPaths = systemPaths,
    207                            priority = priority,
    208                            title= title)
    209
    210          zendiscCmd.extend(['--job', jobid])
    211          super(DeviceCreationJob, self).__init__(jobid, zendiscCmd)
    212
    213
    214 -    def run(self, r):
    215
    216          self._v_loader = JobDeviceLoader(self)
    217
    218          @transact
    219          def createDevice(loader, zProperties):
    220              # set the status properties that were modified up until this
    221              # point in case of a Conflict Error
    222              loader.getStatus().setZProperties(**zProperties)
    223              # create the device
    224              loader._v_loader.load_device(loader.deviceName, loader.devicePath,
    225                                         loader.discoverProto,
    226                                         loader.performanceMonitor, loader.manageIp,
    227                                         loader.zProperties, loader.deviceProps)
    228
    229          # Create the device object and generate the zendisc command
    230          try:
    231              createDevice(self, self.zProperties)
    232          except Exception, e:
    233              log.exception("Encountered error. Rolling back initial device add.")
    234              transaction.abort()
    235              job_log = self.getStatus().getLog()
    236              job_log.write(str(e.args[0]))
    237              job_log.finish()
    238              self.finished(FAILURE)
    239              transaction.commit()
    240          else:
    241              if self.discoverProto != 'none':
    242                  super(DeviceCreationJob, self).run(r)
    243              else:
    244                  job_log = self.getStatus().getLog()
    245                  job_log.write("device added without modeling")
    246                  job_log.finish()
    247                  self.finished(SUCCESS)
    248
    249 -    def finished(self, r):
    250          if self._v_loader.deviceobj is not None and r!=FAILURE:
    251              result = SUCCESS
    252              self.dmd.ZenEventManager.sendEvent(Event(
    253                      summary='Added device: '+self.deviceName,
    254                      severity=2, #info
    255                      eventClass='/Change/Add', #zEventAction=history
    256                      device=self.deviceName))
    257          else:
    258              result = FAILURE
    259          super(DeviceCreationJob, self).finished(result)
    260
    261 -class WeblogDeviceLoader(BaseDeviceLoader):
    262 -    def __init__(self, context, request):
    263          self.context = context
    264          self.request = request
    265
    266 -    def run_zendisc(self, deviceName, devicePath, performanceMonitor):
    267          # Commit to database so everybody can find the new device
    268          transaction.commit()
    269          collector = self.deviceobj.getPerformanceServer()
    270          collector._executeZenDiscCommand(deviceName, devicePath,
    271                                           performanceMonitor,
    272                                           REQUEST=self.request)
    273
    274
    275 -class ZDeviceLoader(ZenModelItem,SimpleItem):
    276      """Load devices into the DMD database"""
    277
    278      portal_type = meta_type = 'DeviceLoader'
    279
    280      manage_options = ((
    281              {'label':'ManualDeviceLoader', 'action':'manualDeviceLoader'},
    282              ) + SimpleItem.manage_options)
    283
    284
    285      security = ClassSecurityInfo()
    286
    287      factory_type_information = (
    288          {
    289              'immediate_view' : 'addDevice',
    290              'actions'        :
    291              (
    292                  { 'id'            : 'status'
    293                  , 'name'          : 'Status'
    294                  , 'action'        : 'addDevice'
    295                  , 'permissions'   : (
    296                    permissions.view, )
    297                  },
    298              )
    299          },
    300          )
    301
    302 -    def __init__(self, id):
    303          self.id = id
    304
    305
    306 -    def loadDevice(self, deviceName, devicePath="/Discovered",
    307              tag="", serialNumber="",
    308              zSnmpCommunity="", zSnmpPort=161, zSnmpVer=None,
    309              rackSlot=0, productionState=1000, comments="",
    310              hwManufacturer="", hwProductName="",
    311              osManufacturer="", osProductName="",
    312              locationPath="", groupPaths=[], systemPaths=[],
    313              performanceMonitor="localhost",
    314              discoverProto="snmp",priority=3,REQUEST=None):
    315          """
    316          Load a device into the database connecting its major relations
    317          and collecting its configuration.
    318          """
    319          device = None
    320          if not deviceName: return self.callZenScreen(REQUEST)
    321          xmlrpc = isXmlRpc(REQUEST)
    322          if REQUEST and not xmlrpc:
    323              handler = setupLoggingHeader(self, REQUEST)
    324
    325          loader = WeblogDeviceLoader(self, REQUEST)
    326
    327          try:
    328              device = loader.load_device(deviceName, devicePath, discoverProto,
    329                                          performanceMonitor,
    330                                          zProperties=dict(
    331                                              zSnmpCommunity=zSnmpCommunity,
    332                                              zSnmpPort=zSnmpPort,
    333                                              zSnmpVer=zSnmpVer
    334                                          ),
    335                                          deviceProperties=dict(
    336                                              tag=tag,
    337                                              serialNumber=serialNumber,
    338                                              rackSlot=rackSlot,
    339                                              productionState=productionState,
    340                                              comments=comments,
    341                                              hwManufacturer=hwManufacturer,
    342                                              hwProductName=hwProductName,
    343                                              osManufacturer=osManufacturer,
    344                                              osProductName=osProductName,
    345                                              locationPath=locationPath,
    346                                              groupPaths=groupPaths,
    347                                              systemPaths=systemPaths,
    348                                              priority=priority
    349                                          ))
    350          except (SystemExit, KeyboardInterrupt):
    351              raise
    352          except ZentinelException, e:
    353              log.info(e)
    354              if xmlrpc: return 1
    355          except DeviceExistsError, e:
    356              log.info(e)
    357              if xmlrpc: return 2
    358          except NoSnmp, e:
    359              log.info(e)
    360              if xmlrpc: return 3
    361          except Exception, e:
    362              log.exception(e)
    363              log.exception('load of device %s failed' % deviceName)
    364              transaction.abort()
    365          if device is None:
    366              log.error("Unable to add the device %s" % deviceName)
    367          else:
    368              log.info("Device %s loaded!" % deviceName)
    369
    370          if REQUEST and not xmlrpc:
    371              self.loaderFooter(device, REQUEST.RESPONSE)
    372              clearWebLoggingStream(handler)
    373          if xmlrpc: return 0
    374
    375 -    def addManufacturer(self, newHWManufacturerName=None,
    376                          newSWManufacturerName=None, REQUEST=None):
    377          """add a manufacturer to the database"""
    378          mname = newHWManufacturerName
    379          field = 'hwManufacturer'
    380          if not mname:
    381              mname = newSWManufacturerName
    382              field = 'osManufacturer'
    383          try:
    384              self.getDmdRoot("Manufacturers").createManufacturer(mname)
    385          except BadRequest, e:
    386              if REQUEST:
    387                  messaging.IMessageSender(self).sendToBrowser(
    388                      'Error',
    389                      str(e),
    390                      priority=messaging.WARNING
    391                  )
    392              else:
    393                  raise e
    394
    395          if REQUEST:
    396              REQUEST[field] = mname
    397              return self.callZenScreen(REQUEST)
    398
    399
    400      security.declareProtected('Change Device', 'setHWProduct')
    401 -    def setHWProduct(self, newHWProductName, hwManufacturer, REQUEST=None):
    402          """set the productName of this device"""
    403          if not hwManufacturer and REQUEST:
    404              messaging.IMessageSender(self).sendToBrowser(
    405                  'Error',
    406                  'Please select a HW Manufacturer',
    407                  priority=messaging.WARNING
    408              )
    409              return self.callZenScreen(REQUEST)
    410
    411          self.getDmdRoot("Manufacturers").createHardwareProduct(
    412                                          newHWProductName, hwManufacturer)
    413          if REQUEST:
    414              REQUEST['hwProductName'] = newHWProductName
    415              return self.callZenScreen(REQUEST)
    416
    417
    418      security.declareProtected('Change Device', 'setOSProduct')
    419 -    def setOSProduct(self, newOSProductName, osManufacturer, REQUEST=None):
    420          """set the productName of this device"""
    421          if not osManufacturer and REQUEST:
    422              messaging.IMessageSender(self).sendToBrowser(
    423                  'Error',
    424                  'Please select an OS Manufacturer.',
    425                  priority=messaging.WARNING
    426              )
    427              return self.callZenScreen(REQUEST)
    428
    429          self.getDmdRoot("Manufacturers").createSoftwareProduct(
    430                                          newOSProductName, osManufacturer, isOS=True)
    431          if REQUEST:
    432              REQUEST['osProductName'] = newOSProductName
    433              return self.callZenScreen(REQUEST)
    434
    435
    436      security.declareProtected('Change Device', 'addLocation')
    437 -    def addLocation(self, newLocationPath, REQUEST=None):
    438          """add a location to the database"""
    439          try:
    440              self.getDmdRoot("Locations").createOrganizer(newLocationPath)
    441          except BadRequest, e:
    442              if REQUEST:
    443                  messaging.IMessageSender(self).sendToBrowser(
    444                      'Error',
    445                      str(e),
    446                      priority=messaging.WARNING
    447                  )
    448              else:
    449                  raise e
    450
    451          if REQUEST:
    452              REQUEST['locationPath'] = newLocationPath
    453              return self.callZenScreen(REQUEST)
    454
    455
    456      security.declareProtected('Change Device', 'addSystem')
    457 -    def addSystem(self, newSystemPath, REQUEST=None):
    458          """add a system to the database"""
    459          try:
    460              self.getDmdRoot("Systems").createOrganizer(newSystemPath)
    461          except BadRequest, e:
    462              if REQUEST:
    463                  messaging.IMessageSender(self).sendToBrowser(
    464                      'Error',
    465                      str(e),
    466                      priority=messaging.WARNING
    467                  )
    468              else:
    469                  raise e
    470
    471          syss = REQUEST.get('systemPaths', [])
    472          syss.append(newSystemPath)
    473          if REQUEST:
    474              REQUEST['systemPaths'] = syss
    475              return self.callZenScreen(REQUEST)
    476
    477
    478      security.declareProtected('Change Device', 'addDeviceGroup')
    479 -    def addDeviceGroup(self, newDeviceGroupPath, REQUEST=None):
    480          """add a device group to the database"""
    481          try:
    482              self.getDmdRoot("Groups").createOrganizer(newDeviceGroupPath)
    483          except BadRequest, e:
    484              if REQUEST:
    485                  messaging.IMessageSender(self).sendToBrowser(
    486                      'Error',
    487                      str(e),
    488                      priority=messaging.WARNING
    489                  )
    490              else:
    491                  raise e
    492
    493          groups = REQUEST.get('groupPaths', [])
    494          groups.append(newDeviceGroupPath)
    495          if REQUEST:
    496              REQUEST['groupPaths'] = groups
    497              return self.callZenScreen(REQUEST)
    498
    499
    500      security.declareProtected('Change Device', 'setPerformanceMonitor')
    501 -    def setPerformanceMonitor(self, newPerformanceMonitor, REQUEST=None):
    502          """add new performance monitor to the database"""
    503          try:
    504              self.getDmdRoot("Monitors").getPerformanceMonitor(newPerformanceMonitor)
    505          except BadRequest, e:
    506              if REQUEST:
    507                  messaging.IMessageSender(self).sendToBrowser(
    508                      'Error',
    509                      str(e),
    510                      priority=messaging.WARNING
    511                  )
    512              else:
    513                  raise e
    514          if REQUEST:
    515              REQUEST['performanceMonitor'] = newPerformanceMonitor
    516              return self.callZenScreen(REQUEST)
    517
    518
    519 -    def setupLog(self, response):
    520          """setup logging package to send to browser"""
    521          root = getLogger()
    522          self._v_handler = StreamHandler(response)
    523          fmt = Formatter("""<tr class="tablevalues">
    524          <td>%(asctime)s</td><td>%(levelname)s</td>
    525          <td>%(name)s</td><td>%(message)s</td></tr>
    526          """, "%Y-%m-%d %H:%M:%S")
    527          self._v_handler.setFormatter(fmt)
    528          root.addHandler(self._v_handler)
    529          root.setLevel(10)
    530
    531
    532 -    def clearLog(self):
    533          alog = getLogger()
    534          if getattr(self, "_v_handler", False):
    535              alog.removeHandler(self._v_handler)
    536
    537
    538 -    def loaderFooter(self, devObj, response):
    539          """add navigation links to the end of the loader output"""
    540          if not devObj: return
    541          devurl = devObj.absolute_url()
    542          response.write("""<tr class="tableheader"><td colspan="4">
    543              Navigate to device <a href=%s>%s</a></td></tr>"""
    544              % (devurl, devObj.getId()))
    545          response.write("</table></body></html>")
    546