From: elinuxhenrik Date: Fri, 5 Jun 2020 08:23:27 +0000 (+0200) Subject: Add monitor web page to Health Check test X-Git-Tag: 2.0.0~6^2 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F03%2F4003%2F12;p=nonrtric.git Add monitor web page to Health Check test Also add documentation. Change-Id: I4732a65d21a458711672f6f49d56d186b15bd8c8 Issue-ID: NONRTRIC-200 Signed-off-by: elinuxhenrik --- diff --git a/.gitignore b/.gitignore index 6136fcbc..e5a2f721 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ docs/_build/ .project .settings .pydevproject -infer-out/ \ No newline at end of file +infer-out/ + +.vscode \ No newline at end of file diff --git a/docs/images/healtcheck.png b/docs/images/healtcheck.png new file mode 100644 index 00000000..fc56e38e Binary files /dev/null and b/docs/images/healtcheck.png differ diff --git a/docs/index.rst b/docs/index.rst index d20c1ba5..21469e74 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,6 +15,7 @@ Non-RT RIC ./developer-guide.rst ./installation-guide.rst ./overview.rst + ./use-cases.rst ./release-notes.rst * :ref:`search` diff --git a/docs/use-cases.rst b/docs/use-cases.rst new file mode 100644 index 00000000..a6d9cc5c --- /dev/null +++ b/docs/use-cases.rst @@ -0,0 +1,19 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. http://creativecommons.org/licenses/by/4.0 +.. Copyright (C) 2020 Nordix + +Use Cases +========= + +To support the use cases defined for the Non-RT RIC, there are implementations provided in the nonrtric repo, see: + nonrtric/test/usecases + +Health Check +------------ +The Health Check use case for the Non-RT RIC is a python script that regularly creates, reads, updates, and deletes a +policy in all Near-RT RICs that support the type used by the script. A self refreshing web page provides a view of +statistics for these regular checks. + +For more information about it, see the README file in the use case's folder. + +.. image:: ./images/healtcheck.png diff --git a/test/usecases/healthcheck/README.md b/test/usecases/healthcheck/README.md new file mode 100644 index 00000000..a699e844 --- /dev/null +++ b/test/usecases/healthcheck/README.md @@ -0,0 +1,39 @@ +# Use case Health Check test +# General + +The Health Check use case test provides a python script that regularly creates, reads, updates, and deletes a policy +in all Near-RT RICs that support the type used by the script. A self refreshing web page provides a view of statistics +for these regular checks. + +# Prerequisits +To run this script Python3 needs to be installed. To install the script's dependencies, run the following command from +the `src` folder: `pip install -r requirements.txt` + +# How to run +Go to the `src/` folder and run `python3 main.py`. The script will start and run until stopped. Use the `-h` option to +see the options available for the script. + +As default, the script uses the "Hello World" policy type with ID "2". To create the instances it uses the body file +`nonrtric/test/autotest/testdata/OSC/pihw_template.json`. The body file contains the string "XXX" as a parameter value. +This string vill be replaced with dynamic data during creation. It is possible to provide a custom policy type and +body file to the script at startup. + +To see the web page, navigate to `localhost:9990/stats`. The page refreshes itself with the same interval as the script +uses. + +## License + +Copyright (C) 2020 Nordix Foundation. +Licensed under the Apache License, Version 2.0 (the "License") +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +For more information about license please see the [LICENSE](LICENSE.txt) file for details. diff --git a/test/usecases/healthcheck/src/main.py b/test/usecases/healthcheck/src/main.py index d3fed3ad..fdf6d422 100644 --- a/test/usecases/healthcheck/src/main.py +++ b/test/usecases/healthcheck/src/main.py @@ -17,6 +17,10 @@ import argparse from datetime import datetime +from jinja2 import Template +from flask import Flask, request +import os.path +from os import path from pygments.util import xrange from requests import ConnectionError import requests @@ -32,6 +36,58 @@ TIME_BETWEEN_CHECKS = 60 type_to_use = '' policy_body = '' +app = Flask(__name__) + +# Server info +HOST_IP = "::" +HOST_PORT = 9990 +APP_URL = "/stats" + +stat_page_template = """ + + + + + Non-RealTime RIC Health Check + + +

