Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 00e6fb0d16
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

executable file 774 lines (625 sloc) 28.197 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
#!/usr/bin/env python

'''nagios-api - a REST-like, JSON API for Nagios

This provides a simple REST interface to Nagios3. See the README for
more information about this software.

Copyright 2011 by Bump Technologies, Inc and other authors and
contributors. See the LICENSE file for full licensing information.

'''


import datetime
import os
import re
import sys
import time
import types
import atexit
from optparse import OptionParser
from diesel import Application, Loop, Service, log as glog, sleep
from diesel.util.lock import synchronized
from diesel.protocols import http
from json import loads, dumps
from nagios import Nagios

LOG = None
CMDFILE = None
CMD_ENABLED = False
LOG_ENABLED = False
URL_REGEX = re.compile(r'^/(\w+)(?:/([\w\d\.\-]+)?)?$')
NAGIOS = None
NLOG = []
NLOGLINES = 0
ALLOW_ORIGIN = None
PIDFILE = "/var/run/nagios-api.pid"


def _send_json(req, success, content):
    '''Internal JSON sender.

'''
    global ALLOW_ORIGIN
    headers = http.HttpHeaders()
    out = dumps({ 'success': success, 'content': content }, ensure_ascii=False)
    headers.add('Content-Length', len(out))
    headers.add('Content-Type', 'application/json')
    if ALLOW_ORIGIN is not None:
        headers.add('Access-Control-Allow-Origin', ALLOW_ORIGIN)
    return http.http_response(req, 200, headers, out)


def json_error(req, msg):
    '''Return an error message to the caller.

'''
    return _send_json(req, False, msg)


def json_response(req, msg):
    '''Return an error message to the caller.

'''
    return _send_json(req, True, msg)


def http_state(req, objid, reqobj):
    '''Get a host->service mapping and return the basic state. This is
the method to use to have status scripts or web interfaces that
contain basic overview information.

This should return everything you need to show the status of the
world.

'''
    global NAGIOS
    if objid is not None:
        return json_error(req, 'Unexpected object ID.')
    return json_response(req, NAGIOS.for_json())


def http_objects(req, objid, reqobj):
    '''Get a host->service mapping. Does not return any data about what
these objects are. This is to be used to efficiently get a list of
everything that exists in the world.

'''
    global NAGIOS
    if objid is not None:
        return json_error(req, 'Unexpected object ID.')
    ret = {}
    for host in NAGIOS.hosts:
        ret[host] = []
        if host in NAGIOS.services:
            for svc in NAGIOS.services[host]:
                ret[host].append(svc)
    return json_response(req, ret)


def http_host(req, objid, reqobj):
    '''Get a view of a single host.
Shows all of the variables for the host object.
'''
    global NAGIOS
    if not objid:
        return json_error(req, 'No hostname provided.')
    elif objid not in NAGIOS.hosts.keys():
        return json_error(req, 'Unknown hostname.')
    ret = {}
    host_keys = [ x for x in dir(NAGIOS.hosts[objid]) if not x.startswith('__') ]
    for host_key in host_keys:
        value = getattr(NAGIOS.hosts[objid], host_key)
        if type(value) == types.StringType:
            ret[host_key] = value
    if objid in NAGIOS.services:
        ret['services'] = []
        for svc in NAGIOS.services[objid]:
            ret['services'].append(svc)
    return json_response(req, ret)


def http_service(req, objid, reqobj):
    '''Get a view of a single service on one host.
Shows all of the variables for the host object.
'''
    global NAGIOS
    if not objid:
        return json_error(req, 'No hostname provided.')
    elif objid not in NAGIOS.services.keys():
        return json_error(req, 'Unknown hostname.')
    ret = {}
    for service in NAGIOS.services[objid]:
        ret[service] = {}
        service_keys = [ x for x in dir(NAGIOS.services[objid][service]) if not x.startswith('__') ]
        for service_key in service_keys:
            value = getattr(NAGIOS.services[objid][service], service_key)
            if type(value) == types.StringType:
                ret[service][service_key] = value
    return json_response(req, ret)


