Package Products :: Package ZenUtils :: Module RunCommand |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenUtils.RunCommand

      1  #!/usr/bin/env python
      2  #############################################################################
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2010, 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  __doc__ = """RunCommand
     14  Run an event command on the local device or on the remote collector.
     15  Assumes that SSH keys have been set up to all remote collectors.
     16
     17    Example usage:
     18
     19  dcsh -collector=xxx 'zencommand run'
     20
     21    The actual command to run *MUST* be in quotes!
     22  """
     23
     24  import logging
     25  log = logging.getLogger('zen.runCommand')
     26
     27  import os
     28  from subprocess import Popen, PIPE
     29  import StringIO
     30  import signal
     31
     32  import Globals
     33  from Products.ZenUtils.ZenScriptBase import ZenScriptBase
     34  from Products.ZenUtils.Utils import zenPath
     35
     36
     37 -class collectorStats:
     38 -    def __init__(self, id, hostname):
     39          self.id = id
     40          self.hostname = hostname
     41          self.succeeded = False
     42          self.stdout = ''
     43          self.stderr = ''
     44
     45
     46 -class RunCommand(ZenScriptBase):
     47 -    def __init__(self):
     48          ZenScriptBase.__init__(self, connect=True)
     49
     50 -    def buildOptions(self):
     51          ZenScriptBase.buildOptions(self)
     52          self.parser.add_option('--collector', dest='collector',
     53              help="Name of specific collector on which to run the command")
     54          self.parser.add_option('--timeout', dest='timeout',
     55                             default=60, type="int",
     56                             help="Kill the process after this many seconds.")
     57          self.parser.add_option('-n', '--useprefix', action='store_false',
     58                                 dest='useprefix', default=True,
     59                             help="Prefix the collector name for remote servers")
     60
     61 -    def run(self):
     62          collectors = self._getCollectors()
     63          if collectors is None:
     64              return
     65          for collector in collectors:
     66              self._runCommandOnCollector(collector)
     67          self.report(collectors)
     68
     69 -    def _getCollectors(self):
     70          if self.options.collector:
     71              try:
     72                  collectors = [self.dmd.Monitors.Performance._getOb(
     73                      self.options.collector)]
     74              except AttributeError:
     75                  log.critical("No collector named %s could be found. Exiting",
     76                      self.options.collector)
     77                  return
     78          else:
     79              collectors = self.dmd.Monitors.Performance.objectValues(
     80                  spec="PerformanceConf")
     81
     82          return [collectorStats(x.id, getattr(x, 'hostname', x.id)) \
     83                          for x in collectors]
     84
     85 -    def report(self, collectors):
     86          header = """
     87  Collector       StdOut/Stderr"""
     88          delimLen = 65
     89          print header
     90          print '-' * delimLen
     91
     92          collectorNames = dict(zip(map(lambda x: x.id, collectors), collectors))
     93          for name in sorted(collectorNames.keys()):
     94              collector = collectorNames[name]
     95              print "%s     %s %s" % (name, collector.stdout, collector.stderr)
     96              print '-' * delimLen
     97
     98 -    def _runCommandOnCollector(self, collector):
     99          def killTimedOutProc(signum, frame):
    100              log.error("Killing process id %s ...", proc.pid)
    101              try:
    102                  os.kill(proc.pid, signal.SIGKILL)
    103              except OSError:
    104                  pass
    105
    106          if collector.hostname == 'localhost':
    107              collectorCommand = self.args
    108          else:
    109              # Quick check to see if we can SSH to the collector.
    110              #p1 = Popen(["echo", "0"], stdout=PIPE)
    111              #p2 = Popen(["nc", "-w", "4", collector.hostname, "22"],
    112              #    stdin=p1.stdout, stdout=PIPE, stderr=PIPE)
    113
    114              #if os.waitpid(p2.pid, 0)[1] != 0:
    115              #    log.warn("Unable to SSH to collector %s (%s)",
    116              #             collector.id, collector.hostname)
    117              #    return
    118
    119              cmd = self.args[0]
    120              if self.options.useprefix:
    121                  cmd = '%s_%s' % (collector.id, cmd)
    122              collectorCommand = ['ssh', collector.hostname, cmd]
    123
    124          collectorCommand = ' '.join(collectorCommand)
    125          log.debug("Runing command '%s' on collector %s (%s)",
    126                    collectorCommand, collector.id, collector.hostname)
    127          proc = Popen(collectorCommand, stdout=PIPE, stderr=PIPE, shell=True)
    128          signal.signal(signal.SIGALRM, killTimedOutProc)
    129          signal.alarm(self.options.timeout)
    130          collector.stdout, collector.stderr = proc.communicate()
    131          proc.wait()
    132          signal.alarm(0) # Disable the alarm
    133
    134
    135  if __name__ == '__main__':
    136      zrc = RunCommand()
    137      zrc.run()
    138