]>
Commit | Line | Data |
---|---|---|
042cee45 MR |
1 | #! /usr/bin/env python |
2 | ||
3 | ############################################################################### | |
40034215 | 4 | # Nagios plugin check_actuator_health |
042cee45 MR |
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 | ||
04a80b59 | 15 | __author__ = 'mr.it@cbs.dk - Michael Rasmussen' |
042cee45 MR |
16 | __version__= 0.1 |
17 | ||
465bdd35 MR |
18 | try: # RHEL8 RHEL9 |
19 | from optparse import OptionParser, OptionGroup # | |
04a80b59 MR |
20 | import logging as log |
21 | import sys | |
465bdd35 | 22 | import urllib3 as urllib # python3-urllib3-1.24.2-5.el8.noarch.rpm python3-urllib3-1.26.5-3.el9.noarch.rpm |
04a80b59 MR |
23 | import json |
24 | except: | |
25 | sys.exit(2) | |
042cee45 MR |
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' | |
465bdd35 | 31 | testing = True |
042cee45 | 32 | |
04a80b59 MR |
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 | """ | |
042cee45 MR |
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') | |
04a80b59 MR |
116 | options = vars(args) |
117 | keyword = options['keyword'] | |
118 | url = options['url'] | |
119 | agent = options['agent'] | |
120 | if keyword is None or url is None: | |
121 | message = "Keywork: {0} url: {1}".format(keyword, url) | |
122 | status = 3 | |
123 | log.fatal(message) | |
124 | else: | |
125 | try: | |
465bdd35 MR |
126 | if not testing: |
127 | req_headers = { | |
128 | 'User-Agent': agent | |
129 | } | |
130 | http = urllib.PoolManager() | |
131 | response = http.request( | |
132 | 'GET', url, headers = req_headers | |
133 | ) | |
134 | data = response.data.decode('utf-8').replace("\\"," ") | |
135 | else: | |
136 | data = DOWN_RESPONSE.replace("\\"," ") | |
137 | data = json.loads(data) | |
04a80b59 MR |
138 | if 'status' in data and data['status'] == keyword: |
139 | message = "UP" | |
140 | status = 2 | |
141 | else: | |
465bdd35 | 142 | message = gather_message(data) |
04a80b59 MR |
143 | status = 0 |
144 | except Exception as e: | |
145 | print(e) | |
146 | message = "DOWN" | |
147 | status = 2 | |
148 | ||
149 | gtfo(status, message) | |
042cee45 | 150 | |
465bdd35 MR |
151 | def gather_message(json_data): |
152 | """ Assemble error messages """ | |
153 | msg = None | |
154 | try: | |
155 | if 'components' in json_data: | |
156 | components = json_data['components'] | |
157 | for component in components: | |
158 | items = components[component] | |
159 | if 'components' in items: | |
160 | for item in items['components']: | |
161 | if 'status' in items['components'][item] and items['components'][item]['status'].upper() == 'DOWN': | |
162 | if msg is not None: | |
163 | msg += "\n{0}: {1}".format(item, items['components'][item]['details']['error']) | |
164 | else: | |
165 | msg = "{0}: {1}".format(item, items['components'][item]['details']['error']) | |
166 | else: | |
167 | if 'status' in items and items['status'].upper() == 'DOWN': | |
168 | if msg is not None: | |
169 | if 'details' in items and 'error' in items['details']: | |
170 | error = items['details']['error'] | |
171 | else: | |
172 | error = "No error message" | |
173 | if msg is not None: | |
174 | msg += "\n{0}: {1}".format(component, error) | |
175 | else: | |
176 | msg = "{0}: {1}".format(component, error) | |
177 | else: | |
178 | sys.exit(3) | |
179 | except: | |
180 | sys.exit(3) | |
181 | return msg | |
182 | ||
042cee45 MR |
183 | |
184 | def parse_args(): | |
185 | """ Parse command-line arguments """ | |
186 | ||
187 | parser = OptionParser(usage='usage: %prog [-v|vv|vvv] [options]', | |
188 | version='{0}: v.{1} by {2}'.format('%prog', __version__, __author__)) | |
189 | ||
190 | ## Verbosity (want this first, so it's right after --help and --version) | |
191 | parser.add_option('-v', help='Set verbosity level', | |
192 | action='count', default=0, dest='v') | |
193 | ||
194 | ## CLI arguments specific to this script | |
195 | group = OptionGroup(parser,'Plugin Options') | |
04a80b59 MR |
196 | group.add_option('-a', '--agent', help="User agent for request. Default: Python-nagios", |
197 | default="Python-nagios", type='string') | |
198 | group.add_option('-k', '--keyword', help="Keyword to search for in response", default=None) | |
465bdd35 | 199 | group.add_option('-t', '--testing', help="Run in testing mode", default=False, action='store_true') |
04a80b59 | 200 | group.add_option('-u', '--url', help="URL to requested resource", default=None) |
042cee45 MR |
201 | |
202 | ## Common CLI arguments | |
203 | #parser.add_option('-c', '--critical', help='Set the critical threshold. Default: %(default)s', | |
204 | # default=97, type=float, dest='crit', metavar='##') | |
205 | #parser.add_option('-w', '--warning', help='Set the warning threshold. Default: %(default)s', | |
206 | # default=95, type=float, dest='warn', metavar='##') | |
207 | ||
208 | parser.add_option_group(group) | |
209 | ||
210 | ## Try to parse based on the testargs variable. If it doesn't exist, use args | |
211 | try: | |
212 | args, args2 = parser.parse_args(testargs.split()) | |
213 | except NameError: | |
214 | args, args2 = parser.parse_args() | |
215 | ||
216 | ## Set the logging level based on the -v arg | |
217 | log.getLogger().setLevel([log.ERROR, log.WARN, log.INFO, log.DEBUG][args.v]) | |
218 | ||
219 | log.debug('Parsed arguments: {0}'.format(args)) | |
220 | log.debug('Other arguments: {0}'.format(args2)) | |
221 | ||
222 | return args, args2 | |
223 | ||
224 | def gtfo(exitcode, message=''): | |
225 | """ Exit gracefully with exitcode and (optional) message """ | |
226 | ||
227 | log.debug('Exiting with status {0}. Message: {1}'.format(exitcode, message)) | |
228 | ||
229 | if message: | |
230 | print(message) | |
231 | exit(exitcode) | |
232 | ||
233 | if __name__ == '__main__': | |
234 | ## Initialize logging before hitting main, in case we need extra debuggability | |
235 | log.basicConfig(level=log.DEBUG, format='%(asctime)s - %(funcName)s - %(levelname)s - %(message)s') | |
236 | main() |