def http_log(req, objid, reqobj):
    '''Return the recent Nagios log entries. This is useful if you just
want to see what has happened recently. See the subscribe method if
you want to be notified when new log lines are added.

'''
    global NLOG, LOG_ENABLED
    if objid is not None:
        return json_error(req, 'Unexpected object ID.')
    if not LOG_ENABLED:
        return json_error(req, 'Log file parsing is not enabled on nagios-api.')
    return json_response(req, NLOG)


def http_schedule_downtime(req, objid, reqobj):
    '''Given a service or host, schedule downtime for it. The main mode
for this API is to schedule a hard downtime that starts now and ends
after so many seconds.

If you specify the host parameter and also set 'services_too' to
true, we will schedule downtime for the host and the services on it.

'''
    global NAGIOS, CMD_ENABLED
    if not CMD_ENABLED:
        return json_error(req, 'External commands not enabled on nagios-api.')
    if objid is not None:
        return json_error(req, 'Unexpected object ID.')

    host = reqobj['host'] if 'host' in reqobj else None
    service = reqobj['service'] if 'service' in reqobj else None
    author = reqobj['author'] if 'author' in reqobj else 'nagios-api'
    comment = reqobj['comment'] if 'comment' in reqobj else 'schedule downtime'
    services_too = reqobj['services_too'] if 'services_too' in reqobj else False
    try:
        dur = int(reqobj['duration']) if 'duration' in reqobj else 0
    except ValueError:
        dur = 0

    obj = NAGIOS.host_or_service(host, service)
    if obj is None:
        return json_error(req, 'Host or service not found.')
    if dur < 60 or dur > 86400 * 7: # Upper limit?
        return json_error(req, 'Downtime must be between 60 seconds and a week.')

    now = int(time.time())
    if obj.service is not None:
        if not send_nagios_command('SCHEDULE_SVC_DOWNTIME', host, service, now,
                                   now+dur, 1, 0, 0, author, comment):
            return json_error(req, 'Failed sending command to Nagios.')
    else:
        if not send_nagios_command('SCHEDULE_HOST_DOWNTIME', host, now, now+dur,
                                   1, 0, 0, author, comment):
            return json_error(req, 'Failed sending command to Nagios.')
        if services_too:
            if not send_nagios_command('SCHEDULE_HOST_SVC_DOWNTIME', host, now, now+dur,
                                       1, 0, 0, author, comment):
                return json_error(req, 'Failed sending command to Nagios.')
    return json_response(req, 'scheduled')


def http_cancel_downtime(req, objid, reqobj):
    '''Given a downtime_id in objid, cancel that downtime. Alternately,
this method will expect a host and/or service parameter and use that
to cancel any matching downtimes.

'''
    global NAGIOS, CMD_ENABLED
    if not CMD_ENABLED:
        return json_error(req, 'External commands not enabled on nagios-api.')

    def cancel_downtime(obj):
        if obj.service is not None:
            return send_nagios_command('DEL_SVC_DOWNTIME', dt.downtime_id)
        else:
            return send_nagios_command('DEL_HOST_DOWNTIME', dt.downtime_id)

    dts = []
    if objid is not None:
        if objid not in NAGIOS.downtimes:
            return json_error(req, 'Downtime ID does not seem valid.')
        dts += [NAGIOS.downtimes[objid]]
    else:
        host = reqobj['host'] if 'host' in reqobj else None
        service = reqobj['service'] if 'service' in reqobj else None
        services_too = reqobj['services_too'] if 'services_too' in reqobj else False
        obj = NAGIOS.host_or_service(host, service)
        if obj is None:
            return json_error(req, 'Failed to get host or service for downtime.')
        if obj.service is None and services_too:
            for svc in obj.services.itervalues():
                dts += svc.downtimes.values()
        dts += obj.downtimes.values()

    res = None
    for dt in dts:
        if res is None:
            res = True # So we know if we found any.
        res = cancel_downtime(dt) and res

    if res is not None:
        if res:
            return json_response(req, 'cancelled')
        else:
            return json_error(req, 'One or more cancels failed. Some may have succeeded.')
    else:
        return json_response(req, 'none found')


