Skip to content

Commit

Permalink
Daemon function added to script
Browse files Browse the repository at this point in the history
  • Loading branch information
Alik Kurdyukov committed Nov 25, 2012
1 parent 2ce26e3 commit 5526644
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 39 deletions.
31 changes: 28 additions & 3 deletions README.asciidoc
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
-------
Expand Down Expand Up @@ -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
---
Expand Down
154 changes: 119 additions & 35 deletions nagios-api
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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)
3 changes: 2 additions & 1 deletion setup.py
Expand Up @@ -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.