-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
280 lines (225 loc) · 14.6 KB
/
main.py
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
import json
from modules.config import Config
from modules.hcnendpoints import HCNEndpoints
from modules.retriever import HCNRetriever
from modules.parser import HCNParser
from modules.hcnbuilder import HCNBuilder
from modules.hcnlogging import HCNLogging
import sys
import re
import os.path
import datetime
# Set configuration file
cfg_file = sys.argv[2]
# Create datetime function for use later
def utc_now():
utc_now = datetime.datetime.utcnow()
utc_timestamp = utc_now.strftime("%Y-%m-%dT%H:%M:%SZ")
return utc_timestamp
# Start of program
if __name__ == "__main__":
if not os.path.exists(cfg_file):
print(f"Error: Configuration file does not exist.")
sys.exit(1)
else:
print(f"{utc_now()} Found config file: {cfg_file}")
try:
environment_filter = sys.argv[1].replace('\n', '').replace('\r', '').replace('\?', '')
except:
print(f"Usage: python3 {sys.argv[0]} <environment (as defined in config.yaml)>")
sys.exit(1)
finally:
print(f"{utc_now()} Running HealthCheckNormalizer for environment {environment_filter}")
# Initialize objects
cfg = Config(cfg_file)
# Set default values
default_values = cfg.default_values
# Global values
global_values = cfg.global_values
# Rules
rules_healthy = cfg.rules_healthy
rules_degraded = cfg.rules_degraded
rules_unhealthy = cfg.rules_unhealthy
# Set environment names
environments = cfg.environments
# Set logging file
if global_values['main_logs']:
log_file = f"{global_values['logging_directory']}/{global_values['main_logging_file']}"
# Check if the given environment exists within the configuration file
if environment_filter in environments:
pass
else:
error_msg = f"{utc_now()} Error: Non-existing environment {environment_filter} in config file."
if global_values['main_logs']:
HCNLogging(log_file, error_msg)
print(error_msg)
cfg.printDebugMessage(environments, f"Environments:")
sys.exit(1)
# Get endpoints URL from configuration file
endpoints = cfg.node_fqdns
hcn = HCNEndpoints(endpoints, default_values)
# Debug mode on: Print all endpoint node URL's (list)
cfg.printDebugMessage(hcn.node_endpoint_fqdn, 'ENDPOINTS')
# Prepare relevant endpoints (Degraded and Unhealthy)
hcn_healthcheck = HCNBuilder()
unique_id = 0
# Go through each URL
for node_endpoint_url in hcn.node_endpoint_urls:
try:
node_endpoint_list = HCNRetriever(node_endpoint_url, global_values['connection_timeout'])
# Debug mode on: Print endpoint URL's
cfg.printDebugMessage(node_endpoint_url, 'ENDPOINT')
# Check if HTTP code is between 200 and 299 (success msgs)
if node_endpoint_list.content.status_code >= 200 and node_endpoint_list.content.status_code <= 299:
node_endpoint_content = node_endpoint_list.content.text
# Raise exception if not
else:
raise Exception("Server returned status code:", node_endpoint_list.content.status_code)
except NameError:
error_msg = f"{utc_now()} Error: Node not found: {node_endpoint_url}"
if global_values['main_logs']:
HCNLogging(log_file, error_msg)
print(error_msg)
sys.exit(1)
except Exception as e:
error_msg = f"{utc_now()} Error: Unable to retrieve node endpoint list from URL: {node_endpoint_url}"
if global_values['main_logs']:
HCNLogging(log_file, error_msg)
print(error_msg)
sys.exit(1)
finally:
try:
parse = HCNParser(node_endpoint_content)
# Detect format for health check
format = parse.detectHCFormat()
# Debug mode on: Print endpoint content
cfg.printDebugMessage(f'{node_endpoint_content}', f'ENDPOINT CONTENT ({format})')
# Check if the endpoint node URL is JSON format
# Convert if neccessary
if format == 'json':
json_content = node_endpoint_content
elif format == 'xml':
json_content = parse.xmlToJson(node_endpoint_content)
elif format == 'html':
json_content = parse.htmlToJson(node_endpoint_content)
elif format == 'unk':
error_msg = f"{utc_now()} Unknown format for health check: {node_endpoint_url}"
if global_values['main_logs']:
HCNLogging(log_file, error_msg)
cfg.printDebugMessage(f"{utc_now()} Unknown format for health check: {node_endpoint_url}", 'ENDPOINT PARSER')
# Parse the endpoints if enlisted in JSON format
try:
json_content = json.loads(json_content)
except:
error_msg = f"{utc_now()} Error while parsing JSON content. Valid JSON format not found for endpoint {node_endpoint_url}"
if global_values['main_logs']:
HCNLogging(log_file, error_msg)
cfg.printDebugMessage(f"{utc_now()} Error while parsing JSON content. Valid JSON format not found for endpoint {node_endpoint_url}", 'ENDPOINT PARSER')
finally:
# Iterate through the retrieved JSON content
for json_dict in json_content:
# Retrieve essential service and server information
service_base_url = json_dict['serviceBaseUrl'].lower()
service_name = json_dict['serviceName']
server_fqdn = re.sub("((http|https):\/\/)|(:[0-9].*$)", "", service_base_url).lower()
server_hostname = re.sub("\.[A-Za-z0-9].*$", "", server_fqdn).lower()
node_endpoint = re.sub("((http|https):\/\/)|(:[0-9].*$)", "", node_endpoint_url).lower()
environment_name = endpoints[node_endpoint]['environment']
# Check if the given environment is equal to this entry
if environment_filter == environment_name:
# If debug is `True` - print the service and server information
debug_msg = f"SERVICE NAME: {service_name}\nSERVICE BASE URL: {service_base_url}\nSERVER FQDN: {server_fqdn}\nSERVER HOSTNAME: {server_hostname}\nJSON:\n{json_dict}\nENVIRONMENT:\n{environment_name}"
cfg.printDebugMessage(debug_msg, 'SERVER AND SERVICE INFO')
# Iterate through each endpoint for the given service
for endpoint in json_dict['endpoints']:
unique_id = int(unique_id+1)
endpoint_id = unique_id
# Build endpoint address and retrieve data from endpoint
service_endpoint_url = service_base_url + endpoint['endpointAddress']
cfg.printDebugMessage(f"{utc_now()} Attempting to retrieve endpoint: {service_endpoint_url}")
service = HCNRetriever(service_endpoint_url, global_values['connection_timeout'])
# Check if the service returns http_code between 200 - 299
if service.content.status_code >= 200 and service.content.status_code <= 299:
# Convert endpoint data to text, load parser and detect format for health check
healthcheck = service.content.text
svc_parse = HCNParser(healthcheck)
svc_format = svc_parse.detectHCFormat()
# Check if given rules (string) exists in URL
rules_healthy_url = bool([element for element in rules_healthy['in_url'] if(element in service_endpoint_url)])
# Check if given rules (string) exists in reply
rules_healthy_reply = bool([element for element in rules_healthy['url_expected_reply'] if(element in healthcheck)])
# Check if the health check format is HTML
if svc_format == 'html':
json_service_content = parse.htmlToJson(healthcheck)
service_status = 'HEALTHY'
cfg.printDebugMessage(f"HTML detected:\n{service_endpoint_url}", 'SERVICE STATUS')
# Check if the health check format is XML
elif svc_format == 'xml':
json_service_content = parse.xmlToJson(healthcheck)
service_status = 'HEALTHY'
cfg.printDebugMessage(f"XML detected:\n{service_endpoint_url}", 'SERVICE STATUS')
# Check if rules applies to both URL and the reply
elif rules_healthy_url is True and rules_healthy_reply is True:
service_status = 'HEALTHY'
cfg.printDebugMessage(f"Healthy rules matched on URL and reply: {service_endpoint_url}", 'SERVICE STATUS')
# JSON - Check if the format is JSON and the rules applies for URL
elif svc_format == 'json' and rules_healthy_url is True:
json_service_content = json.loads(healthcheck.lower())
service_status = json_service_content['status'].upper()
service_status_code = json_service_content['statuscode']
# Check if service status is matching status code for unhealthy service
if service_status == rules_unhealthy['status_code']:
service_status = 'UNHEALTHY'
# Check if service status is matching status code for degraded service
elif service_status == rules_degraded['status_code']:
service_status = 'DEGRADED'
# Check if service status is matching status code for healthy service
elif service_status == rules_healthy['status_code']:
service_status = 'HEALTHY'
cfg.printDebugMessage(f"JSON detected:\n{service_endpoint_url}\nSERVICE STATUS: {service_status} ({service_name})\n{healthcheck}", 'SERVICE STATUS:')
# Check if the reply is blank
elif len(healthcheck) == 0:
service_status = 'HEALTHY'
cfg.printDebugMessage(f"Healthy health check, length of health check is 0: {service_endpoint_url}", 'SERVICE STATUS')
# Check if the rules only applies for the reply
elif rules_healthy_reply is True:
service_status = 'HEALTHY'
cfg.printDebugMessage(f"Healthy rules matched on reply only: {service_endpoint_url}", 'SERVICE STATUS')
# Unknown format for health check (e.g. format not implemented)
elif svc_format == 'unk':
service_status = 'unknown'
cfg.printDebugMessage(f"Unknown format for health check: {service_endpoint_url}", 'SERVICE STATUS')
# Check if the global values indicates if healthy endpoints should be shown in generated health check
if (global_values['show_active'] and service_status == 'HEALTHY') or service_status == 'DEGRADED':
hcn_healthcheck.buildHealthCheck(server_hostname, service_endpoint_url, service_name, service_status, endpoint_id, environment_name)
else:
# generate health check data
service_status = 'UNHEALTHY'
hcn_healthcheck.buildHealthCheck(server_hostname, service_endpoint_url, service_name, service_status, endpoint_id, environment_name)
debug_msg = f"SERVICE NAME: {service_name} | SERVER HOSTNAME: {server_hostname} | SERVER FQDN: {server_fqdn} | SERVICE ENDPOINT URL: {service_endpoint_url}"
cfg.printDebugMessage(debug_msg, 'SERVICE STATUS')
# Print a compact debug message if chosen in configuration
compact_debug_msg = f"{service_status} {server_fqdn} {service_name} {service_endpoint_url}"
cfg.printDebugMessage(compact_debug_msg)
# Print full debug message if chosen
cfg.printDebugMessage(f"SERVICE STATUS: {service_status} ({service_name}): {service_endpoint_url}", 'SERVICE STATUS')
except NameError:
error_msg = f"{utc_now()} Error: Node not found: {node_endpoint_url}"
if global_values['main_logs']:
HCNLogging(log_file, error_msg)
print(error_msg)
# Dump JSON to file
if global_values['write_to_file']:
json_dump = json.dumps(hcn_healthcheck.getHealthCheck(), indent=2)
output_dir = f"{global_values['endpoint_dir']}"
if not os.path.exists(output_dir):
error_msg = f"{utc_now()} Error: Output directory does not exist: {output_dir}"
if global_values['main_logs']:
HCNLogging(log_file, error_msg)
print(error_msg)
else:
output_file = f"{output_dir}/{environment_filter}-endpoints.json"
with open(output_file, "w") as file:
if file.write(json_dump):
print(f"{utc_now()} Successfully dumped JSON structure to JSON file: {output_file}")
print(f"{utc_now()} Script finished for {environment_filter}")