def http_submit_result(req, objid, reqobj):
    '''Submits a check result for a host or service. This is mostly used
for passive services where Nagios is not responsible for doing the
checks itself.

As with other APIs, you are required to supply a host parameter and
you may supply a service. The output parameter is an opaque string
that gets given to Nagios. Use the status parameter to specify an
integer return code.

Host status codes: 0 = UP, 1 = DOWN, 2 = UNREACHABLE.
Service status codes: 0 = OK, 1 = WARNING, 2 = CRITICAL, 3 = UNKNOWN.

'''
    global NAGIOS, CMD_ENABLED
    if not CMD_ENABLED:
        return json_error(req, 'External commands not enabled on nagios-api.')

    host = reqobj['host'] if 'host' in reqobj else None
    service = reqobj['service'] if 'service' in reqobj else None
    obj = NAGIOS.host_or_service(host, service)
    if obj is None:
        return json_error(req, 'Failed to find host or service to update.')

    if 'status' not in reqobj:
        return json_error(req, 'Required parameter "status" not found.')
    if 'output' not in reqobj:
        return json_error(req, 'Required parameter "output" not found.')

    try:
        status = int(reqobj['status'])
    except ValueError:
        return json_error(req, 'Invalid status provided.')

    if obj.service is not None:
        if not send_nagios_command('PROCESS_SERVICE_CHECK_RESULT', host,
                                   service, status, reqobj['output']):
            return json_error(req, 'Failed sending command to Nagios.')
    else:
        if not send_nagios_command('PROCESS_HOST_CHECK_RESULT', host, status,
                                   reqobj['output']):
            return json_error(req, 'Failed sending command to Nagios.')
    return json_response(req, 'submitted')


def http_acknowledge_problem(req, objid, reqobj):
    '''Submits an acknowledgement of a host or service problem. This is used
when you have an open issue, that you want to acknowledge to prevent it
from sending further alerts.

You are required to send at minimum a host parameter, and you can also
supply an optional service parameter. If no service is supplied, the
host will be acknowledged, and if the service parameter is supplied
only the specific service will be acknowledged.

Additionally, you are required to supply a comment parameter as well. This
is a text string that will be used to identify the reason for the
acknowledgement.

The following options are also available, but optional.

sticky (0/1): If set to 1, the acknowledgement will remain until the service
or host enters an OK state. If set to 0, it will clear the acknowledgement on
the first state change, ex. CRITICAL->WARNING. Defaults to 1 (on).

notify (0/1): If set to 0, no notification will be sent to the configure
contacts for the host or service. Defaults to 1 (on).

persistent (0/1): If set to 1, the comment will remain even after the service
or host problem has been resolved. Defaults to 0 (off).

author (string): This is the name displayed as the creator of the
acknowledgement. This will default to 'nagios-api'.
'''
    global NAGIOS, CMD_ENABLED
    if not CMD_ENABLED:
        return json_error(req, 'External commands not enabled on nagios-api.')

    host = reqobj['host'] if 'host' in reqobj else None
    service = reqobj['service'] if 'service' in reqobj else None
    comment = reqobj['comment'] if 'comment' in reqobj else None
    sticky = reqobj['sticky'] if 'sticky' in reqobj else 1
    notify = reqobj['notify'] if 'notify' in reqobj else 1
    persistent = reqobj['persistent'] if 'persistent' in reqobj else 0
    author = reqobj['author'] if 'author' in reqobj else 'nagios-api'

    obj = NAGIOS.host_or_service(host, service)
    if obj is None:
        return json_error(req, 'Failed to find host or service to update.')

    if not 'comment':
        return json_error(req, 'Required parameter "comment" not found.')

    try:
        sticky = int(sticky)
        notify = int(notify)
        persistent = int(persistent)
    except ValueError:
        return json_error(req, 'Invalid value provided for one or more of sticky, notify or persistent')

    if obj.service is not None:
        if not send_nagios_command('ACKNOWLEDGE_SVC_PROBLEM', host, service,
                                    sticky, notify, persistent, author, comment):
            return json_error(req, 'Failed sending command to Nagios.')
    else:
        if not send_nagios_command('ACKNOWLEDGE_HOST_PROBLEM', host, sticky,
                                    notify, persistent, author, comment):
            return json_error(req, 'Failed sending command to Nagios.')
    return json_response(req, 'submitted')


