(file) Return to openvps-mon CVS log (file) (dir) Up to [Development] / openvps-host / scripts

File: [Development] / openvps-host / scripts / openvps-mon (download)
Revision: 1.7, Thu Sep 29 20:09:52 2005 UTC (4 years, 11 months ago) by grisha
Branch: MAIN
CVS Tags: HEAD
Changes since 1.6: +9 -7 lines
added live_system

#!/usr/bin/env python

#
# Copyright 2005 OpenHosting, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# $Id: openvps-mon,v 1.7 2005/09/29 21:09:52 grisha Exp $

# This is a daemon script that runs in the background, collects
# datapoints (using openvps.host.mon) and sends them out to a central
# server in a hmac signed udp packet at constant intervals

# the daemonization stuff is from recipe 66012 on ASPN with some
# enhancements

import os
import sys
import signal
import time
import socket
import traceback
from signal import SIGTERM, SIGKILL
from syslog import syslog, openlog, closelog

# openvps modules
from openvps.host import mon, vsmon, cfg


def log(data):
   openlog('openvps-mon')
   syslog('%s' % data)
   closelog()

EXIT = 0

def exit_handler(signum, frame):
   global EXIT

   # ok so here we need to send an 'administrative down' packet to the
   # recv agent.

   log('Received SIGTERM.')
   EXIT = 1

   # send admin_down command to mon server. upon receiving this, the
   # server should disable the check for 'gone-silent' device. any
   # further packet from us would reenable it
   
   data = mon.cmd_admin_down()
   send_data(data)
   
                   
def daemonize(pidfile=None):
   """
      Detach a process from the controlling terminal and run it in the
      background as a daemon.
   """

   log('Starting...')

   # make sure we can write the pid file
   if pidfile:
      file(pidfile,'w+').write("\n")
      os.unlink(pidfile)
   
   try:
      # Fork a child process so the parent can exit.
      pid = os.fork()
   except OSError, e:
      log('Could not start: %s %s' % (e.errno, e.strerror))
      sys.exit(1)
   
   if (pid == 0):       # The first child.
      
      # Next we call os.setsid() to become the session leader of this new
      # session.
      os.setsid()

      # When the first child terminates, all processes in the second child
      # are sent a SIGHUP, so it's ignored.
      signal.signal(signal.SIGHUP, signal.SIG_IGN)
      
      try:
         # Fork a second child to prevent zombies.
         pid = os.fork()        # Fork a second child.
      except OSError, e:
         log('Could not start: %s %s' % (e.errno, e.strerror))
      
      if (pid == 0):      # The second child.
         # Ensure that the daemon doesn't keep any directory in use.
         os.chdir("/")
         # Give the child complete control over permissions.
         os.umask(0)
      else:
         os._exit(0)      # Exit parent (the first child) of the second child.
   else:
      os._exit(0)         # Exit parent of the first child.

   # Close all open files.
   try:
      maxfd = os.sysconf("SC_OPEN_MAX")
   except (AttributeError, ValueError):
      maxfd = 256       # default maximum

   for fd in range(0, maxfd):
      try:
         os.close(fd)
      except OSError:   # ERROR (ignore)
         pass

   # Redirect the standard file descriptors to /dev/null.
   os.open("/dev/null", os.O_RDONLY) 
   os.open("/dev/null", os.O_RDWR)
   os.open("/dev/null", os.O_RDWR)

   # register signal handler
   signal.signal(SIGTERM, exit_handler)

   # write our pid file
   if pidfile:
      file(pidfile,'w+').write("%s\n" % os.getpid())

def startstop(pidfile='%s.pid' % os.path.basename(sys.argv[0])):

   # turn pid into full path
   pidfile = os.path.abspath(pidfile)
   
   if len(sys.argv) > 1:
      
      action = sys.argv[1]

      # is there a pid file?
      try:
         pf  = file(pidfile,'r')
         pid = int(pf.read().strip())
         pf.close()
      except IOError:
         pid = None
         
      if 'stop' == action or 'restart' == action:

         # start or restart without pid
         if not pid:

            print >> sys.stderr, "Could not stop, pid file '%s' missing." % pidfile

            if 'stop' == action:
               # we're done
               sys.exit(1)
               
            action = 'start'
            pid = None
            
         else:

            # we have a pid file
            try:

               # keep on killing for X seconds
               X = 5
               for x in xrange(X):
                  os.kill(pid, SIGTERM)
                  time.sleep(1)

               # now really kill
               print >> sys.stderr, "%d SIGTERMs did not kill it, switching to SIGKILL" % X
               for x in xrange(3):
                  os.kill(pid, SIGKILL)
                  time.sleep(1)

               print >> sys.stderr, "Process still running, we give up!"
               sys.exit(1)
                  
            except OSError, err:
               err = str(err)
               if err.find("No such process") > 0:
                  # kill succeeded
                  os.remove(pidfile)
                  if 'stop' == action:
                     sys.exit(0)
                  action = 'start'
                  pid = None

               else:
                  # some other error?
                  print >> sys.stderr, str(err)
                  sys.exit(1)
                  
      if 'start' == action:
         if pid:
            print >> sys.stderr, "NOT starting because pid file '%s' exists." % pidfile
            sys.exit(1)
            
         daemonize(pidfile=pidfile)
         log('Started.')
         return

   print "usage: %s start|stop|restart" % sys.argv[0]
   sys.exit(2)


def send_data(data):

   if cfg.LIVE_SYSTEM:

      udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

      if type(cfg.MON_TARGET_IP) == type([]):
         for target_ip in cfg.MON_TARGET_IP:
            udp_sock.sendto(data, (target_ip, cfg.MON_TARGET_PORT))
      else:
         udp_sock.sendto(data, (cfg.MON_TARGET_IP, cfg.MON_TARGET_PORT))


def work():

   try:
      do_work()
   except:
      etype, evalue, etb = sys.exc_info()

      # write to log
      for e in traceback.format_exception(etype, evalue, etb):
         log(e[:-1])
      
   log('Exiting...')
   sys.exit(1)

def do_work():

   # work starts here

   global EXIT
   
   # first time do it right away

   # send to mon server
   data = mon.collect_stats()
   send_data(data)

   # local vserver stats
   vsmon.collect_stats()

   # enter the loop
   # loop every second, but only do anything once per N loops

   N, n = 60, 0
   
   while 1:

      if EXIT:
         return

      if n < N:
         n += 1
         time.sleep(1)
         continue

      # send to mon server
      data = mon.collect_stats()
      send_data(data)

      # local vserver stats
      vsmon.collect_stats()

      n = 0

   return

if __name__ == "__main__":

   try:
      startstop(pidfile=cfg.MON_PID_FILE)
   except IOError:
      etype, evalue, etb = sys.exc_info()

      # write to log
      for e in traceback.format_exception(etype, evalue, etb):
         log(e[:-1])
      
      log('Exiting...')
      sys.exit(1)

   # start working
   work()
   
      
###
# do not edit this if you like using emacs
# makes emacs go into python mode
### Local Variables:
### mode:python
### End:

webmaster@dev.openhosting.com
Powered by
ViewCVS 0.9.2