]>
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: | |
da5fc43f | 25 | sys.exit(3) |
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' | |
da5fc43f | 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'] | |
da5fc43f | 120 | testing = options['testing'] |
04a80b59 MR |
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: | |
465bdd35 MR |
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) | |
04a80b59 MR |
139 | if 'status' in data and data['status'] == keyword: |
140 | message = "UP" | |
141 | status = 2 | |
142 | else: | |
da5fc43f | 143 | status, message = gather_message(data) |
04a80b59 | 144 | except Exception as e: |
da5fc43f MR |
145 | log.fatal(e) |
146 | message = e | |
147 | status = 3 | |
04a80b59 MR |
148 | |
149 | gtfo(status, message) | |
042cee45 | 150 | |
465bdd35 MR |
151 | def gather_message(json_data): |
152 | """ Assemble error messages """ | |
da5fc43f | 153 | status = 2 |
465bdd35 MR |
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: | |
da5fc43f | 179 | return (3, msg) |
465bdd35 | 180 | except: |
da5fc43f MR |
181 | return (3, msg) |
182 | return (status, msg) | |
465bdd35 | 183 | |
042cee45 MR |
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') | |
04a80b59 MR |
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) | |
465bdd35 | 200 | group.add_option('-t', '--testing', help="Run in testing mode", default=False, action='store_true') |
04a80b59 | 201 | group.add_option('-u', '--url', help="URL to requested resource", default=None) |
042cee45 MR |
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() |