def http_remove_acknowledgement(req, objid, reqobj):
    '''Removes an acknowledgement from a host or service.

You are required to send at minimum a host parameter, and you can also
supply an optional service parameter. If no service is supplied, the
host will be acknowledged, and if the service parameter is supplied
only the specific service will be acknowledged.

'''
    global NAGIOS, CMD_ENABLED
    if not CMD_ENABLED:
        return json_error(req, 'External commands not enabled on nagios-api.')

    host = reqobj['host'] if 'host' in reqobj else None
    service = reqobj['service'] if 'service' in reqobj else None

    obj = NAGIOS.host_or_service(host, service)
    if obj is None:
        return json_error(req, 'Failed to find host or service to update.')

    if obj.service is not None:
        if not send_nagios_command('REMOVE_SVC_ACKNOWLEDGEMENT', host, service):
            return json_error(req, 'Failed sending command to Nagios.')
    else:
        if not send_nagios_command('REMOVE_HOST_ACKNOWLEDGEMENT', host):
            return json_error(req, 'Failed sending command to Nagios.')
    return json_response(req, 'submitted')


def http_add_comment(req, objid, reqobj):
    '''Adds a comment to a host or service.

You are required to send at minimum a host and comment parameter,
and you can also supply an optional service parameter. If the service
parameter is supplied, the comment will be added to the specific service
and if its omitted, the comment will be added to the host

The following options are also available, but optional.

persistent (0/1): If set to 1, the comment will remain until manually
deleted, if set to 0, it will automatically be purged at the next
restart of the Nagios process. Defaults to 0 (off)
or host problem has been resolved. Defaults to 0 (off).

author (string): This is the name displayed as the creator of the
comment. This will default to 'nagios-api'.
'''
    global NAGIOS, CMD_ENABLED
    if not CMD_ENABLED:
        return json_error(req, 'External commands not enabled on nagios-api.')

    host = reqobj['host'] if 'host' in reqobj else None
    service = reqobj['service'] if 'service' in reqobj else None
    comment = reqobj['comment'] if 'comment' in reqobj else None
    persistent = reqobj['persistent'] if 'persistent' in reqobj else 0
    author = reqobj['author'] if 'author' in reqobj else 'nagios-api'

    obj = NAGIOS.host_or_service(host, service)
    if obj is None:
        return json_error(req, 'Failed to find host or service to update.')

    if not comment:
        return json_error(req, 'Required parameter "comment" not found.')

    try:
        persistent = int(persistent)
    except ValueError:
        return json_error(req, 'Invalid value provided for persistent')

    if obj.service is not None:
        if not send_nagios_command('ADD_SVC_COMMENT', host, service,
                                    persistent, author, comment):
            return json_error(req, 'Failed sending command to Nagios.')
    else:
        if not send_nagios_command('ADD_HOST_COMMENT', host,
                                    persistent, author, comment):
            return json_error(req, 'Failed sending command to Nagios.')
    return json_response(req, 'submitted')


def http_delete_comment(req, objid, reqobj):
    '''Deletes one or all comments for a host or service.

You are required to supply atleast the host parameter along
with comment_id, detailing the comment to delete. You can also
supply the service parameter to delete comments from a specific
service.

If comment_id is set to -1, all comments for the host or service
will be deleted.

'''
    global NAGIOS, CMD_ENABLED
    if not CMD_ENABLED:
        return json_error(req, 'External commands not enabled on nagios-api.')

    host = reqobj['host'] if 'host' in reqobj else None
    service = reqobj['service'] if 'service' in reqobj else None
    comment_id = reqobj['comment_id'] if 'comment_id' in reqobj else None

    obj = NAGIOS.host_or_service(host, service)
    if obj is None:
        return json_error(req, 'Failed to find host or service to update.')

    if not 'comment_id':
        return json_error(req, 'Required parameter "comment" not found.')

    try:
        comment_id = int(comment_id)
    except ValueError:
        return json_error(req, 'Invalid value provided for comment_id')

    if obj.service is not None:
        if comment_id == -1:
            if not send_nagios_command('DEL_ALL_SVC_COMMENTS', host, service):
                return json_error(req, 'Failed sending command to Nagios.')
        else:
            if not send_nagios_command('DEL_SVC_COMMENT', comment_id):
                return json_error(req, 'Failed sending command to Nagios.')
    else:
        if comment_id == -1:
            if not send_nagios_command('DEL_ALL_HOST_COMMENTS', host):
                return json_error(req, 'Failed sending command to Nagios.')
        else:
            if not send_nagios_command('DEL_HOST_COMMENT', comment_id):
                return json_error(req, 'Failed sending command to Nagios.')
    return json_response(req, 'submitted')


