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

# Source Code for Module Products.ZenModel.ZenossInfo

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, 2009 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 as published by
      8  # the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13
     14  import os
     15  import os.path
     16  import sys
     17  import re
     18  from urllib import unquote
     19  from subprocess import Popen, PIPE, call
     20  from xml.dom.minidom import parse
     21  import shutil
     22  import traceback
     23  import logging
     24  log = logging.getLogger("zen.ZenossInfo")
     25
     26  from Globals import InitializeClass
     27  from OFS.SimpleItem import SimpleItem
     28  from AccessControl import ClassSecurityInfo
     29
     30  from Products.ZenModel.ZenModelItem import ZenModelItem
     31  from Products.ZenUtils import Time
     32  from Products.ZenUtils.Version import *
     33  from Products.ZenUtils.Utils import zenPath, binPath
     34  from Products.ZenWidgets import messaging
     35
     36  from Products.ZenEvents.UpdateCheck import UpdateCheck, parseVersion
     37
     38 -def manage_addZenossInfo(context, id='About', REQUEST=None):
     39      """
     40      Provide an instance of ZenossInfo for the portal.
     41      """
     42      about = ZenossInfo(id)
     43      context._setObject(id, about)
     44      if REQUEST is not None:
     45          REQUEST.RESPONSE.redirect(context.absolute_url() +'/manage_main')
     46
     47 -class ZenossInfo(ZenModelItem, SimpleItem):
     48
     49      portal_type = meta_type = 'ZenossInfo'
     50
     51      security = ClassSecurityInfo()
     52
     53      _properties = (
     54          {'id':'id', 'type':'string'},
     55          {'id':'title', 'type':'string'},
     56      )
     57
     58      factory_type_information = (
     59          {
     60              'immediate_view' : 'zenossInfo',
     61              'actions'        :
     62              (
     63                  { 'id'            : 'settings'
     64                  , 'name'          : 'Settings'
     65                  , 'action'        : '../dmd/editSettings'
     66                  , 'permissions'   : ( "Manage DMD", )
     67                  },
     68                  { 'id'            : 'manage'
     69                  , 'name'          : 'Commands'
     70                  , 'action'        : '../dmd/dataRootManage'
     71                  , 'permissions'   : ('Manage DMD',)
     72                  },
     73                  { 'id'            : 'users'
     74                  , 'name'          : 'Users'
     75                  , 'action'        : '../dmd/ZenUsers/manageUserFolder'
     76                  , 'permissions'   : ( 'Manage DMD', )
     77                  },
     78                  { 'id'            : 'packs'
     79                  , 'name'          : 'ZenPacks'
     80                  , 'action'        : '../dmd/ZenPackManager/viewZenPacks'
     81                  , 'permissions'   : ( "Manage DMD", )
     82                  },
     83                  { 'id'            : 'jobs'
     84                  , 'name'          : 'Jobs'
     85                  , 'action'        : '../dmd/joblist'
     86                  , 'permissions'   : ( "Manage DMD", )
     87                  },
     88                  { 'id'            : 'menus'
     89                  , 'name'          : 'Menus'
     90                  , 'action'        : '../dmd/editMenus'
     91                  , 'permissions'   : ( "Manage DMD", )
     92                  },
     93                  { 'id'            : 'portlets'
     94                  , 'name'          : 'Portlets'
     95                  , 'action'        : '../dmd/editPortletPerms'
     96                  , 'permissions'   : ( "Manage DMD", )
     97                  },
     98                  { 'id'            : 'daemons'
     99                  , 'name'          : 'Daemons'
    100                  , 'action'        : 'zenossInfo'
    101                  , 'permissions'   : ( "Manage DMD", )
    102                  },
    103                  { 'id'            : 'versions'
    104                  , 'name'          : 'Versions'
    105                  , 'action'        : 'zenossVersions'
    106                  , 'permissions'   : ( "Manage DMD", )
    107                  },
    108                  { 'id'            : 'backups'
    109                  , 'name'          : 'Backups'
    110                  , 'action'        : '../dmd/backupInfo'
    111                  , 'permissions'   : ( "Manage DMD", )
    112                  },
    113             )
    114            },
    115          )
    116
    117
    118 -    def titleOrId(self):
    119          return self.title or self.id
    120
    121
    122      security.declarePublic('getZenossVersion')
    123 -    def getZenossVersion(self):
    124          from Products.ZenModel.ZVersion import VERSION
    125          return Version.parse("Zenoss %s %s" %
    126                      (VERSION, self.getZenossRevision()))
    127
    128
    129      security.declarePublic('getZenossVersionShort')
    130 -    def getZenossVersionShort(self):
    131          return self.getZenossVersion().short()
    132
    133
    134 -    def getOSVersion(self):
    135          """
    136          This function returns a Version-ready tuple. For use with the Version
    137          object, use extended call syntax:
    138
    139              v = Version(*getOSVersion())
    140              v.full()
    141          """
    142          if os.name == 'posix':
    143              sysname, nodename, version, build, arch = os.uname()
    144              name = "%s (%s)" % (sysname, arch)
    145              major, minor, micro = getVersionTupleFromString(version)
    146              comment = ' '.join(os.uname())
    147          elif os.name == 'nt':
    148              from win32api import GetVersionEx
    149              major, minor, micro, platformID, additional = GetVersionEx()
    150              name = 'Windows %s (%s)' % (os.name.upper(), additional)
    151              comment = ''
    152          else:
    153              raise VersionNotSupported
    154          return Version(name, major, minor, micro, 0, comment)
    155
    156
    157 -    def getPythonVersion(self):
    158          """
    159          This function returns a Version-ready tuple. For use with the Version
    160          object, use extended call syntax:
    161
    162              v = Version(*getPythonVersion())
    163              v.full()
    164          """
    165          name = 'Python'
    166          major, minor, micro, releaselevel, serial = sys.version_info
    167          return Version(name, major, minor, micro)
    168
    169
    170 -    def getMySQLVersion(self):
    171          """
    172          This function returns a Version-ready tuple. For use with the Version
    173          object, use extended call syntax:
    174
    175              v = Version(*getMySQLVersion())
    176              v.full()
    177
    178          The regex was tested against the following output strings:
    179              mysql  Ver 14.12 Distrib 5.0.24, for apple-darwin8.5.1 (i686) using readline 5.0
    180              mysql  Ver 12.22 Distrib 4.0.24, for pc-linux-gnu (i486)
    181              mysql  Ver 14.12 Distrib 5.0.24a, for Win32 (ia32)
    182              /usr/local/zenoss/mysql/bin/mysql.bin  Ver 14.12 Distrib 5.0.45, for unknown-linux-gnu (x86_64) using readline 5.0
    183          """
    184          cmd = 'mysql --version'
    185          fd = os.popen(cmd)
    186          output = fd.readlines()
    187          version = "0"
    188          if fd.close() is None and len(output) > 0:
    189              output = output[0].strip()
    190              regexString = '.*(mysql).*Ver [0-9]{2}\.[0-9]{2} '
    191              regexString += 'Distrib ([0-9]+.[0-9]+.[0-9]+)(.*), for (.*\(.*\))'
    192              regex = re.match(regexString, output)
    193              if regex:
    194                  name, version, release, info = regex.groups()
    195          comment = 'Ver %s' % version
    196          # the name returned in the output is all lower case, so we'll make our own
    197          name = 'MySQL'
    198          major, minor, micro = getVersionTupleFromString(version)
    199          return Version(name, major, minor, micro, 0, comment)
    200
    201
    202 -    def getRRDToolVersion(self):
    203          """
    204          This function returns a Version-ready tuple. For use with the Version
    205          object, use extended call syntax:
    206
    207              v = Version(*getRRDToolVersion())
    208              v.full()
    209          """
    210          cmd = binPath('rrdtool')
    211          if not os.path.exists(cmd):
    212              cmd = 'rrdtool'
    213          fd = os.popen(cmd)
    214          output = fd.readlines()[0].strip()
    215          fd.close()
    216          name, version = output.split()[:2]
    217          major, minor, micro = getVersionTupleFromString(version)
    218          return Version(name, major, minor, micro)
    219
    220
    221 -    def getTwistedVersion(self):
    222          """
    223          This function returns a Version-ready tuple. For use with the Version
    224          object, use extended call syntax:
    225
    226              v = Version(*getTwistedVersion())
    227              v.full()
    228          """
    229          from twisted._version import version as v
    230
    231          return Version('Twisted', v.major, v.minor, v.micro)
    232
    233
    234 -    def getZopeVersion(self):
    235          """
    236          This function returns a Version-ready tuple. For use with the Version
    237          object, use extended call syntax:
    238
    239              v = Version(*getZopeVersion())
    240              v.full()
    241          """
    242          from App import version_txt as version
    243
    244          name = 'Zope'
    245          major, minor, micro, status, release = version.getZopeVersion()
    246          return Version(name, major, minor, micro)
    247
    248
    249 -    def getZenossRevision(self):
    250          """
    251          Determine the Zenoss version number
    252
    253          @return: version number or ''
    254          @rtype: string
    255          """
    256          try:
    257              products = zenPath("Products")
    258              cmd = "svn info '%s' 2>/dev/null | awk '/Revision/ {print $2}'" % products
    259              fd = os.popen(cmd)
    260              return fd.readlines()[0].strip()
    261          except:
    262              return ''
    263
    264
    265 -    def getNetSnmpVersion(self):
    266          from pynetsnmp.netsnmp import lib
    267          return Version.parse('NetSnmp %s ' % lib.netsnmp_get_version())
    268
    269
    270 -    def getPyNetSnmpVersion(self):
    271          from pynetsnmp.version import VERSION
    272          return Version.parse('PyNetSnmp %s ' % VERSION)
    273
    274
    275 -    def getWmiVersion(self):
    276          from pysamba.version import VERSION
    277          return Version.parse('Wmi %s ' % VERSION)
    278
    279
    280 -    def getAllVersions(self):
    281          """
    282          Return a list of version numbers for currently tracked component
    283          software.
    284          """
    285          versions = (
    286          {'header': 'Zenoss', 'data': self.getZenossVersion().full(),
    287              'href': "http://www.zenoss.com" },
    288          {'header': 'OS', 'data': self.getOSVersion().full(),
    289              'href': "http://www.tldp.org" },
    290          {'header': 'Zope', 'data': self.getZopeVersion().full(),
    291              'href': "http://www.zope.org" },
    292          {'header': 'Python', 'data': self.getPythonVersion().full(),
    293              'href': "http://www.python.org" },
    294          {'header': 'Database', 'data': self.getMySQLVersion().full(),
    295              'href': "http://www.mysql.com" },
    296          {'header': 'RRD', 'data': self.getRRDToolVersion().full(),
    297              'href': "http://oss.oetiker.ch/rrdtool" },
    298          {'header': 'Twisted', 'data': self.getTwistedVersion().full(),
    299              'href': "http:///twistedmatrix.com/trac" },
    300          )
    301          try:
    302              versions += (
    303                  {'header': 'NetSnmp', 'data': self.getNetSnmpVersion().full(),
    304                   'href': "http://net-snmp.sourceforge.net"  },
    305                  )
    306          except:
    307              pass
    308          try:
    309              versions += (
    310                  {'header': 'PyNetSnmp', 'data': self.getPyNetSnmpVersion().full(),
    311                   'href': "http://www.zenoss.com"  },
    312                  )
    313          except:
    314              pass
    315          try:
    316              versions += (
    317                  {'header': 'WMI', 'data': self.getWmiVersion().full(),
    318                   'href': "http://www.zenoss.com"  },
    319                  )
    320          except:
    321              pass
    322          return versions
    323
    324      security.declareProtected('View','getAllVersions')
    325
    326
    327 -    def getAllUptimes(self):
    328          """
    329          Return a list of daemons with their uptimes.
    330          """
    331          app = self.getPhysicalRoot()
    332          uptimes = []
    333          zope = {
    334              'header': 'Zope',
    335              'data': app.Control_Panel.process_time(),
    336          }
    337          uptimes.append(zope)
    338          return uptimes
    339      security.declareProtected('View','getAllUptimes')
    340
    341
    342
    343      daemon_tooltips= {
    344        "zeoctl": "Zope Enterprise Objects server (shares database between Zope instances)",
    345        "zopectl": "The Zope open source web application server",
    346        "zenhub": "Broker between the data layer and the collection daemons",
    347        "zenping": "ICMP ping status monitoring",
    348        "zensyslog": "Collection of and classification of syslog events",
    349        "zenstatus": "Active TCP connection testing of remote daemons",
    350        "zenactions": "Alerts (SMTP, SNPP and Maintenance Windows)",
    351        "zentrap": "Receives SNMP traps and turns them into events",
    352        "zenmodeler": "Configuration collection and configuration",
    353        "zenperfsnmp": "High performance asynchronous SNMP performance collection",
    354        "zencommand": "Runs plug-ins on the local box or on remote boxes through SSH",
    355        "zenprocess": "Process monitoring using SNMP host resources MIB",
    356        "zenwin": "Windows Service Monitoring (WMI)",
    357        "zeneventlog": "Collect (WMI) event log events (aka NT Eventlog)",
    358        "zenwinmodeler": "MS Windows configuration collection and configuration",
    359        "zendisc": "Discover the network topology to find active IPs and devices",
    360        "zenperfxmlrpc": "XML RPC data collection",
    361      }
    362
    363
    364 -    def getZenossDaemonStates(self):
    365          """
    366          Return a data structures representing the states of the supported
    367          Zenoss daemons.
    368          """
    369          states = []
    370          activeButtons = {'button1': 'Restart', 'button2': 'Stop', 'button2state': True}
    371          inactiveButtons = {'button1': 'Start', 'button2': 'Stop', 'button2state': False}
    372          for daemon in self._getDaemonList():
    373              pid = self._getDaemonPID(daemon)
    374              if pid:
    375                  buttons = activeButtons
    376                  msg = 'Up'
    377                  color = '#0F0'
    378              else:
    379                  buttons = inactiveButtons
    380                  msg = 'Down'
    381                  color = '#F00'
    382
    383              if daemon in self.daemon_tooltips:
    384                 tooltip= self.daemon_tooltips[ daemon ]
    385              else:
    386                 tooltip= ''
    387
    388              states.append({
    389                  'name': daemon,
    390                  'pid': pid,
    391                  'msg': msg,
    392                  'tooltip': tooltip,
    393                  'color': color,
    394                  'buttons': buttons})
    395
    396          return states
    397
    398
    399 -    def _pidRunning(self, pid):
    400          try:
    401              os.kill(pid, 0)
    402              return pid
    403          except OSError, ex:
    404              import errno
    405              errnum, msg = ex.args
    406              if errnum == errno.EPERM:
    407                  return pid
    408
    409
    410 -    def _getDaemonPID(self, name):
    411          """
    412          For a given daemon name, return its PID from a .pid file.
    413          """
    414          if name == 'zopectl':
    415              name = 'Z2'
    416          elif name == 'zeoctl':
    417              name = 'ZEO'
    418          elif '_' in name:
    419              collector, daemon = name.split('_', 1)
    420              name = '%s-%s' % (daemon, collector)
    421          else:
    422              name = "%s-localhost" % name
    423          pidFile = zenPath('var', '%s.pid' % name)
    424          if os.path.exists(pidFile):
    425              pid = open(pidFile).read()
    426              try:
    427                  pid = int(pid)
    428              except ValueError:
    429                  return None
    430              return self._pidRunning(int(pid))
    431          else:
    432              pid = None
    433          return pid
    434
    435
    436 -    def _getDaemonList(self):
    437          """
    438          Get the list of supported Zenoss daemons.
    439          """
    440          masterScript = binPath('zenoss')
    441          daemons = []
    442          for line in os.popen("%s list" % masterScript).readlines():
    443              daemons.append(line.strip())
    444          return daemons
    445
    446
    447 -    def getZenossDaemonConfigs(self):
    448          """
    449          Return a data structures representing the config infor for the
    450          supported Zenoss daemons.
    451          """
    452          return [ dict(name=x) for x in self._getDaemonList() ]
    453
    454 -    def _readLogFile(self, filename, maxBytes):
    455          fh = open(filename)
    456          try:
    457              size = os.path.getsize(filename)
    458              if size > maxBytes:
    459                  fh.seek(-maxBytes, 2)
    460                  # the first line could be a partial line, so skip it
    461                  fh.readline()
    462              return fh.read()
    463          finally:
    464             fh.close()
    465
    466 -    def _getLogPath(self, daemon):
    467          """
    468          Returns the path the log file for the daemon this is monkey-patched
    469          in the distributed collector zenpack to support the localhost
    470          subdirectory.
    471          """
    472          return zenPath('log', "%s.log" % daemon)
    473
    474 -    def getLogData(self, daemon, kb=500):
    475          """
    476          Get the last kb kilobytes of a daemon's log file contents.
    477          """
    478          maxBytes = 1024 * int(kb)
    479          if daemon == 'zopectl':
    480              daemon = 'event'
    481          elif daemon == 'zeoctl':
    482              daemon = 'zeo'
    483          if daemon == 'zopectl':
    484              daemon = 'event'
    485          elif daemon == 'zeoctl':
    486              daemon = 'zeo'
    487          filename = self._getLogPath(daemon)
    488          # if there is no data read, we don't want to return something that can
    489          # be interptreted as "None", so we make the default a single white
    490          # space
    491          data = ' '
    492          try:
    493              data = self._readLogFile(filename, maxBytes) or ' '
    494          except IOError:
    495              data = 'Error reading log file'
    496          return data
    497
    498
    499 -    def _getConfigFilename(self, daemon):
    500          if daemon == 'zopectl':
    501              daemon = 'zope'
    502          elif daemon == 'zeoctl':
    503              daemon = 'zeo'
    504          return zenPath('etc', "%s.conf" % daemon)
    505
    506 -    def _readConfigFile(self, filename):
    507         fh = open(filename)
    508         try:
    509             return fh.read()
    510         finally:
    511             fh.close()
    512
    513 -    def getConfigData(self, daemon):
    514          """
    515          Return the contents of the daemon's config file.
    516          """
    517          filename = self._getConfigFilename(daemon)
    518          # if there is no data read, we don't want to return something that can
    519          # be interptreted as "None", so we make the default a single white
    520          # space
    521          data = ' '
    522          try:
    523              data = self._readConfigFile(filename) or  ' '
    524          except IOError:
    525              data = 'Unable to read config file'
    526          return data
    527
    528
    529 -    def manage_saveConfigData(self, REQUEST):
    530          """
    531          Save config data from REQUEST to the daemon's config file.
    532          """
    533          daemon = REQUEST.form.get('daemon')
    534          filename = self._getConfigFilename(daemon)
    535          try:
    536              fh = open(filename, 'w+')
    537              data = REQUEST.form.get('data')
    538              fh.write(data)
    539          finally:
    540              fh.close()
    541          return self.callZenScreen(REQUEST, redirect=True)
    542
    543 -    def parseconfig(self, filename=""):
    544          """
    545          From the given configuration file construct a configuration object
    546          """
    547          configs = {}
    548
    549          config_file = open(filename)
    550          try:
    551              for line in config_file:
    552                  line = line.strip()
    553                  if line.startswith('#'): continue
    554                  if line == '': continue
    555
    556                  try:
    557                      key, value = line.split(None, 1)
    558                  except ValueError:
    559                      # Ignore errors
    560                      continue
    561                  configs[key] = value
    562          finally:
    563              config_file.close()
    564
    565          return configs
    566
    567 -    def show_daemon_xml_configs(self, daemon, REQUEST=None ):
    568          """
    569          Display the daemon configuration options in an XML format.
    570          Merges the defaults with options in the config file.
    571          """
    572          # Sanity check
    573          if not daemon or daemon == '':
    574              messaging.IMessageSender(self).sendToBrowser(
    575                  'Internal Error',
    576                  'Called without a daemon name',
    577                  priority=messaging.WARNING
    578              )
    579              return []
    580
    581          if daemon in [ 'zeoctl', 'zopectl' ]:
    582              return []
    583
    584          xml_default_name = zenPath( "etc", daemon + ".xml" )
    585          try:
    586              # Always recreate the defaults file in order to avoid caching issues
    587              log.debug("Creating XML config file for %s" % daemon)
    588              make_xml = ' '.join([daemon, "genxmlconfigs", ">", xml_default_name])
    589              proc = Popen(make_xml, shell=True, stdout=PIPE, stderr=PIPE)
    590              output, errors = proc.communicate()
    591              proc.wait()
    592              if proc.returncode != 0:
    593                  log.error(errors)
    594                  messaging.IMessageSender(self).sendToBrowser(
    595                      'Internal Error', errors,
    596                      priority=messaging.CRITICAL
    597                  )
    598                  return [["Output", output, errors, make_xml, "string"]]
    599          except Exception, ex:
    600              msg = "Unable to execute '%s'\noutput='%s'\nerrors='%s'\nex=%s" % (
    601                          make_xml, output, errors, ex)
    602              log.error(msg)
    603              messaging.IMessageSender(self).sendToBrowser(
    604                  'Internal Error', msg,
    605                  priority=messaging.CRITICAL
    606              )
    607              return [["Error in command", output, errors, make_xml, "string"]]
    608
    609          try:
    610              xml_defaults = parse( xml_default_name )
    611          except:
    612              info = traceback.format_exc()
    613              msg = "Unable to parse XML file %s because %s" % (
    614                  xml_default_name, info)
    615              log.error(msg)
    616              messaging.IMessageSender(self).sendToBrowser(
    617                  'Internal Error', msg,
    618                  priority=messaging.CRITICAL
    619              )
    620              return [["Error parsing XML file", xml_default_name, "XML", info, "string"]]
    621
    622          configfile = self._getConfigFilename(daemon)
    623          try:
    624              # Grab the current configs
    625              current_configs = self.parseconfig( configfile )
    626          except:
    627              info = traceback.format_exc()
    628              msg = "Unable to obtain current configuration from %s because %s" % (
    629                         configfile, info)
    630              log.error(msg)
    631              messaging.IMessageSender(self).sendToBrowser(
    632                  'Internal Error', msg,
    633                  priority=messaging.CRITICAL
    634              )
    635              return [["Configuration file issue", configfile, configfile, info, "string"]]
    636
    637          all_options = {}
    638          ignore_options = ['configfile', 'cycle', 'daemon', 'weblog']
    639          try:
    640              for option in xml_defaults.getElementsByTagName('option'):
    641                  id = option.attributes['id'].nodeValue
    642                  if id in ignore_options:
    643                      continue
    644                  try:
    645                      help = unquote(option.attributes['help'].nodeValue)
    646                  except:
    647                      help = ''
    648
    649                  try:
    650                      default = unquote(option.attributes['default'].nodeValue)
    651                  except:
    652                      default = ''
    653                  if default == '[]':  # Attempt at a list argument -- ignore
    654                      continue
    655
    656                  all_options[id] = [
    657                      id,
    658                      current_configs.get(id, default),
    659                      default,
    660                      help,
    661                      option.attributes['type'].nodeValue,
    662                  ]
    663
    664          except:
    665              info = traceback.format_exc()
    666              msg = "Unable to merge XML defaults with config file" \
    667                        " %s because %s" % (configfile, info)
    668              log.error(msg)
    669              messaging.IMessageSender(self).sendToBrowser(
    670                  'Internal Error', msg,
    671                  priority=messaging.CRITICAL
    672              )
    673              return [["XML file issue", daemon, xml_default_name, info, "string"]]
    674
    675          return [all_options[name] for name in sorted(all_options.keys())]
    676
    677
    678 -    def save_daemon_configs( self, REQUEST=None, **kwargs ):
    679          """
    680          Save the updated daemon configuration to disk.
    681          """
    682          if not REQUEST:
    683              return
    684          elif not hasattr(REQUEST, 'form'):
    685              return
    686
    687          # Sanity check
    688          formdata = REQUEST.form
    689          ignore_names = ['save_daemon_configs', 'zenScreenName', 'daemon_name']
    690
    691          daemon = formdata.get('daemon_name', '')
    692          if not daemon or daemon in ['zeoctl', 'zopectl']:
    693              return
    694          for item in ignore_names:
    695              del formdata[item]
    696
    697          if not formdata: # If empty, don't overwrite -- assume an error
    698              msg = "Received empty form data for %s config -- ignoring" % (
    699                        daemon)
    700              log.error(msg)
    701              messaging.IMessageSender(self).sendToBrowser(
    702                  'Internal Error', msg,
    703                  priority=messaging.CRITICAL
    704              )
    705              return
    706
    707          configfile = self._getConfigFilename(daemon)
    708          config_file_pre = configfile + ".pre"
    709          try:
    710              config = open( config_file_pre, 'w' )
    711              config.write("# Config file written out from GUI\n")
    712              for key, value in formdata.items():
    713                  if value == '':
    714                      continue
    715                  if key == value: # Yes, this is strange
    716                      value = True
    717                  config.write('%s %s\n' % (key, value))
    718              config.close()
    719          except Exception, ex:
    720              msg = "Couldn't write to %s because %s" % (config_file_pre, ex)
    721              log.error(msg)
    722              messaging.IMessageSender(self).sendToBrowser(
    723                  'Internal Error', msg,
    724                  priority=messaging.CRITICAL
    725              )
    726              config.close()
    727              try:
    728                  os.unlink(config_file_pre)
    729              except:
    730                  pass
    731              return
    732
    733          # If we got here things succeeded
    734          config_file_save = configfile + ".save"
    735          try:
    736              shutil.copy(configfile, config_file_save)
    737          except:
    738              log.error("Unable to make backup copy of %s" % configfile)
    739              # Don't bother telling the user
    740          try:
    741              shutil.move(config_file_pre, configfile)
    742          except:
    743              msg = "Unable to save contents to %s" % configfile
    744              log.error(msg)
    745              messaging.IMessageSender(self).sendToBrowser(
    746                  'Internal Error', msg,
    747                  priority=messaging.CRITICAL
    748              )
    749
    750
    751 -    def manage_daemonAction(self, REQUEST):
    752          """
    753          Start, stop, or restart Zenoss daemons from a web interface.
    754          """
    755          legalValues = ['start', 'restart', 'stop']
    756          action = (REQUEST.form.get('action') or '').lower()
    757          if action not in legalValues:
    758              return self.callZenScreen(REQUEST)
    759          daemonName = REQUEST.form.get('daemon')
    760          self.doDaemonAction(daemonName, action)
    761          return self.callZenScreen(REQUEST)
    762      security.declareProtected('Manage DMD','manage_daemonAction')
    763
    764
    765 -    def doDaemonAction(self, daemonName, action):
    766          """
    767          Do the given action (start, stop, restart) or the given daemon.
    768          Block until the action is completed.
    769          No return value.
    770          """
    771          import time
    772          import subprocess
    773          daemonPath = binPath(daemonName)
    774          if not os.path.isfile(daemonPath):
    775              return
    776          log.info('Telling %s to %s' % (daemonName, action))
    777          proc = subprocess.Popen([daemonPath, action], stdout=subprocess.PIPE,
    778                      stderr=subprocess.STDOUT)
    779          output, _ = proc.communicate()
    780          code = proc.wait()
    781          if code:
    782              log.info('Error from %s: %s (%s)' % (daemonName, output, code))
    783          if action in ('stop', 'restart'):
    784              time.sleep(2)
    785
    786
    787 -    def manage_checkVersion(self, optInOut=False, optInOutMetrics=False, REQUEST=None):
    788          "Check for Zenoss updates on the Zenoss website"
    789          self.dmd.versionCheckOptIn = optInOut
    790          self.dmd.reportMetricsOptIn = optInOutMetrics
    791          # There is a hidden field for manage_checkVersions in the form so that
    792          # the javascript submit() calls will end up calling this method.
    793          # That means that when user hits the Check Now button we will receive
    794          # 2 values for that field.  (button is that same field name.)
    795          # We want to initiate only when the button is pressed.
    796          if self.dmd.versionCheckOptIn \
    797            and REQUEST \
    798            and isinstance(REQUEST.form['manage_checkVersion'], list):
    799              uc = UpdateCheck()
    800              uc.check(self.dmd, self.dmd.ZenEventManager, manual=True)
    801          return self.callZenScreen(REQUEST)
    802      security.declareProtected('Manage DMD','manage_checkVersion')
    803
    804
    805 -    def lastVersionCheckedString(self):
    806          if not self.dmd.lastVersionCheck:
    807              return "Never"
    808          return Time.LocalDateTime(self.dmd.lastVersionCheck)
    809
    810
    811 -    def versionBehind(self):
    812          if self.dmd.availableVersion is None:
    813              return False
    814          if parseVersion(self.dmd.availableVersion) > self.getZenossVersion():
    815              return True
    816          return False
    817
    818
    819  InitializeClass(ZenossInfo)
    820