]>
Commit | Line | Data |
---|---|---|
1 | #! /usr/bin/env python | |
2 | ||
3 | ############################################################################### | |
4 | # Nagios plugin check_actuator_health | |
5 | # | |
6 | # Notes | |
7 | # - The RHEL boxes I work on are currently limited to Python 2.6.6, hence the | |
8 | # use of (deprecated) optparse. If I can ever get them all updated to | |
9 | # Python 2.7 (or better yet, 3.3), I'll switch to argparse | |
10 | # - This template runs in 2.6-3.3. Any changes made will need to be appropriate | |
11 | # to the Python distro you want to use | |
12 | # | |
13 | ############################################################################### | |
14 | ||
15 | __author__ = 'mr.it@cbs.dk - Michael Rasmussen' | |
16 | __version__= 0.1 | |
17 | ||
18 | try: # RHEL8 RHEL9 | |
19 | from optparse import OptionParser, OptionGroup # | |
20 | import logging as log | |
21 | import sys | |
22 | import urllib3 as urllib # python3-urllib3-1.24.2-5.el8.noarch.rpm python3-urllib3-1.26.5-3.el9.noarch.rpm | |
23 | import json | |
24 | except: | |
25 | sys.exit(3) | |
26 | ||
27 | ## These will override any args passed to the script normally. Comment out after testing. | |
28 | #testargs = '--help' | |
29 | #testargs = '--version' | |
30 | #testargs = '-vvv' | |
31 | #testing = True | |
32 | ||
33 | OK_RESPONSE = """ | |
34 | { | |
35 | "status": "UP", | |
36 | "components": { | |
37 | "db": { | |
38 | "status": "UP", | |
39 | "details": { | |
40 | "database": "PostgreSQL", | |
41 | "validationQuery": "isValid()" | |
42 | } | |
43 | }, | |
44 | "diskSpace": { | |
45 | "status": "UP", | |
46 | "details": { | |
47 | "total": 2013582688256, | |
48 | "free": 1635574571008, | |
49 | "threshold": 10485760, | |
50 | "path": "/home/mir/git/soasi-course-catalog-course-target-adapter/.", | |
51 | "exists": true | |
52 | } | |
53 | }, | |
54 | "ping": { | |
55 | "status": "UP" | |
56 | } | |
57 | } | |
58 | }""" | |
59 | DOWN_RESPONSE = """ | |
60 | { | |
61 | "status": "DOWN", | |
62 | "components": { | |
63 | "db": { | |
64 | "status": "DOWN", | |
65 | "components": { | |
66 | "adapterDataSource": { | |
67 | "status": "DOWN", | |
68 | "details": { | |
69 | "error": "org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection" | |
70 | } | |
71 | }, | |
72 | "es3DataSource": { | |
73 | "status": "UP", | |
74 | "details": { | |
75 | "database": "Microsoft SQL Server", | |
76 | "validationQuery": "isValid()" | |
77 | } | |
78 | }, | |
79 | "providersDataSource": { | |
80 | "status": "DOWN", | |
81 | "details": { | |
82 | "error": "org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection" | |
83 | } | |
84 | } | |
85 | } | |
86 | }, | |
87 | "diskSpace": { | |
88 | "status": "UP", | |
89 | "details": { | |
90 | "total": 1013309239296, | |
91 | "free": 882010726400, | |
92 | "threshold": 10485760, | |
93 | "path": "C:\\Users\\hf.it\\Projects\\smart-integrations\\adapters\\target\\soasi-invigilation-report-target-adapter", | |
94 | "exists": true | |
95 | } | |
96 | }, | |
97 | "ping": { | |
98 | "status": "UP" | |
99 | } | |
100 | } | |
101 | } | |
102 | """ | |
103 | def main(): | |
104 | """ Main plugin logic goes here """ | |
105 | ||
106 | ## Parse command-line arguments | |
107 | args, args2 = parse_args() | |
108 | ||
109 | ## Uncomment to test logging levels against verbosity settings | |
110 | # log.debug('debug message') | |
111 | # log.info('info message') | |
112 | # log.warning('warning message') | |
113 | # log.error('error message') | |
114 | # log.critical('critical message') | |
115 | # log.fatal('fatal message') | |
116 | options = vars(args) | |
117 | keyword = options['keyword'] | |
118 | url = options['url'] | |
119 | agent = options['agent'] | |
120 | testing = options['testing'] | |
121 | if keyword is None or url is None: | |
122 | message = "Keywork: {0} url: {1}".format(keyword, url) | |
123 | status = 3 | |
124 | log.fatal(message) | |
125 | else: | |
126 | try: | |
127 | if not testing: | |
128 | req_headers = { | |
129 | 'User-Agent': agent | |
130 | } | |
131 | http = urllib.PoolManager() | |
132 | response = http.request( | |
133 | 'GET', url, headers = req_headers | |
134 | ) | |
135 | data = response.data.decode('utf-8').replace("\\"," ") | |
136 | else: | |
137 | data = DOWN_RESPONSE.replace("\\"," ") | |
138 | data = json.loads(data) | |
139 | if 'status' in data and data['status'] == keyword: | |
140 | message = "UP" | |
141 | status = 2 | |
142 | else: | |
143 | status, message = gather_message(data) | |
144 | except Exception as e: | |
145 | log.fatal(e) | |
146 | message = e | |
147 | status = 3 | |
148 | ||
149 | gtfo(status, message) | |
150 | ||
151 | def gather_message(json_data): | |
152 | """ Assemble error messages """ | |
153 | status = 2 | |
154 | msg = None | |
155 | try: | |
156 | if 'components' in json_data: | |
157 | components = json_data['components'] | |
158 | for component in components: | |
159 | items = components[component] | |
160 | if 'components' in items: | |
161 | for item in items['components']: | |
162 | if 'status' in items['components'][item] and items['components'][item]['status'].upper() == 'DOWN': | |
163 | if msg is not None: | |
164 | msg += "\n{0}: {1}".format(item, items['components'][item]['details']['error']) | |
165 | else: | |
166 | msg = "{0}: {1}".format(item, items['components'][item]['details']['error']) | |
167 | else: | |
168 | if 'status' in items and items['status'].upper() == 'DOWN': | |
169 | if msg is not None: | |
170 | if 'details' in items and 'error' in items['details']: | |
171 | error = items['details']['error'] | |
172 | else: | |
173 | error = "No error message" | |
174 | if msg is not None: | |
175 | msg += "\n{0}: {1}".format(component, error) | |
176 | else: | |
177 | msg = "{0}: {1}".format(component, error) | |
178 | else: | |
179 | return (3, msg) | |
180 | except: | |
181 | return (3, msg) | |
182 | return (status, msg) | |
183 | ||
184 | ||
185 | def parse_args(): | |
186 | """ Parse command-line arguments """ | |
187 | ||
188 | parser = OptionParser(usage='usage: %prog [-v|vv|vvv] [options]', | |
189 | version='{0}: v.{1} by {2}'.format('%prog', __version__, __author__)) | |
190 | ||
191 | ## Verbosity (want this first, so it's right after --help and --version) | |
192 | parser.add_option('-v', help='Set verbosity level', | |
193 | action='count', default=0, dest='v') | |
194 | ||
195 | ## CLI arguments specific to this script | |
196 | group = OptionGroup(parser,'Plugin Options') | |
197 | group.add_option('-a', '--agent', help="User agent for request. Default: Python-nagios", | |
198 | default="Python-nagios", type='string') | |
199 | group.add_option('-k', '--keyword', help="Keyword to search for in response", default=None) | |
200 | group.add_option('-t', '--testing', help="Run in testing mode", default=False, action='store_true') | |
201 | group.add_option('-u', '--url', help="URL to requested resource", default=None) | |
202 | ||
203 | ## Common CLI arguments | |
204 | #parser.add_option('-c', '--critical', help='Set the critical threshold. Default: %(default)s', | |
205 | # default=97, type=float, dest='crit', metavar='##') | |
206 | #parser.add_option('-w', '--warning', help='Set the warning threshold. Default: %(default)s', | |
207 | # default=95, type=float, dest='warn', metavar='##') | |
208 | ||
209 | parser.add_option_group(group) | |
210 | ||
211 | ## Try to parse based on the testargs variable. If it doesn't exist, use args | |
212 | try: | |
213 | args, args2 = parser.parse_args(testargs.split()) | |
214 | except NameError: | |
215 | args, args2 = parser.parse_args() | |
216 | ||
217 | ## Set the logging level based on the -v arg | |
218 | log.getLogger().setLevel([log.ERROR, log.WARN, log.INFO, log.DEBUG][args.v]) | |
219 | ||
220 | log.debug('Parsed arguments: {0}'.format(args)) | |
221 | log.debug('Other arguments: {0}'.format(args2)) | |
222 | ||
223 | return args, args2 | |
224 | ||
225 | def gtfo(exitcode, message=''): | |
226 | """ Exit gracefully with exitcode and (optional) message """ | |
227 | ||
228 | log.debug('Exiting with status {0}. Message: {1}'.format(exitcode, message)) | |
229 | ||
230 | if message: | |
231 | print(message) | |
232 | exit(exitcode) | |
233 | ||
234 | if __name__ == '__main__': | |
235 | ## Initialize logging before hitting main, in case we need extra debuggability | |
236 | log.basicConfig(level=log.DEBUG, format='%(asctime)s - %(funcName)s - %(levelname)s - %(message)s') | |
237 | main() |