def http_schedule_check(req, objid, reqobj):
    '''Schedules a check for a host or service.

Requires the host parameter to be sent, and also accepts
a service parameter. If the service parameter is set to
*, all check will be scheduled for all services on the host.

Scheduling a non-forced check, does not mean the check will
occur. If active checks have been disabled either on a
program-wide or service level, or the services are already
scheduled to be checked sooner than your requested time,
the check will not be performed, unless forced mode is
enabled (see below).

The following optional parameters are also available:

check_time (int): The time at which to schedule the check,
in time_t (UNIX timestamp) format. Defaults to current time.

forced (0/1): If set to 1, the scheduled check will be
marked as forced, meaning it will be performed, even if
there has been other checks run on the host or service
in the mean time. Defaults to 0 (off)

all_services (0/1): If set to 1, all services for the
specified host will be checked. Enabling this option, will
cause the service parameter to be ignored, if supplied.
Defaults to 0 (off)
'''
    from time import time
    global NAGIOS, CMD_ENABLED
    if not CMD_ENABLED:
        return json_error(req, 'External commands not enabled on nagios-api.')

    host = reqobj['host'] if 'host' in reqobj else None
    service = reqobj['service'] if 'service' in reqobj else None
    check_time = reqobj['check_time'] if 'check_time' in reqobj else time()
    forced = reqobj['forced'] if 'forced' in reqobj else 0
    all_services = reqobj['all_services'] if 'all_services' in reqobj else 0

    obj = NAGIOS.host_or_service(host, service)
    if obj is None:
        return json_error(req, 'Failed to find host or service to update.')

    try:
        check_time = int(check_time)
        forced = int(forced)
        all_services = int(all_services)
    except ValueError:
        return json_error(req, 'Invalid value provided for check_time, all_services or forced')

    if obj.service is not None:
        if forced:
            if not send_nagios_command('SCHEDULE_FORCED_SVC_CHECK', host, service, check_time):
                return json_error(req, 'Failed sending command to Nagios.')
        else:
            if not send_nagios_command('SCHEDULE_SVC_CHECK', host, service, check_time):
                return json_error(req, 'Failed sending command to Nagios.')
    else:
        if forced:
            if not send_nagios_command('SCHEDULE_FORCED_HOST_CHECK', host, check_time):
                return json_error(req, 'Failed sending command to Nagios.')
        else:
            if not send_nagios_command('SCHEDULE_HOST_CHECK', host, check_time):
                return json_error(req, 'Failed sending command to Nagios.')

        if all_services:
            if forced:
                if not send_nagios_command('SCHEDULE_FORCED_HOST_SVC_CHECKS', host, check_time):
                    return json_error(req, 'Failed sending command to Nagios.')
            else:
                if not send_nagios_command('SCHEDULE_HOST_SVC_CHECKS', host, check_time):
                    return json_error(req, 'Failed sending command to Nagios.')

    return json_response(req, 'submitted')