General

+ + Policy type ID:...............................{{policyTypeId}}
+ Policy body path:.............................{{policyBodyPath}}
+ Time of last check:...........................{{time}}
+ Duration of check:............................{{duration}}
+ Number of checks:.............................{{noOfChecks}}
+
+

Near-RT RICs

+ + Number of unavailable Near-RT RICS:...........{{noOfUnavailableRics}}
+ Number of Near-RT RICS not supporting type....{{noOfNotSupportingRics}}
+ Number of Near-RT RICS supporting type:.......{{noOfSupportingRics}}
+
+

Policies

+ + Number of created policies:...................{{noOfCreatedPolicies}}
+ Number of read policies:......................{{noOfReadPolicies}}
+ Number of updated policies:...................{{noOfUpdatedPolicies}}
+ Number of deleted policies:...................{{noOfDeletedPolicies}}
+
+ + +""" +type_to_use = "2" +test_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +policy_body_path = os.path.join(test_dir, 'auto-test','testdata','OSC','pihw_template.json') + +duration = 0 +no_of_checks = 0 +no_of_unavailable_rics = 0 +no_of_rics_not_supporting_type = 0 +no_of_rics_supporting_type = 0 +no_of_created_policies = 0 +no_of_read_policies = 0 +no_of_updated_policies = 0 +no_of_deleted_policies = 0 class Ric: @@ -79,6 +135,27 @@ class PolicyCheckThread (threading.Thread): self.ric.no_of_deleted_policies += 1 +class MonitorServer (threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + + def run(self): + verboseprint('Staring monitor server') + app.run(port=HOST_PORT, host=HOST_IP) + + +@app.route(APP_URL, + methods=['GET']) +def produceStatsPage(): + t = Template(stat_page_template) + page = t.render(refreshTime=TIME_BETWEEN_CHECKS, policyTypeId=type_to_use, policyBodyPath=policy_body_path, + time=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), duration=duration, noOfChecks=no_of_checks, + noOfUnavailableRics=no_of_unavailable_rics, noOfNotSupportingRics=no_of_rics_not_supporting_type, + noOfSupportingRics=no_of_rics_supporting_type, noOfCreatedPolicies=no_of_created_policies, + noOfReadPolicies=no_of_read_policies, noOfUpdatedPolicies=no_of_updated_policies, + noOfDeletedPolicies=no_of_deleted_policies) + return page,200 + def get_rics_from_agent(): resp = requests.get(BASE_URL + '/rics') if not resp.ok: @@ -146,14 +223,21 @@ def delete_policy(thread_id): return True -def statistics(duration): +def statistics(): + global duration + global no_of_checks + global no_of_unavailable_rics + global no_of_rics_not_supporting_type + global no_of_rics_supporting_type + global no_of_created_policies + global no_of_read_policies + global no_of_updated_policies + global no_of_deleted_policies + + # Clear ric data between checks as it may have changed since last check. no_of_unavailable_rics = 0 no_of_rics_not_supporting_type = 0 no_of_rics_supporting_type = 0 - no_of_created_policies = 0 - no_of_read_policies = 0 - no_of_updated_policies = 0 - no_of_deleted_policies = 0 for ric in rics.values(): if not (ric.state == 'AVAILABLE' or ric.state == 'CONSISTENCY_CHECK'): @@ -167,17 +251,17 @@ def statistics(duration): else: no_of_rics_not_supporting_type += 1 - print(f'*********** Statistics {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} ***********') - print(f'Duration of check: {duration.total_seconds()} seconds') - print(f'Number of checks: {no_of_checks}') - print(f'Number of unavailable rics: {no_of_unavailable_rics}') - print(f'Number of rics not supporting type: {no_of_rics_not_supporting_type}') - print(f'Number of rics supporting type: {no_of_rics_supporting_type}') - print(f'Number of created policies: {no_of_created_policies}') - print(f'Number of read policies: {no_of_read_policies}') - print(f'Number of updated policies: {no_of_updated_policies}') - print(f'Number of deleted policies: {no_of_deleted_policies}') - print('******************************************************') + print(f'*********** Statistics {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} *******************') + print(f'Duration of check: {duration.total_seconds()} seconds') + print(f'Number of checks: {no_of_checks}') + print(f'Number of unavailable Near-RT RICS: {no_of_unavailable_rics}') + print(f'Number of Near-RT RICS not supporting type: {no_of_rics_not_supporting_type}') + print(f'Number of Near-RT RICS supporting type: {no_of_rics_supporting_type}') + print(f'Number of created policies: {no_of_created_policies}') + print(f'Number of read policies: {no_of_read_policies}') + print(f'Number of updated policies: {no_of_updated_policies}') + print(f'Number of deleted policies: {no_of_deleted_policies}') + print('**************************************************************') def run_check_threads(rics): @@ -197,6 +281,9 @@ def run_check_threads(rics): def split_rics_equally(chunks): # prep with empty dicts return_list = [dict() for _ in xrange(chunks)] + if len(rics) < RIC_CHUNK_SIZE: + return [rics] + idx = 0 for k,v in rics.items(): return_list[idx][k] = v @@ -214,8 +301,8 @@ def get_no_of_chunks(size_of_chunks, size_to_chunk): if __name__ == '__main__': parser = argparse.ArgumentParser(prog='PROG') - parser.add_argument('policyTypeId', help='The ID of the policy type to use') - parser.add_argument('policyBodyPath', help='The path to the JSON body of the policy to create') + parser.add_argument('--policyTypeId', help='The ID of the policy type to use') + parser.add_argument('--policyBodyPath', help='The path to the JSON body of the policy to create') parser.add_argument('-v', '--verbose', action='store_true', help='Turn on verbose printing') parser.add_argument('--version', action='version', version='%(prog)s 1.0') args = vars(parser.parse_args()) @@ -226,11 +313,19 @@ if __name__ == '__main__': else: verboseprint = lambda *a, **k: None # do-nothing function - verboseprint(f'Using policy type {args["policyTypeId"]}') - verboseprint(f'Using policy file {args["policyBodyPath"]}') + if args["policyTypeId"]: + type_to_use = args["policyTypeId"] + + if args["policyBodyPath"]: + policy_body_path = args["policyBodyPath"] + if not os.path.exists(policy_body_path): + print(f'Policy body {policy_body_path} does not exist.') + sys.exit(1) - type_to_use = args["policyTypeId"] - with open(args["policyBodyPath"]) as json_file: + verboseprint(f'Using policy type {type_to_use}') + verboseprint(f'Using policy file {policy_body_path}') + + with open(policy_body_path) as json_file: policy_body = json_file.read() verboseprint(f'Policy body: {policy_body}') @@ -242,7 +337,9 @@ if __name__ == '__main__': rics = create_ric_dict(rics_from_agent) - no_of_checks = 0 + monitor_server = MonitorServer() + monitor_server.start() + while True: start_time = datetime.now() chunked_rics = split_rics_equally(get_no_of_chunks(RIC_CHUNK_SIZE, rics.__len__())) @@ -252,7 +349,7 @@ if __name__ == '__main__': no_of_checks += 1 finish_time = datetime.now() duration = finish_time - start_time - statistics(duration) + statistics() sleep_time = TIME_BETWEEN_CHECKS - duration.total_seconds() verboseprint(f'Sleeping {sleep_time} seconds') time.sleep(sleep_time) diff --git a/test/usecases/healthcheck/src/requirements.txt b/test/usecases/healthcheck/src/requirements.txt new file mode 100644 index 00000000..7018123f --- /dev/null +++ b/test/usecases/healthcheck/src/requirements.txt @@ -0,0 +1,3 @@ +Flask==1.1.1 +jinja2==2.11.2 +pygments==2.2.0