1 # ============LICENSE_START===============================================
2 # Copyright (C) 2020 Nordix Foundation. All rights reserved.
3 # ========================================================================
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 # ============LICENSE_END=================================================
19 from datetime import datetime
20 from jinja2 import Template
21 from flask import Flask, request
25 from pygments.util import xrange
26 from requests import ConnectionError
32 SERVICE_NAME = 'HealthCheck'
33 DEFAULT_HOST = "http://localhost:8081"
34 BASE_PATH = "/a1-policy/v2"
36 TIME_BETWEEN_CHECKS = 60
48 stat_page_template = """
52 <meta http-equiv=\"refresh\" content=\"{{refreshTime}}\">
53 <title>Non-RealTime RIC Health Check</title>
57 <font face=\"monospace\">
58 Policy type ID:...............................{{policyTypeId}}<br>
59 Policy body path:.............................{{policyBodyPath}}<br>
60 Time of last check:...........................{{time}}<br>
61 Duration of check:............................{{duration}}<br>
62 Number of checks:.............................{{noOfChecks}}<br>
65 <font face=\"monospace\">
66 Number of unavailable Near-RT RICS:...........{{noOfUnavailableRics}}<br>
67 Number of Near-RT RICS not supporting type....{{noOfNotSupportingRics}}<br>
68 Number of Near-RT RICS supporting type:.......{{noOfSupportingRics}}<br>
71 <font face=\"monospace\">
72 Number of created policies:...................{{noOfCreatedPolicies}}<br>
73 Number of read policies:......................{{noOfReadPolicies}}<br>
74 Number of updated policies:...................{{noOfUpdatedPolicies}}<br>
75 Number of deleted policies:...................{{noOfDeletedPolicies}}<br>
80 base_url = DEFAULT_HOST + BASE_PATH
82 policy_body_path = 'pihw_template.json'
86 no_of_unavailable_rics = 0
87 no_of_rics_not_supporting_type = 0
88 no_of_rics_supporting_type = 0
89 no_of_created_policies = 0
90 no_of_read_policies = 0
91 no_of_updated_policies = 0
92 no_of_deleted_policies = 0
96 def __init__(self, name, supported_types, state):
98 self.supports_type_to_use = self.policy_type_supported(supported_types)
100 self.no_of_created_policies = 0
101 self.no_of_read_policies = 0
102 self.no_of_updated_policies = 0
103 self.no_of_deleted_policies = 0
105 def update_supported_types(self, supported_types):
106 self.supports_type_to_use = self.policy_type_supported(supported_types)
108 def policy_type_supported(self, supported_policy_types):
109 for supported_type in supported_policy_types:
110 if type_to_use == supported_type:
116 class PolicyCheckThread (threading.Thread):
118 def __init__(self, thread_id, ric):
119 threading.Thread.__init__(self)
120 self.thread_id = thread_id
124 verboseprint(f'Checking ric: {self.ric.name}')
125 if put_policy(self.thread_id, self.ric.name):
126 verboseprint(f'Created policy: {self.thread_id} in ric: {self.ric.name}')
127 self.ric.no_of_created_policies += 1
129 if get_policy(self.thread_id):
130 verboseprint(f'Read policy: {self.thread_id} from ric: {self.ric.name}')
131 self.ric.no_of_read_policies += 1
132 if put_policy(self.thread_id, self.ric.name, update_value=1):
133 verboseprint(f'Updated policy: {self.thread_id} in ric: {self.ric.name}')
134 self.ric.no_of_updated_policies += 1
135 if delete_policy(self.thread_id):
136 verboseprint(f'Deleted policy: {self.thread_id} from ric: {self.ric.name}')
137 self.ric.no_of_deleted_policies += 1
140 class MonitorServer (threading.Thread):
142 threading.Thread.__init__(self)
145 verboseprint('Staring monitor server')
146 app.run(port=HOST_PORT, host=HOST_IP)
151 def produceStatsPage():
152 t = Template(stat_page_template)
153 page = t.render(refreshTime=TIME_BETWEEN_CHECKS, policyTypeId=type_to_use, policyBodyPath=policy_body_path,
154 time=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), duration=duration, noOfChecks=no_of_checks,
155 noOfUnavailableRics=no_of_unavailable_rics, noOfNotSupportingRics=no_of_rics_not_supporting_type,
156 noOfSupportingRics=no_of_rics_supporting_type, noOfCreatedPolicies=no_of_created_policies,
157 noOfReadPolicies=no_of_read_policies, noOfUpdatedPolicies=no_of_updated_policies,
158 noOfDeletedPolicies=no_of_deleted_policies)
161 def get_rics_from_agent():
162 resp = requests.get(base_url + '/rics')
164 verboseprint(f'Unable to get Rics {resp.status_code}')
169 def create_ric_dict(rics_as_json):
171 for ric_info in rics_as_json["rics"]:
172 rics[ric_info["ric_id"]] = (Ric(ric_info["ric_id"], ric_info["policytype_ids"], ric_info['state']))
173 verboseprint(f'Adding ric: {rics[ric_info["ric_id"]]}')
180 for ric_info in get_rics_from_agent()["rics"]:
181 if ric_info["ric_id"] in rics:
182 rics[ric_info["ric_id"]].update_supported_types(ric_info["policytype_ids"])
183 rics[ric_info["ric_id"]].state = ric_info['state']
185 added_rics[ric_info["ric_id"]] = (Ric(ric_info["ric_id"], ric_info["policytype_ids"]))
186 verboseprint(f'Adding ric: {rics[ric_info["ric_id"]]}')
188 rics.update(added_rics)
191 def put_policy(thread_id, ric_name, update_value=0):
192 policy_id = f'thread_{thread_id}'
193 complete_url = base_url + '/policies'
194 headers = {'content-type': 'application/json'}
195 policy_obj = json.loads(policy_data.replace('XXX', str(thread_id + update_value)))
198 "policy_id": policy_id,
199 "service_id": SERVICE_NAME,
200 "policy_data": policy_obj,
201 "policytype_id": type_to_use
203 resp = requests.put(complete_url, json=body, headers=headers, verify=False)
206 verboseprint(f'Unable to create policy {resp}')
212 def get_policy(thread_id):
213 policy_id = f'thread_{thread_id}'
214 complete_url = f'{base_url}/policies/{policy_id}'
215 resp = requests.get(complete_url)
218 verboseprint(f'Unable to get policy {resp}')
224 def delete_policy(thread_id):
225 policy_id = f'thread_{thread_id}'
226 complete_url = f'{base_url}/policies/{policy_id}'
227 resp = requests.delete(complete_url)
230 verboseprint(f'Unable to delete policy for policy ID {policy_id}')
239 global no_of_unavailable_rics
240 global no_of_rics_not_supporting_type
241 global no_of_rics_supporting_type
242 global no_of_created_policies
243 global no_of_read_policies
244 global no_of_updated_policies
245 global no_of_deleted_policies
247 # Clear ric data between checks as it may have changed since last check.
248 no_of_unavailable_rics = 0
249 no_of_rics_not_supporting_type = 0
250 no_of_rics_supporting_type = 0
252 for ric in rics.values():
253 if not (ric.state == 'AVAILABLE' or ric.state == 'CONSISTENCY_CHECK'):
254 no_of_unavailable_rics += 1
255 elif ric.supports_type_to_use:
256 no_of_rics_supporting_type += 1
257 no_of_created_policies += ric.no_of_created_policies
258 no_of_read_policies += ric.no_of_read_policies
259 no_of_updated_policies += ric.no_of_updated_policies
260 no_of_deleted_policies += ric.no_of_deleted_policies
262 no_of_rics_not_supporting_type += 1
264 print(f'*********** Statistics {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} *******************')
265 print(f'Duration of check: {duration.total_seconds()} seconds')
266 print(f'Number of checks: {no_of_checks}')
267 print(f'Number of unavailable Near-RT RICS: {no_of_unavailable_rics}')
268 print(f'Number of Near-RT RICS not supporting type: {no_of_rics_not_supporting_type}')
269 print(f'Number of Near-RT RICS supporting type: {no_of_rics_supporting_type}')
270 print(f'Number of created policies: {no_of_created_policies}')
271 print(f'Number of read policies: {no_of_read_policies}')
272 print(f'Number of updated policies: {no_of_updated_policies}')
273 print(f'Number of deleted policies: {no_of_deleted_policies}')
274 print('**************************************************************')
277 def run_check_threads(rics):
280 for ric in rics.values():
281 if ric.supports_type_to_use and (ric.state == 'AVAILABLE' or ric.state == 'CONSISTENCY_CHECK'): #or ric.name == 'ric_not_working':
282 policy_checker = PolicyCheckThread(thread_id, ric)
283 policy_checker.start()
285 threads.append(policy_checker)
287 for checker in threads:
291 def split_rics_equally(chunks):
292 # prep with empty dicts
293 return_list = [dict() for _ in xrange(chunks)]
294 if len(rics) < RIC_CHUNK_SIZE:
298 for k,v in rics.items():
299 return_list[idx][k] = v
300 if idx < chunks-1: # indexes start at 0
307 def get_no_of_chunks(size_of_chunks, size_to_chunk):
308 (q, _) = divmod(size_to_chunk, size_of_chunks)
312 if __name__ == '__main__':
313 parser = argparse.ArgumentParser(prog='PROG')
314 parser.add_argument('--pmsHost', help='The host of the A1 PMS, e.g. http://localhost:8081')
315 parser.add_argument('--policyTypeId', help='The ID of the policy type to use')
316 parser.add_argument('--policyBodyPath', help='The path to the JSON body of the policy to create')
317 parser.add_argument('-v', '--verbose', action='store_true', help='Turn on verbose printing')
318 parser.add_argument('--version', action='version', version='%(prog)s 1.1')
319 args = vars(parser.parse_args())
322 def verboseprint(*args, **kwargs):
323 print(*args, **kwargs)
325 verboseprint = lambda *a, **k: None # do-nothing function
328 base_url = args["pmsHost"] + BASE_PATH
330 if args["policyTypeId"]:
331 type_to_use = args["policyTypeId"]
333 if args["policyBodyPath"]:
334 policy_body_path = args["policyBodyPath"]
335 if not os.path.exists(policy_body_path):
336 print(f'Policy body {policy_body_path} does not exist.')
339 verboseprint(f'Using policy type {type_to_use}')
340 verboseprint(f'Using policy file {policy_body_path}')
342 with open(policy_body_path) as json_file:
343 policy_data = json_file.read()
344 verboseprint(f'Policy body: {policy_data}')
347 rics_from_agent = get_rics_from_agent()
348 except ConnectionError:
349 print(f'A1PMS is not answering on {base_url}, cannot start!')
352 rics = create_ric_dict(rics_from_agent)
354 monitor_server = MonitorServer()
355 monitor_server.start()
358 start_time = datetime.now()
359 chunked_rics = split_rics_equally(get_no_of_chunks(RIC_CHUNK_SIZE, rics.__len__()))
360 for ric_chunk in chunked_rics:
361 run_check_threads(ric_chunk)
364 finish_time = datetime.now()
365 duration = finish_time - start_time
367 sleep_time = TIME_BETWEEN_CHECKS - duration.total_seconds()
368 verboseprint(f'Sleeping {sleep_time} seconds')
369 time.sleep(sleep_time)
372 verboseprint('Exiting main')