def http_handler(req):
    '''Handle an incoming HTTP request.

'''
    # All requests should follow this very simple format.
    global URL_REGEX
    res = URL_REGEX.match(req.url)
    if res is None:
        return json_error(req, 'Invalid request URI')
    verb, objid = res.group(1), res.group(2)
    try:
        objid = int(objid)
    except ValueError:
        pass
    except TypeError:
        objid = None

    # If it's a POST, try to extract a JSON object from the body.
    try:
        reqobj = loads(req.body) if req.method == 'POST' else None
    except ValueError:
        return json_error(req, 'Invalid JSON body')
    if reqobj is not None and type(reqobj) is not dict:
        return json_error(req, 'JSON body must be a dict')

    # Dispatch table goes here
    dispatch = {
        'GET': {
            'log': http_log,
            'state': http_state,
            'objects': http_objects,
            'host': http_host,
            'service': http_service,
        },
        'POST': {
            'cancel_downtime': http_cancel_downtime,
            'schedule_downtime': http_schedule_downtime,
            'submit_result': http_submit_result,
            'acknowledge_problem': http_acknowledge_problem,
            'remove_acknowledgement': http_remove_acknowledgement,
            'add_comment': http_add_comment,
            'delete_comment': http_delete_comment,
            'schedule_check': http_schedule_check,
        }
    }

    if req.method not in dispatch:
        return json_error(req, 'Method %s not supported' % req.method)
    if verb not in dispatch[req.method]:
        return json_error(req, 'Verb %s (method %s) not supported' % (verb, req.method))
    return dispatch[req.method][verb](req, objid, reqobj)


def send_nagios_command(*args):
    '''Send a simple command to our local Nagios server.

'''
    global CMDFILE, CMD_ENABLED
    if not CMD_ENABLED:
        return False # May not be enabled.
    if len(args) < 2:
        return False
    arg = '[%d] ' % int(time.time()) + ';'.join(unicode(j) for j in args)
    arg = arg.encode('latin1')
    with synchronized():
        info('Sending command: %s' % arg)
        with open(CMDFILE, 'w') as pipe:
            pipe.write(arg + '\n')
    return True


def read_status(statusfile):
    '''Monitor the Nagios status file and update our global data store
with the data as it changes.

'''
    global NAGIOS
    mtime = None
    while True:
        stat = os.stat(statusfile)
        if mtime is None or stat.st_mtime > mtime:
            NAGIOS = Nagios(statusfile)
            mtime = stat.st_mtime
        sleep(1)


def read_log(logfile):
    '''This function reads the Nagios log file and parses events. This
allows us to provide a pubsub style interface so people can get
real-time updates from the Nagios system.

'''
    global NLOG, NLOGLINES
    f = open(logfile, 'r')
    while True:
        loc = f.tell()
        line = f.readline()
        if not line:
            sleep(1)
            f.seek(loc)
        else:
            NLOGLINES += 1
            NLOG.append(line.strip())
            NLOG = NLOG[-1000:] # Keep the most recent 1k lines.
    f.close() # Useless?


def main(argv):
    '''A simple REST API for Nagios3.

'''
    global LOG, CMDFILE, CMD_ENABLED, LOG_ENABLED, ALLOW_ORIGIN
    app = Application()

    parser = OptionParser(description='Give Nagios a REST API.')
    parser.add_option('-o', '--allow-origin', dest='alloworigin', metavar='ORIGIN',
            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.')
    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.')
    parser.add_option('-l', '--log-file', dest='logfile', metavar='FILE',
            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.')
    parser.add_option('-p', '--port', dest='port', metavar='PORT', type='int',
            default=6315, help='The port to listen for requests on.')
    parser.add_option('-q', '--quiet', dest='quiet', action='store_true',
            help='Quiet mode')
    (options, args) = parser.parse_args(args=argv[1:])

    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 = glog.sublog('nagios-api', glog.warning)
    else:
        LOG = glog.sublog('nagios-api', glog.info)

    if os.path.exists(options.commandfile):
        CMD_ENABLED = True
        CMDFILE = options.commandfile
    if options.alloworigin:
        ALLOW_ORIGIN = options.alloworigin

    info('Listening on port %d, starting to rock and roll!' % options.port)
    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 critical(msg):
    '''Handle error messaging.

'''
    global LOG
    LOG.critical(msg)
    return 1


def info(msg):
    '''Print an informative logging message.

'''
    global LOG
    LOG.info(msg)
    return 0


def warn(msg):
    '''Print a warning message.

'''
    global LOG
    LOG.warn(msg)
    return 0

def _exitfunc():
    print "Exiting. Cleaning up PID."
    os.unlink(PIDFILE)

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))
Something went wrong with that request. Please try again.