├── requirements.txt ├── LICENSE ├── README.md └── check-consul-health.py /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.6.0 2 | docopt==0.6.1 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Zsolt Takács 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## What is this? 2 | 3 | This is a consul check integration plugin for nagios that makes easy to add nagios commands that queries and aggregates specific checks or services by node from a consul agent. 4 | 5 | Returns 2 if there are any critical checks, 1 if there are no criticals but warnings. Returns 3 when node not found or when there is no matches based on the given filters (CheckID, ServiceName). Returns 0 on passing checks. 6 | 7 | ## Example 8 | 9 | Query the local consul agent for the serf health check of node named consul01 in datacenter dc01: 10 | ``` 11 | $ python check-consul-health.py node consul01 dc01 --CheckID=serfHealth 12 | Passing: 1 13 | > consul01::Serf Health Status:serfHealth:passing 14 | ``` 15 | 16 | Query for a specific service: 17 | ``` 18 | $ python check-consul-health.py node consul01 dc01 --ServiceName=raft 19 | Passing: 2 20 | > consul01:raft:Leader elected & operational:leader:passing 21 | > consul01:raft:Peer list match:peers:passing 22 | ``` 23 | 24 | ## Install dependencies with pip 25 | 26 | ``` 27 | pip install -r requirements.txt 28 | ``` 29 | 30 | ## Usage 31 | 32 | ``` 33 | $ python check-consul-health.py -h 34 | Usage: 35 | check-consul-health.py node NODE DC 36 | [--addr=ADDR] 37 | [--CheckID=CheckID | --ServiceName=ServiceName] 38 | [--verbose] 39 | 40 | Arguments: 41 | NODE the consul node_name 42 | DC the consul datacenter 43 | 44 | Options: 45 | -h --help show this 46 | -v --verbose verbose output 47 | --addr=ADDR consul address [default: http://localhost:8500] 48 | --CheckID=CheckID CheckID matcher 49 | --ServiceName=ServiceName ServiceName matcher 50 | ``` 51 | 52 | -------------------------------------------------------------------------------- /check-consul-health.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """Usage: 3 | check-consul-health.py node NODE DC 4 | [--addr=ADDR] 5 | [--CheckID=CheckID | --ServiceName=ServiceName] 6 | [--verbose] 7 | 8 | Arguments: 9 | NODE the consul node_name 10 | DC the consul datacenter 11 | 12 | Options: 13 | -h --help show this 14 | -v --verbose verbose output 15 | --addr=ADDR consul address [default: http://localhost:8500] 16 | --CheckID=CheckID CheckID matcher 17 | --ServiceName=ServiceName ServiceName matcher 18 | """ 19 | 20 | from docopt import docopt 21 | import requests, json, traceback, exceptions 22 | 23 | def dump(it): 24 | if arguments['--verbose']: print it 25 | 26 | def buildNodeUrl(): 27 | url = "%(--addr)s/v1/health/node/%(NODE)s?dc=%(DC)s" % arguments 28 | dump("Url: " + url) 29 | return url 30 | 31 | def getJsonFromUrl(url): 32 | r = requests.get(url) 33 | dump("Response: " + r.text) 34 | dump("Status code: " + str(r.status_code)) 35 | r.raise_for_status() 36 | return r.json() 37 | 38 | def printCheck(check): 39 | print "> %(Node)s:%(ServiceName)s:%(Name)s:%(CheckID)s:%(Status)s" % check 40 | 41 | def processFailing(checks): 42 | filters = map(lambda field: \ 43 | lambda x: arguments['--' + field] is None or x[field] == arguments['--'+field], 44 | ['CheckID', 'ServiceName'] 45 | ) 46 | 47 | filtered = filter(lambda x: all(f(x) for f in filters), checks) 48 | passing = filter(lambda x: x['Status'] == 'passing', filtered) 49 | warning = filter(lambda x: x['Status'] == 'warning', filtered) 50 | critical = filter(lambda x: x['Status'] == 'critical', filtered) 51 | 52 | if len(checks) == 0: 53 | print "There is no matching node!" 54 | return 1 55 | 56 | if len(filtered) == 0: 57 | print "There is no matching check!" 58 | return 1 59 | 60 | checkOutput = lambda x: x["Name"] + ":" + x["Output"] 61 | 62 | if len(critical): 63 | print "|".join(map(checkOutput, critical)) 64 | for check in critical: 65 | printCheck(check) 66 | if len(warning): 67 | print "|".join(map(checkOutput, warning)) 68 | for check in warning: 69 | printCheck(check) 70 | if len(passing): 71 | print "Passing: %d" % (len(passing)) 72 | for check in passing: 73 | printCheck(check) 74 | 75 | return 2 if len(critical) else 1 if len(warning) else 0 76 | 77 | if __name__ == '__main__': 78 | try: 79 | arguments = docopt(__doc__) 80 | dump("Arguments: " + str(arguments)) 81 | if arguments['node']: 82 | url = buildNodeUrl() 83 | json = getJsonFromUrl(url) 84 | exit(processFailing(json)) 85 | except exceptions.SystemExit: raise 86 | except: 87 | traceback.print_exc() 88 | exit(3) 89 | --------------------------------------------------------------------------------