Permalink
Browse files

Daemon function added to script

  • Loading branch information...
1 parent 2ce26e3 commit 55266446f15e11e80f9b995cd0ecd601af97a17c Alik Kurdyukov committed Nov 25, 2012
Showing with 149 additions and 39 deletions.
  1. +28 −3 README.asciidoc
  2. +119 −35 nagios-api
  3. +2 −1 setup.py
View
31 README.asciidoc
@@ -10,12 +10,12 @@ nagios-api - presents a REST-like JSON interface to Nagios
SYNOPSIS
--------
-*nagios-api* ['OPTIONS']
+*nagios-api* ['OPTIONS'] <Action>
DEPENDENCIES
------------
-Dependencies include: diesel, greenlet and python-openssl bindings
+Dependencies include: python-daemon, diesel, greenlet and python-openssl bindings
DESCRIPTION
@@ -32,7 +32,7 @@ USAGE
Usage is pretty easy:
nagios-api -p 8080 -c /var/lib/nagios3/rw/nagios.cmd \
- -s /var/cache/nagios3/status.dat -l /var/log/nagios3/nagios.log
+ -s /var/cache/nagios3/status.dat -l /var/log/nagios3/nagios.log console
You must at least provide the status file options. If you don't provide
the other options, then we will disable that functionality and error to
@@ -74,6 +74,16 @@ so you should be able to get all you need:
The original raw JSON mode is still supported by passing the --raw
option.
+ACTIONS
+-------
+*console*::
+ Run Nagios-API as console application
+
+*start*::
+ Start Nagios API as daemon
+
+*stop*::
+ Stop Nagios API daemon
OPTIONS
-------
@@ -112,6 +122,21 @@ http://www.w3.org/TR/cors/[CORS specification].
If present, we will only print warning/critical messages. Useful if
you are running this in the background.
+*-u, --user*::
+ If present, daemon will run as given user.
+
+*-w, --writelog*::
+ If present, application will write logs into given 'FILE'. Otherwise,
+ console is used.
+
+TROUBLESHOOTING
+---------------
+
+In daemon mode PID file /var/run/nagios/nagios-api.pid should be writable
+by daemon user set in -u option.
+
+If case of daemon starting problems consult /var/log/nagios-api-daemon.log
+file. If should contain start/stop errors.
API
---
View
154 nagios-api
@@ -20,12 +20,19 @@ import types
import atexit
from optparse import OptionParser
from diesel import Application, Loop, Service, sleep
-from diesel.logmod import log, levels
+from diesel.logmod import diesel_format
+import twiggy
from diesel.util.lock import synchronized
from diesel.protocols import http
from json import dumps
from nagios import Nagios
from werkzeug.exceptions import BadRequest
+import daemon
+import lockfile
+from pwd import getpwnam
+import errno
+import signal
+from lockfile.pidlockfile import PIDLockFile
CMDFILE = None
CMD_ENABLED = False
@@ -35,9 +42,24 @@ NAGIOS = None
NLOG = []
NLOGLINES = 0
ALLOW_ORIGIN = None
-PIDFILE = "/var/run/nagios-api.pid"
+PIDFILE = "/var/run/nagios/nagios-api.pid"
+DAEMON_LOG="/var/log/nagios-api-daemon.log"
+## init console logging
+diesel_output = twiggy.outputs.StreamOutput(diesel_format)
+def set_log_level(level=twiggy.levels.INFO):
+ twiggy.emitters.clear()
+
+ twiggy.addEmitters(
+ ('*', level, None, diesel_output)
+ )
+
+log = twiggy.log.name("diesel")
+
+set_log_level()
+
+## main code goes below
def _send_json(req, success, content):
'''Internal JSON sender.
@@ -755,73 +777,135 @@ def read_log(logfile):
NLOG = NLOG[-1000:] # Keep the most recent 1k lines.
f.close() # Useless?
+def touch(fname, times=None):
+ with file(fname, 'a'):
+ os.utime(fname, times)
-def main(argv):
- '''A simple REST API for Nagios3.
-
+def parse_args(argv):
+ '''Parse arguments and set global variables. Returns tuple (options, args)
'''
global CMDFILE, CMD_ENABLED, LOG_ENABLED, ALLOW_ORIGIN
- app = Application()
- parser = OptionParser(description='Give Nagios a REST API.')
+ usage = "usage: %prog [options] <start/stop/console>"
+ parser = OptionParser(description='Give Nagios a REST API.', usage = usage)
parser.add_option('-o', '--allow-origin', dest='alloworigin', metavar='ORIGIN',
- help='Access-Control-Allow-Origin header contents')
+ help='Access-Control-Allow-Origin header contents')
parser.add_option('-s', '--status-file', dest='statusfile', metavar='FILE',
- default='/var/cache/nagios3/status.dat', help='The file that contains '
- 'the Nagios status.')
+ default='/var/cache/nagios3/status.dat', help='The file that contains '
+ 'the Nagios status.')
parser.add_option('-c', '--command-file', dest='commandfile', metavar='FILE',
- default='/var/lib/nagios3/rw/nagios.cmd', help='The file to write '
- 'Nagios commands to.')
+ default='/var/lib/nagios3/rw/nagios.cmd', help='The file to write '
+ 'Nagios commands to.')
parser.add_option('-l', '--log-file', dest='logfile', metavar='FILE',
- default='/var/log/nagios3/nagios.log', help='The file Nagios writes '
- 'log events to.')
+ default='/var/log/nagios3/nagios.log', help='The file Nagios writes '
+ 'log events to.')
parser.add_option('-b', '--bind', dest='bind_addr', metavar='ADDR',
- default='', help='The address to listen for requests on.')
+ default='', help='The address to listen for requests on.')
parser.add_option('-p', '--port', dest='port', metavar='PORT', type='int',
- default=6315, help='The port to listen for requests on.')
+ default=6315, help='The port to listen for requests on.')
parser.add_option('-q', '--quiet', dest='quiet', action='store_true',
- help='Quiet mode')
+ help='Quiet mode')
+ parser.add_option('-u', '--user', dest='user', metavar='USER',
+ help='User to run daemon as')
+ parser.add_option('-w', '--write-log', dest='writelog', metavar='FILE',
+ help='The file deamon will write logs to')
(options, args) = parser.parse_args(args=argv[1:])
+ if len(args) != 1:
+ parser.error('Action should be set for this script')
+ if args[0] not in ('console', 'stop', 'start'):
+ parser.error("Unknown action '%s'. Action should be 'console', 'start' or 'stop'" % action)
+
if not os.path.isfile(options.statusfile):
parser.error('Status file not found: %s' % options.statusfile)
if options.port < 0 or options.port > 65535:
parser.error('Port must be in the range 1..65535.')
if options.quiet:
- log.min_level = levels.WARNING
+ log.min_level = twiggy.levels.WARNING
if os.path.exists(options.commandfile):
CMD_ENABLED = True
CMDFILE = options.commandfile
if options.alloworigin:
ALLOW_ORIGIN = options.alloworigin
+ if os.path.isfile(options.logfile):
+ LOG_ENABLED = True
+
+ if options.user is not None:
+ try:
+ pw = getpwnam(options.user)
+ options.uid = pw.pw_uid
+ gid = pw.pw_gid
+
+ if options.writelog is not None:
+ ## create log file and set ownership
+ touch(options.writelog)
+ os.chown(options.writelog, options.uid, gid)
+
+ except KeyError:
+ parser.error("Unknown local user '%s'" % options.user)
+ else:
+ options.uid = None
+
+ return (options, args)
+
+def main(options):
+ '''A simple REST API for Nagios3.
+
+ '''
+
+ if options.writelog is not None:
+ global diesel_output
+ diesel_output = twiggy.outputs.FileOutput(options.writelog, diesel_format)
+ set_log_level()
+
log.info('Listening on port %d, starting to rock and roll!' % options.port)
+ app = Application()
app.add_service(Service(http.HttpServer(http_handler), options.port,
options.bind_addr))
app.add_loop(Loop(read_status, options.statusfile), keep_alive=True)
if os.path.isfile(options.logfile):
- LOG_ENABLED = True
app.add_loop(Loop(read_log, options.logfile), keep_alive=True)
app.run()
return 1
-
-def _exitfunc():
- print "Exiting. Cleaning up PID."
- os.unlink(PIDFILE)
-
+def stop_by_pidfile(pidfile):
+ str = pidfile.readline(100)
+ if str == "":
+ return None
+ try:
+ n = int(str)
+ except ValueError:
+ return None
+ print "Stopping process %d" % n
+ try:
+ os.kill(n, signal.SIGTERM)
+ except OSError, (code, message):
+ if code != errno.ESRCH:
+ raise
if __name__ == '__main__':
- pid = str(os.getpid())
-
- if os.path.isfile(PIDFILE):
- print "%s already exists, exiting" % PIDFILE
- sys.exit()
- else:
- file(PIDFILE, 'w').write(pid)
-
- atexit.register(_exitfunc)
-
- sys.exit(main(sys.argv))
+ (options, args) = parse_args(sys.argv)
+
+ action = args[0]
+ if action == 'console':
+ sys.exit(main(options))
+ elif action == 'stop':
+ if not os.path.exists(PIDFILE):
+ ## pid not found - nothing to do
+ sys.exit(-1)
+ stop_by_pidfile(file(PIDFILE))
+ elif action == 'start':
+ context = daemon.DaemonContext(pidfile = PIDLockFile(PIDFILE))
+ if options.uid is not None:
+ context.uid = options.uid
+
+ if options.writelog is not None:
+ daemon_log = file(DAEMON_LOG, 'a')
+ context.stdout = daemon_log
+ context.stderr = daemon_log
+
+ with context:
+ main(options)
View
3 setup.py
@@ -12,6 +12,7 @@
requires=[
'diesel(>=3.0)',
'greenlet(==0.3.4)',
- 'requests'
+ 'requests',
+ 'pythondaemon(>=1.5.5)'
]
)

0 comments on commit 5526644

Please sign in to comment.