From eae5626936f20d51af41c1b2ea4a7bcc10fa4086 Mon Sep 17 00:00:00 2001 From: DenisGNoonan Date: Thu, 3 Aug 2023 16:03:44 +0100 Subject: [PATCH] A1-Simulator - Align with OSC Near-RT-RIC A1 Mediator Issue-ID: NONRTRIC-892 Change-Id: I8123c492e1f3354df15dd107e73ee71a7e28a22d Signed-off-by: DenisGNoonan --- near-rt-ric-simulator/README.md | 5 +- near-rt-ric-simulator/api/OSC_2.1.0/openapi.yaml | 537 ++++++++++++--------- near-rt-ric-simulator/src/OSC_2.1.0/__init__.py | 0 .../src/OSC_2.1.0/controllers/__init__.py | 0 .../a1_mediator_controller.py} | 84 ++-- near-rt-ric-simulator/src/OSC_2.1.0/main.py | 117 +++-- .../src/OSC_2.1.0/models/enforceStatus.py | 81 ++++ .../src/OSC_2.1.0/var_declaration.py | 30 +- near-rt-ric-simulator/src/STD_2.0.0/main.py | 8 +- near-rt-ric-simulator/src/common/maincommon.py | 4 +- near-rt-ric-simulator/src/start.sh | 10 +- near-rt-ric-simulator/test/OSC_2.1.0/basic_test.sh | 48 +- near-rt-ric-simulator/tests/test_osc_2_1_0.py | 176 ++++++- near-rt-ric-simulator/tests/unittest_setup.py | 13 +- 14 files changed, 765 insertions(+), 348 deletions(-) create mode 100644 near-rt-ric-simulator/src/OSC_2.1.0/__init__.py create mode 100644 near-rt-ric-simulator/src/OSC_2.1.0/controllers/__init__.py rename near-rt-ric-simulator/src/OSC_2.1.0/{a1.py => controllers/a1_mediator_controller.py} (78%) create mode 100644 near-rt-ric-simulator/src/OSC_2.1.0/models/enforceStatus.py diff --git a/near-rt-ric-simulator/README.md b/near-rt-ric-simulator/README.md index b62324e..2369872 100644 --- a/near-rt-ric-simulator/README.md +++ b/near-rt-ric-simulator/README.md @@ -59,6 +59,7 @@ URIs for A1: | GET, get a policy | http://localhost:8085/a1-p/policytypes/{policy\_type\_id}/policies/{policy\_instance\_id} | | PUT, create/update a policy | http://localhost:8085/a1-p/policytypes/{policy\_type\_id}/policies/{policy\_instance\_id} | | GET, get policy status | http://localhost:8085/a1-p/policytypes/{policy\_type\_id}/policies/{policy\_instance\_id}/status | +| PUT, deliver data produced by data producer | http://localhost:8085/data-delivery json payload = {"job":"101", "payload":"another payload"}| Swagger UI at: http://localhost:8085/ui/ @@ -82,8 +83,6 @@ URIs for admin operations: | Turn on http header and payload logging | http://localhost:8085payload_logging/on | | Turn off http header and payload logging | http://localhost:8085payload_logging/off | - - # Supported operations in simulator A1 Standard 1.1.3 For the complete yaml specification, see [STD_A1.yaml](../near-rt-ric-simulator/api/STD_1.1.3/STD_A1.yaml). @@ -164,7 +163,7 @@ An env variable, A1\_VERSION need to be passed to the container at start to sele An env variable, REMOTE_HOSTS_LOGGING, can be set (any value is ok) and the the counter remote\_hosts will log the host names of all remote hosts that has accessed the A1 URIs. If host names cannot be resolved, the ip address of the remote host is logged instead. This logging is default off so must be configured to be enabled. If not configured, the counter remote\_hosts will return a fixed text indicating that host name logging is not enabled. Use this feature with caution, remote host lookup may take time in certain environments. -And optional env variable, DUPLICATE_CHECK, can be set to '1' to turn on duplicate check of policiy json. A duplicate policy is when the policy json is exactly same as for a different policy id of the same type. This function is default set off if the variable is not set at all or set to '0'. +And optional env variable, DUPLICATE_CHECK, can be set to '1' to turn on duplicate check of policy json. A duplicate policy is when the policy json is exactly same as for a different policy id of the same type. This function is default set off if the variable is not set at all or set to '0'. The simulator can also run using the https protocol. The enable https, a valid certificate and key need to provided. There is self-signed certificate available in the certificate dir and that dir shall be mounted to the container to make it available diff --git a/near-rt-ric-simulator/api/OSC_2.1.0/openapi.yaml b/near-rt-ric-simulator/api/OSC_2.1.0/openapi.yaml index 828a6bf..39af6c8 100644 --- a/near-rt-ric-simulator/api/OSC_2.1.0/openapi.yaml +++ b/near-rt-ric-simulator/api/OSC_2.1.0/openapi.yaml @@ -1,7 +1,7 @@ # ================================================================================== # Copyright (c) 2019-2020 Nokia # Copyright (c) 2018-2020 AT&T Intellectual Property. -# Copyright (c) 2020 Nordix Foundation, Modifications +# Copyright (c) 2020-2023 Nordix Foundation, Modifications # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,311 +15,410 @@ # See the License for the specific language governing permissions and # limitations under the License. # ================================================================================== -openapi: 3.0.0 +openapi: 3.0.1 info: - version: 2.1.0 title: RIC A1 + version: 2.1.0 +servers: +- url: / paths: - '/a1-p/healthcheck': + /a1-p/healthcheck: get: - description: > - Perform a healthcheck on a1 tags: - - A1 Mediator - operationId: a1.get_healthcheck + - A1 Mediator + description: | + Perform a healthcheck on a1 + operationId: a1_controller_get_healthcheck responses: 200: - description: > - A1 is healthy. - Anything other than a 200 should be considered a1 as failing - - '/a1-p/policytypes': + description: | + A1 is healthy. Anything other than a 200 should be considered a1 as failing + content: {} + /a1-p/policytypes: get: - description: "Get a list of all registered policy type ids" tags: - - A1 Mediator - operationId: a1.get_all_policy_types + - A1 Mediator + description: Get a list of all registered policy type ids + operationId: a1_controller_get_all_policy_types responses: 200: - description: "list of all registered policy type ids" + description: list of all registered policy type ids content: application/json: schema: type: array items: - "$ref": "#/components/schemas/policy_type_id" - example: [20000, 20020] + $ref: '#/components/schemas/policy_type_id' + example: + - 20000 + - 20020 503: - description: "Potentially transient backend database error. Client should attempt to retry later." - - '/a1-p/policytypes/{policy_type_id}': - parameters: + description: Potentially transient backend database error. Client should + attempt to retry later. + content: {} + /a1-p/policytypes/{policy_type_id}: + get: + tags: + - A1 Mediator + description: | + Get this policy type + operationId: a1_controller_get_policy_type + parameters: - name: policy_type_id in: path + description: | + represents a policy type identifier. Currently this is restricted to an integer range. required: true schema: - "$ref": "#/components/schemas/policy_type_id" - get: - description: > - Get this policy type - tags: - - A1 Mediator - operationId: a1.get_policy_type + maximum: 2147483647 + minimum: 1 + type: integer responses: - '200': - description: "policy type successfully found" + 200: + description: policy type successfully found content: application/json: schema: - "$ref": "#/components/schemas/policy_type_schema" - '404': - description: > - policy type not found - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - delete: - description: > - Delete this policy type. Can only be performed if there are no instances of this type - tags: - - A1 Mediator - operationId: a1.delete_policy_type - responses: - '204': - description: > - policy type successfully deleted - '400': - description: > - Policy type cannot be deleted because there are instances - All instances must be removed before a policy type can be deleted - '404': - description: > + $ref: '#/components/schemas/policy_type_schema' + 404: + description: | policy type not found - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." + content: {} + 503: + description: Potentially transient backend database error. Client should + attempt to retry later. + content: {} put: - description: > - Create a new policy type . - Replace is not currently allowed; to replace, for now do a DELETE and then a PUT again. - tags: - - A1 Mediator - operationId: a1.create_policy_type + - A1 Mediator + description: | + Create a new policy type . Replace is not currently allowed; to replace, for now do a DELETE and then a PUT again. + operationId: a1_controller_create_policy_type + parameters: + - name: policy_type_id + in: path + description: | + represents a policy type identifier. Currently this is restricted to an integer range. + required: true + schema: + maximum: 2147483647 + minimum: 1 + type: integer requestBody: content: application/json: schema: - "$ref": "#/components/schemas/policy_type_schema" - example: - name: admission_control_policy - description: various parameters to control admission of dual connection - policy_type_id: 20000 - create_schema: - $schema: 'http://json-schema.org/draft-07/schema#' - type: object - properties: - enforce: - type: boolean - default: true - window_length: - type: integer - default: 1 - minimum: 1 - maximum: 60 - description: Sliding window length (in minutes) - blocking_rate: - type: number - default: 10 - minimum: 1 - maximum: 100 - description: '% Connections to block' - trigger_threshold: - type: integer - default: 10 - minimum: 1 - description: Minimum number of events in window to trigger blocking - additionalProperties: false - + $ref: '#/components/schemas/policy_type_schema' + required: false responses: - '201': - description: "policy type successfully created" - '400': - description: "illegal ID, or object already existed" - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - - '/a1-p/policytypes/{policy_type_id}/policies': - parameters: + 201: + description: policy type successfully created + content: {} + 400: + description: illegal ID, or object already existed + content: {} + 503: + description: Potentially transient backend database error. Client should + attempt to retry later. + content: {} + x-codegen-request-body-name: body + delete: + tags: + - A1 Mediator + description: | + Delete this policy type. Can only be performed if there are no instances of this type + operationId: a1_controller_delete_policy_type + parameters: - name: policy_type_id in: path + description: | + represents a policy type identifier. Currently this is restricted to an integer range. required: true schema: - "$ref": "#/components/schemas/policy_type_id" + maximum: 2147483647 + minimum: 1 + type: integer + responses: + 204: + description: | + policy type successfully deleted + content: {} + 400: + description: | + Policy type cannot be deleted because there are instances All instances must be removed before a policy type can be deleted + content: {} + 404: + description: | + policy type not found + content: {} + 503: + description: Potentially transient backend database error. Client should + attempt to retry later. + content: {} + /a1-p/policytypes/{policy_type_id}/policies: get: - description: "get a list of all policy instance ids for this policy type id" tags: - - A1 Mediator - operationId: a1.get_all_policy_identities + - A1 Mediator + description: get a list of all policy instance ids for this policy type id + operationId: a1_controller_get_all_instances_for_type + parameters: + - name: policy_type_id + in: path + description: | + represents a policy type identifier. Currently this is restricted to an integer range. + required: true + schema: + maximum: 2147483647 + minimum: 1 + type: integer responses: 200: - description: "list of all policy instance ids for this policy type id" + description: list of all policy instance ids for this policy type id content: application/json: schema: type: array items: - "$ref": "#/components/schemas/policy_instance_id" - example: ["3d2157af-6a8f-4a7c-810f-38c2f824bf12", "06911bfc-c127-444a-8eb1-1bffad27cc3d"] - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - - - '/a1-p/policytypes/{policy_type_id}/policies/{policy_instance_id}': - parameters: + $ref: '#/components/schemas/policy_instance_id' + example: + - 3d2157af-6a8f-4a7c-810f-38c2f824bf12 + - 06911bfc-c127-444a-8eb1-1bffad27cc3d + 503: + description: Potentially transient backend database error. Client should + attempt to retry later. + content: {} + /a1-p/policytypes/{policy_type_id}/policies/{policy_instance_id}: + get: + tags: + - A1 Mediator + description: | + Retrieve the policy instance + operationId: a1_controller_get_policy_instance + parameters: - name: policy_type_id in: path + description: | + represents a policy type identifier. Currently this is restricted to an integer range. required: true schema: - "$ref": "#/components/schemas/policy_type_id" - + maximum: 2147483647 + minimum: 1 + type: integer - name: policy_instance_id in: path + description: | + represents a policy instance identifier. UUIDs are advisable but can be any string required: true schema: - "$ref": "#/components/schemas/policy_instance_id" - - get: - description: > - Retrieve the policy instance - - tags: - - A1 Mediator - operationId: a1.get_policy_instance + type: string + - name: notificationDestination + in: query + description: | + URL send by non-RT RIC. This where non-RT RIC expects status updates on the policy creation + schema: + type: string responses: - '200': - description: > - The policy instance. - the schema of this object is defined by the create_schema field of the policy type + 200: + description: | + The policy instance. the schema of this object is defined by the create_schema field of the policy type content: application/json: schema: type: object - '404': - description: > + 404: + description: | there is no policy instance with this policy_instance_id or there is no policy type with this policy_type_id - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - - delete: - description: > - Delete this policy instance - - tags: - - A1 Mediator - operationId: a1.delete_policy_instance - responses: - '202': - description: > - policy instance deletion initiated - '404': - description: > - there is no policy instance with this policy_instance_id or there is no policy type with this policy_type_id - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - + content: {} + 503: + description: Potentially transient backend database error. Client should + attempt to retry later. + content: {} put: - description: > - Create or replace a policy instance of type policy_type_id. - The schema of the PUT body is defined by the create_schema field of the policy type. - tags: - - A1 Mediator - operationId: a1.create_or_replace_policy_instance + - A1 Mediator + description: | + Create or replace a policy instance of type policy_type_id. The schema of the PUT body is defined by the create_schema field of the policy type. + operationId: a1_controller_create_or_replace_policy_instance + parameters: + - name: policy_type_id + in: path + description: | + represents a policy type identifier. Currently this is restricted to an integer range. + required: true + schema: + maximum: 2147483647 + minimum: 1 + type: integer + - name: policy_instance_id + in: path + description: | + represents a policy instance identifier. UUIDs are advisable but can be any string + required: true + schema: + type: string + - name: notificationDestination + in: query + description: | + URL send by non-RT RIC. This where non-RT RIC expects status updates on the policy creation + schema: + type: string requestBody: content: application/json: schema: type: object - description: > - the schema of this object is defined by the create_schema field of the policy type - example: - enforce: true - window_length: 10 - blocking_rate: 20 - trigger_threshold: 10 - + description: | + the schema of this object is defined by the create_schema field of the policy type + required: false responses: - '202': - description: > + 202: + description: | Policy instance creation initiated - '400': - description: > + content: {} + 400: + description: | Bad PUT body for this policy instance - '404': - description: > + content: {} + 404: + description: | There is no policy type with this policy_type_id - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - - '/a1-p/policytypes/{policy_type_id}/policies/{policy_instance_id}/status': - parameters: + content: {} + 503: + description: Potentially transient backend database error. Client should + attempt to retry later. + content: {} + x-codegen-request-body-name: body + delete: + tags: + - A1 Mediator + description: | + Delete this policy instance + operationId: a1_controller_delete_policy_instance + parameters: - name: policy_type_id in: path + description: | + represents a policy type identifier. Currently this is restricted to an integer range. required: true schema: - "$ref": "#/components/schemas/policy_type_id" - + maximum: 2147483647 + minimum: 1 + type: integer - name: policy_instance_id in: path + description: | + represents a policy instance identifier. UUIDs are advisable but can be any string required: true schema: - "$ref": "#/components/schemas/policy_instance_id" - + type: string + - name: notificationDestination + in: query + description: | + URL send by non-RT RIC. This where non-RT RIC expects status updates on the policy creation + schema: + type: string + responses: + 202: + description: | + policy instance deletion initiated + content: {} + 404: + description: | + there is no policy instance with this policy_instance_id or there is no policy type with this policy_type_id + content: {} + 503: + description: Potentially transient backend database error. Client should + attempt to retry later. + content: {} + /a1-p/policytypes/{policy_type_id}/policies/{policy_instance_id}/status: get: - description: > - Retrieve the policy instance status across all handlers of the policy - If this endpoint returns successfully (200), it is either IN EFFECT or NOT IN EFFECT. - IN EFFECT is returned if at least one policy handler in the RIC is implementing the policy - NOT IN EFFECT is returned otherwise - If a policy instance is successfully deleted, this endpoint will return a 404 (not a 200) tags: - - A1 Mediator - operationId: a1.get_policy_instance_status + - A1 Mediator + description: | + Retrieve the policy instance status across all handlers of the policy If this endpoint returns successfully (200), + it is either ENFORCED or NOT_ENFORCED. ENFORCED is returned if at least one policy handler in the RIC is implementing + the policy. + NOT_ENFORCED is returned otherwise If a policy instance is successfully deleted, this endpoint will return a 404 (not a 200). + operationId: a1_controller_get_policy_instance_status + parameters: + - name: policy_type_id + in: path + description: | + represents a policy type identifier. Currently this is restricted to an integer range. + required: true + schema: + maximum: 2147483647 + minimum: 1 + type: integer + - name: policy_instance_id + in: path + description: | + represents a policy instance identifier. UUIDs are advisable but can be any string + required: true + schema: + type: string responses: - '200': - description: > + 200: + description: | successfully retrieved the status content: application/json: schema: type: object properties: - instance_status: + enforceStatus: type: string enum: - - IN EFFECT - - NOT IN EFFECT - has_been_deleted: - type: boolean - created_at: + - ENFORCED + - NOT_ENFORCED + enforceReason: type: string - format: date-time - - '404': - description: > + enum: + - SCOPE_NOT_APPLICABLE + - STATEMENT_NOT_APPLICABLE + - OTHER_REASON + 404: + description: | there is no policy instance with this policy_instance_id or there is no policy type with this policy_type_id - '503': - description: "Potentially transient backend database error. Client should attempt to retry later." - + content: {} + 503: + description: Potentially transient backend database error. Client should + attempt to retry later. + content: {} + /data-delivery: + post: + tags: + - A1 EI Data Delivery + description: | + Deliver data produced by data producer. + operationId: a1_controller_data_delivery + requestBody: + content: + application/json: + schema: + type: object + description: | + object to represent data object + required: false + responses: + 200: + description: | + successfully delivered data from data producer + content: {} + 404: + description: | + no job id defined for this data delivery + content: {} + x-codegen-request-body-name: body components: schemas: policy_type_schema: - type: object required: - - name + - create_schema - description + - name - policy_type_id - - create_schema - additionalProperties: false + type: object properties: name: type: string @@ -328,22 +427,22 @@ components: type: string description: description of the policy type policy_type_id: - description: the integer of the policy type type: integer + description: the integer of the policy type create_schema: type: object - description: > + properties: {} + description: | jsonschema (following http://json-schema.org/draft-07/schema) of the CREATE payload to be sent to handlers of this policy - policy_type_id: - description: > - represents a policy type identifier. Currently this is restricted to an integer range. - type: integer - minimum: 1 maximum: 2147483647 - + minimum: 1 + type: integer + description: | + represents a policy type identifier. Currently this is restricted to an integer range. policy_instance_id: - description: > - represents a policy instance identifier. UUIDs are advisable but can be any string type: string - example: "3d2157af-6a8f-4a7c-810f-38c2f824bf12" + description: | + represents a policy instance identifier. UUIDs are advisable but can be any string + example: 3d2157af-6a8f-4a7c-810f-38c2f824bf12 +x-components: {} diff --git a/near-rt-ric-simulator/src/OSC_2.1.0/__init__.py b/near-rt-ric-simulator/src/OSC_2.1.0/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/near-rt-ric-simulator/src/OSC_2.1.0/controllers/__init__.py b/near-rt-ric-simulator/src/OSC_2.1.0/controllers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/near-rt-ric-simulator/src/OSC_2.1.0/a1.py b/near-rt-ric-simulator/src/OSC_2.1.0/controllers/a1_mediator_controller.py similarity index 78% rename from near-rt-ric-simulator/src/OSC_2.1.0/a1.py rename to near-rt-ric-simulator/src/OSC_2.1.0/controllers/a1_mediator_controller.py index b343e30..aec84c5 100644 --- a/near-rt-ric-simulator/src/OSC_2.1.0/a1.py +++ b/near-rt-ric-simulator/src/OSC_2.1.0/controllers/a1_mediator_controller.py @@ -1,5 +1,5 @@ # ============LICENSE_START=============================================== -# Copyright (C) 2021 Nordix Foundation. All rights reserved. +# Copyright (C) 2021-2023 Nordix Foundation. All rights reserved. # ======================================================================== # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,21 +16,20 @@ # import json -import datetime +# import datetime import time from datetime import datetime -from connexion import NoContent -from flask import Flask, request, Response +from flask import request, Response from jsonschema import validate -from var_declaration import policy_instances, policy_types, policy_status, policy_fingerprint, forced_settings, hosts_set +from var_declaration import policy_instances, policy_types, policy_status, policy_fingerprint, callbacks, forced_settings, hosts_set, jobs, data_delivery +from models.enforceStatus import EnforceStatus from utils import calcFingerprint from maincommon import extract_host_name, is_duplicate_check from payload_logging import is_payload_logging -#Constsants -APPL_JSON='application/json' - +# Constants +APPL_JSON = 'application/json' #Helper funtion to log http reponse def log_resp_text(msg): @@ -40,7 +39,7 @@ def log_resp_text(msg): print(str(msg)) # API Function: Health check -def get_healthcheck(): +def a1_controller_get_healthcheck(): extract_host_name(hosts_set, request) @@ -50,8 +49,7 @@ def get_healthcheck(): return (None, 200) # API Function: Get all policy type ids -def get_all_policy_types(): - +def a1_controller_get_all_policy_types(): extract_host_name(hosts_set, request) if ((r := check_modified_response()) is not None): @@ -62,8 +60,7 @@ def get_all_policy_types(): return (res, 200) # API Function: Get a policy type -def get_policy_type(policy_type_id): - +def a1_controller_get_policy_type(policy_type_id): extract_host_name(hosts_set, request) if ((r := check_modified_response()) is not None): @@ -78,7 +75,7 @@ def get_policy_type(policy_type_id): return Response(json.dumps(policy_types[policy_type_id]), 200, mimetype=APPL_JSON) # API Function: Delete a policy type -def delete_policy_type(policy_type_id): +def a1_controller_delete_policy_type(policy_type_id): extract_host_name(hosts_set, request) @@ -102,7 +99,7 @@ def delete_policy_type(policy_type_id): # API Function: Create a policy type -def create_policy_type(policy_type_id): +def a1_controller_create_policy_type(policy_type_id): extract_host_name(hosts_set, request) @@ -141,7 +138,7 @@ def create_policy_type(policy_type_id): # API Function: Get all policy ids for a type -def get_all_policy_identities(policy_type_id): +def a1_controller_get_all_instances_for_type(policy_type_id): extract_host_name(hosts_set, request) @@ -156,7 +153,7 @@ def get_all_policy_identities(policy_type_id): return (list(policy_instances[policy_type_id].keys()), 200) # API Function: Get a policy instance -def get_policy_instance(policy_type_id, policy_instance_id): +def a1_controller_get_policy_instance(policy_type_id, policy_instance_id): extract_host_name(hosts_set, request) @@ -176,7 +173,7 @@ def get_policy_instance(policy_type_id, policy_instance_id): return Response(json.dumps(policy_instances[policy_type_id][policy_instance_id]), 200, mimetype=APPL_JSON) # API function: Delete a policy -def delete_policy_instance(policy_type_id, policy_instance_id): +def a1_controller_delete_policy_instance(policy_type_id, policy_instance_id): extract_host_name(hosts_set, request) @@ -201,11 +198,13 @@ def delete_policy_instance(policy_type_id, policy_instance_id): del policy_fingerprint[fp_previous] del policy_instances[policy_type_id][policy_instance_id] del policy_status[policy_instance_id] + callbacks.pop(policy_instance_id) return (None, 202) + # API function: Create/update a policy -def create_or_replace_policy_instance(policy_type_id, policy_instance_id): +def a1_controller_create_or_replace_policy_instance(policy_type_id, policy_instance_id): extract_host_name(hosts_set, request) @@ -243,33 +242,35 @@ def create_or_replace_policy_instance(policy_type_id, policy_instance_id): log_resp_text("Policy id already exist for other type") return (None, 400) - if (is_duplicate_check()): - fp=calcFingerprint(data, policy_type_id) + if is_duplicate_check(): + fp = calcFingerprint(data, policy_type_id) else: - fp=policy_instance_id + fp = policy_instance_id - if ((fp in policy_fingerprint.keys()) and is_duplicate_check()): - p_id=policy_fingerprint[fp] + if (fp in policy_fingerprint.keys()) and is_duplicate_check(): + p_id = policy_fingerprint[fp] if (p_id != policy_instance_id): log_resp_text("Policy json duplicate of other instance") return (None, 400) - if (fp_previous is not None): + if fp_previous is not None: del policy_fingerprint[fp_previous] policy_fingerprint[fp]=policy_instance_id + noti = request.args.get('notificationDestination') + callbacks[policy_instance_id] = noti + policy_instances[policy_type_id][policy_instance_id]=data - ps={} - ps["instance_status"] = "NOT IN EFFECT" - ps["has_been_deleted"] = "false" - ps["created_at"] = str(datetime.now().strftime("%m/%d/%Y, %H:%M:%S")) - policy_status[policy_instance_id]=ps + enforceStatus = EnforceStatus("NOT_ENFORCED", "OTHER_REASON") + policy_status[policy_instance_id] = enforceStatus.to_dict() + + # return Response(json.dumps(data), 200, mimetype=APPL_JSON) return (None, 202) # API function: Get policy status -def get_policy_instance_status(policy_type_id, policy_instance_id): +def a1_controller_get_policy_instance_status(policy_type_id, policy_instance_id): extract_host_name(hosts_set, request) @@ -287,6 +288,27 @@ def get_policy_instance_status(policy_type_id, policy_instance_id): return Response(json.dumps(policy_status[policy_instance_id]), 200, mimetype=APPL_JSON) +# API function: Receive a data delivery package +def a1_controller_data_delivery(): + + extract_host_name(hosts_set, request) + if ((r := check_modified_response()) is not None): + return r + + try: + data = request.data + data = json.loads(data) + job = data['job'] + jobs.index(job) + except ValueError: + log_resp_text("no job id defined for this data delivery") + return (None, 404) + except Exception: + log_resp_text("The data is corrupt or missing.") + return (None, 400) + data_delivery.append(data) + return (None, 200) # Should A1 and the A1 Simulator return 201 for creating a new resource? + # Helper: Create a response object if forced http response code is set def get_forced_response(): diff --git a/near-rt-ric-simulator/src/OSC_2.1.0/main.py b/near-rt-ric-simulator/src/OSC_2.1.0/main.py index 18bc03b..736438e 100644 --- a/near-rt-ric-simulator/src/OSC_2.1.0/main.py +++ b/near-rt-ric-simulator/src/OSC_2.1.0/main.py @@ -1,5 +1,5 @@ # ============LICENSE_START=============================================== -# Copyright (C) 2021 Nordix Foundation. All rights reserved. +# Copyright (C) 2021-2023 Nordix Foundation. All rights reserved. # ======================================================================== # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,32 +15,31 @@ # ============LICENSE_END================================================= # -import connexion import json import sys import os import requests +from connexion.resolver import RelativeResolver from pathlib import Path -from flask import Flask, escape, request, Response, jsonify -from jsonschema import validate -from var_declaration import policy_instances, policy_types, policy_status, policy_fingerprint, forced_settings, hosts_set, app -from maincommon import check_apipath, apipath, get_supported_interfaces_response, extract_host_name +from flask import request, Response, jsonify +from var_declaration import policy_instances, policy_types, policy_status, callbacks, policy_fingerprint, forced_settings, hosts_set, app, data_delivery +from models.enforceStatus import EnforceStatus +from maincommon import check_apipath, get_supported_interfaces_response from time import sleep -#Constants -TEXT_PLAIN='text/plain' + +# Constants +TEXT_PLAIN = 'text/plain' +APPL_JSON = 'application/json' check_apipath() # app is created in var_declarations -import payload_logging # app var need to be initialized - -#Check alive function +# Check alive function @app.route('/', methods=['GET']) def test(): - return Response("OK", 200, mimetype=TEXT_PLAIN) @app.route('/ip', methods=['GET']) @@ -53,17 +52,16 @@ def get_ip(): #Return the current and all supported yamls for the this container @app.route('/container_interfaces', methods=['GET']) def container_interfaces(): - return get_supported_interfaces_response() #Delete all created instances and status @app.route('/deleteinstances', methods=['POST']) def deleteinstances(): - for i in policy_instances.keys(): policy_instances[i]={} policy_status.clear() + callbacks.clear() forced_settings.clear() forced_settings['code']=None forced_settings['delay']=None @@ -73,13 +71,14 @@ def deleteinstances(): #Delete all - all reset @app.route('/deleteall', methods=['POST']) def deleteall(): - policy_instances.clear() policy_types.clear() policy_status.clear() + callbacks.clear() forced_settings['code']=None forced_settings['delay']=None policy_fingerprint.clear() + data_delivery.clear() return Response("All policy instances and types deleted", 200, mimetype=TEXT_PLAIN) #Load a policy type @@ -138,7 +137,6 @@ def del_policytype(): # Get all policy type ids @app.route('/policytypes', methods=['GET']) def get_policytype_ids(): - return (json.dumps(list(policy_instances.keys())), 200) #Set force response for one A1 response @@ -164,36 +162,40 @@ def forcedelay(): return Response("Force delay: " + str(forced_settings['delay']) + " sec set for all A1 responses", 200, mimetype=TEXT_PLAIN) -#Set status and reason -#/status?policyid=&status=[&deleted=][&created_at=] +# Set status and reason +#/status?policyid=&status=[&reason=] @app.route('/status', methods=['PUT']) def setstatus(): policy_id=request.args.get('policyid') if (policy_id is None): return Response('Parameter missing in request', status=400, mimetype=TEXT_PLAIN) - if policy_id not in policy_status.keys(): return Response('Policyid: '+policy_id+' not found.', status=404, mimetype=TEXT_PLAIN) + status=request.args.get('status') if (status is None): return Response('Parameter missing in request', status=400, mimetype=TEXT_PLAIN) - policy_status[policy_id]["instance_status"]=status - msg = "Status set to "+status - deleted_policy=request.args.get('deleted') - if (deleted_policy is not None): - policy_status[policy_id]["has_been_deleted"]=deleted_policy - msg = msg + " and has_been_deleted set to "+deleted_policy - created_at = request.args.get('created_at') - if (created_at is not None): - policy_status[policy_id]["created_at"]=created_at - msg = msg + " and created_at set to "+created_at - msg=msg + " for policy: " + policy_id - return Response(msg, 200, mimetype=TEXT_PLAIN) + enforceStatus = EnforceStatus() + try: + enforceStatus.enforce_status = status + msg = "Status set to " + status + + reason = request.args.get('reason') + if (reason is not None): + enforceStatus.enforce_reason = reason + msg = msg + " and " + reason + + policy_status[policy_id] = enforceStatus.to_dict() + msg = msg + " for policy: " + policy_id + except ValueError as error: + return Response(str(error), status=400, mimetype=TEXT_PLAIN) + + return Response(msg, 200, mimetype=TEXT_PLAIN) -#Metrics function -#Get a named counter +# Metrics function +# Get a named counter @app.route('/counter/', methods=['GET']) def getcounter(countername): @@ -209,7 +211,8 @@ def getcounter(countername): hosts=",".join(hosts_set) return str(hosts),200 elif (countername == "datadelivery"): - return Response(str(0),200, mimetype=TEXT_PLAIN) + data_delivery_counter = str(len(data_delivery)) + return Response(data_delivery_counter,200, mimetype=TEXT_PLAIN) else: return Response("Counter name: "+countername+" not found.",404, mimetype=TEXT_PLAIN) @@ -218,7 +221,49 @@ if len(sys.argv) >= 2 : if isinstance(sys.argv[1], int): port_number = sys.argv[1] -app.add_api('openapi.yaml') + +# Send status +# /sendstatus?policyid= +@app.route('/sendstatus', methods=['POST']) +def sendstatus(): + policyid = request.args.get('policyid') + if policyid is None: + return Response('Parameter missing in request', status=400, mimetype=TEXT_PLAIN) + if policyid not in policy_status.keys(): + return Response('Policyid: '+policyid+' not found.', status=404, mimetype=TEXT_PLAIN) + + ps = policy_status[ policyid ] + cb_url = callbacks[ policyid ] + + try: + resp = requests.post(cb_url, json = json.dumps(ps), headers = { "Content-Type": APPL_JSON, "Accept": "*/*" }) + resp.raise_for_status() + if (resp.status_code >= 200 and resp.status_code <= 300): + return Response("OK", resp.status_code, mimetype = TEXT_PLAIN) + return Response('Post status failed', status = resp.status_code, mimetype = TEXT_PLAIN) + + except requests.ConnectionError as error: + return Response('Post status failed with Connection Error, could not send to ' + str(cb_url), status = 502, mimetype = TEXT_PLAIN) + except requests.Timeout as error: + return Response('Post status failed with Timeout, could not send to ' + str(cb_url), status = 504, mimetype = TEXT_PLAIN) + except requests.HTTPError as error: + return Response('Post status failed with HTTP Error, could not send to ' + str(cb_url), status = 502, mimetype = TEXT_PLAIN) + except requests.RequestException as error: + return Response('Post status failed with RequestException, could not send to ' + str(cb_url), status = 500, mimetype = TEXT_PLAIN) + +# Receive status (only for testing callbacks) +# /statustest +@app.route('/statustest', methods=['POST', 'PUT']) +def statustest(): + try: + data = request.data + data = json.loads(data) + except Exception: + return Response("The status data is corrupt or missing.", 400, mimetype=TEXT_PLAIN) + + return Response("OK", 201, mimetype=TEXT_PLAIN) + +app.add_api('openapi.yaml', resolver=RelativeResolver('controllers.a1_mediator_controller')) if __name__ == '__main__': - app.run(port=port_number, host="127.0.0.1", threaded=False) \ No newline at end of file + app.run(port=port_number, host="127.0.0.1", threaded=True) \ No newline at end of file diff --git a/near-rt-ric-simulator/src/OSC_2.1.0/models/enforceStatus.py b/near-rt-ric-simulator/src/OSC_2.1.0/models/enforceStatus.py new file mode 100644 index 0000000..89fafd9 --- /dev/null +++ b/near-rt-ric-simulator/src/OSC_2.1.0/models/enforceStatus.py @@ -0,0 +1,81 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + + +class EnforceStatus(): + + def __init__(self, enforce_status: str=None, enforce_reason: str=None): # noqa: E501 + """EnforceStatus + + :param enforce_status: The enforce_status of this EnforceStatus. # noqa: E501 + :type enforce_status: str + :param enforce_reason: The enforce_reason of this EnforceStatus. # noqa: E501 + :type enforce_reason: str + """ + self._enforce_status = enforce_status + self._enforce_reason = enforce_reason + + @property + def enforce_status(self) -> str: + """Gets the enforce_status of this EnforceStatus. + + :return: The enforce_status of this EnforceStatus. + :rtype: str + """ + return self._enforce_status + + @enforce_status.setter + def enforce_status(self, enforce_status: str): + """Sets the enforce_status of this EnforceStatus. + + :param enforce_status: The enforce_status of this EnforceStatus. + :type enforce_status: str + """ + allowed_values = ["ENFORCED", "NOT_ENFORCED"] # noqa: E501 + if enforce_status not in allowed_values: + raise ValueError( + "Invalid value for `enforce_status` ({0}), must be one of {1}" + .format(enforce_status, allowed_values) + ) + + self._enforce_status = enforce_status + + @property + def enforce_reason(self) -> str: + """Gets the enforce_reason of this EnforceStatus. + + :return: The enforce_reason of this EnforceStatus. + :rtype: str + """ + return self._enforce_reason + + @enforce_reason.setter + def enforce_reason(self, enforce_reason: str): + """Sets the enforce_reason of this EnforceStatus. + + :param enforce_reason: The enforce_reason of this EnforceStatus. + :type enforce_reason: str + """ + allowed_values = ["SCOPE_NOT_APPLICABLE", "STATEMENT_NOT_APPLICABLE", "OTHER_REASON"] # noqa: E501 + if enforce_reason not in allowed_values: + raise ValueError( + "Invalid value for `enforce_reason` ({0}), must be one of {1}" + .format(enforce_reason, allowed_values) + ) + + self._enforce_reason = enforce_reason + + def to_dict(self): + """Returns the model properties as a dict + + :rtype: dict + """ + result = { + 'enforceStatus': self._enforce_status, + 'enforceReason': self._enforce_reason + } + return result diff --git a/near-rt-ric-simulator/src/OSC_2.1.0/var_declaration.py b/near-rt-ric-simulator/src/OSC_2.1.0/var_declaration.py index f4ff6ac..f78ca50 100644 --- a/near-rt-ric-simulator/src/OSC_2.1.0/var_declaration.py +++ b/near-rt-ric-simulator/src/OSC_2.1.0/var_declaration.py @@ -1,5 +1,5 @@ # ============LICENSE_START=============================================== -# Copyright (C) 2021 Nordix Foundation. All rights reserved. +# Copyright (C) 2021-2023 Nordix Foundation. All rights reserved. # ======================================================================== # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,14 +18,22 @@ from maincommon import apipath import connexion -#Main app -app = connexion.FlaskApp(__name__, specification_dir=apipath) +# Main app +def create_app(): + app = connexion.FlaskApp(__name__, specification_dir=apipath) + return app -policy_types={} -policy_instances={} -policy_status={} -forced_settings={} -forced_settings['code']=None -forced_settings['delay']=None -policy_fingerprint={} -hosts_set=set() +app = create_app() + +policy_types = {} +policy_instances = {} +policy_status = {} +callbacks = {} +forced_settings = {} +forced_settings['code'] = None +forced_settings['delay'] = None +policy_fingerprint = {} +hosts_set = set() + +data_delivery = [] +jobs = ['100', '101'] diff --git a/near-rt-ric-simulator/src/STD_2.0.0/main.py b/near-rt-ric-simulator/src/STD_2.0.0/main.py index 16e4a31..9733a0f 100644 --- a/near-rt-ric-simulator/src/STD_2.0.0/main.py +++ b/near-rt-ric-simulator/src/STD_2.0.0/main.py @@ -28,8 +28,10 @@ from jsonschema import validate from var_declaration import policy_instances, policy_types, policy_status, callbacks, forced_settings, policy_fingerprint, hosts_set, data_delivery_counter, app from maincommon import check_apipath, apipath, get_supported_interfaces_response, extract_host_name -#Constants +# Constants TEXT_PLAIN='text/plain' +APPL_JSON='application/json' + check_apipath() @@ -198,7 +200,7 @@ def sendstatus(): return Response('Post status failed with code: '+resp.status_code, status=500, mimetype=TEXT_PLAIN) data = resp.json() - return Response(data, 200, mimetype='application/json') + return Response(data, 200, mimetype=APPL_JSON) #Receive status (only for testing callbacks) #/statustest @@ -210,7 +212,7 @@ def statustest(): except Exception: return Response("The status data is corrupt or missing.", 400, mimetype=TEXT_PLAIN) - return Response(json.dumps(data), 200, mimetype='application/json') + return Response(json.dumps(data), 200, mimetype=APPL_JSON) #Receive a data delivery package #/datadelivery diff --git a/near-rt-ric-simulator/src/common/maincommon.py b/near-rt-ric-simulator/src/common/maincommon.py index 131bf0d..6239f79 100644 --- a/near-rt-ric-simulator/src/common/maincommon.py +++ b/near-rt-ric-simulator/src/common/maincommon.py @@ -22,9 +22,9 @@ from flask import Response import socket import ssl -#Must exist +# Must exist apipath=os.environ['APIPATH'] -#May exist +# May exist remote_hosts_logging=os.getenv('REMOTE_HOSTS_LOGGING') duplicate_check=os.getenv('DUPLICATE_CHECK') diff --git a/near-rt-ric-simulator/src/start.sh b/near-rt-ric-simulator/src/start.sh index 8518541..ad5c677 100755 --- a/near-rt-ric-simulator/src/start.sh +++ b/near-rt-ric-simulator/src/start.sh @@ -25,27 +25,27 @@ if [ $# -ne 1 ]; then fi echo "Version folder for simulator: "$1 -#Set path to open api +# Set path to open api export APIPATH=$PWD/api/$1 echo "APIPATH set to: "$APIPATH cd src -#Include common module(s) +# Include common module(s) export PYTHONPATH=$PWD/common echo "PYTHONPATH set to: "$PYTHONPATH cd $1 -#start nginx +# start nginx nginx -c /usr/src/app/nginx.conf -#start callBack server +start callBack server if [[ ${A1_VERSION} == "STD"* ]]; then echo "Path to callBack.py: "$PWD python -u callBack.py & fi -#start near-rt-ric-simulator +# start near-rt-ric-simulator echo "Path to main.py: "$PWD python -u main.py diff --git a/near-rt-ric-simulator/test/OSC_2.1.0/basic_test.sh b/near-rt-ric-simulator/test/OSC_2.1.0/basic_test.sh index 2f70295..f97d945 100755 --- a/near-rt-ric-simulator/test/OSC_2.1.0/basic_test.sh +++ b/near-rt-ric-simulator/test/OSC_2.1.0/basic_test.sh @@ -115,11 +115,16 @@ do_curl GET '/a1-p/policytypes/1/policies' 200 echo "=== API: Create policy instance pi1 of type: 1 ===" RESULT="" +# res=$(cat jsonfiles/pi1.json) +# RESULT="json:$res" do_curl PUT '/a1-p/policytypes/1/policies/pi1' 202 jsonfiles/pi1.json echo "=== API: Update policy instance pi1 of type: 1 ===" RESULT="" do_curl PUT '/a1-p/policytypes/1/policies/pi1' 202 jsonfiles/pi1.json +# res=$(cat jsonfiles/pi1.json) +# RESULT="json:$res" +# do_curl PUT '/a1-p/policytypes/1/policies/pi1' 200 jsonfiles/pi1.json echo "=== API: Load a policy type: 1, shall fail ===" RESULT="" @@ -137,6 +142,9 @@ else echo "=== API: Create policy instance pi2 (copy of pi1) of type: 1. Shall succeed ===" RESULT="" do_curl PUT '/a1-p/policytypes/1/policies/pi2' 202 jsonfiles/pi1.json + # res=$(cat jsonfiles/pi1.json) + # RESULT="json:$res" + # do_curl PUT '/a1-p/policytypes/1/policies/pi2' 200 jsonfiles/pi1.json echo "=== Delete policy instance: pi2 ===" RESULT="" @@ -152,7 +160,7 @@ RESULT="" do_curl GET '/a1-p/policytypes/1' 401 echo "=== API: Get policy status ===" -RESULT="json:{\"instance_status\": \"NOT IN EFFECT\", \"has_been_deleted\": \"false\", \"created_at\": \"????\"}" +RESULT="json:{\"enforceStatus\": \"NOT_ENFORCED\", \"enforceReason\": \"OTHER_REASON\"}" do_curl GET '/a1-p/policytypes/1/policies/pi1/status' 200 echo "=== Load a policy type: 2 ===" @@ -206,11 +214,22 @@ do_curl PUT '/a1-p/policytypes/2/policies/pi2' 400 jsonfiles/pi2_missing_param.j echo "=== API: Create policy instance pi2 of type: 2 ===" RESULT="" do_curl PUT '/a1-p/policytypes/2/policies/pi2' 202 jsonfiles/pi2.json +# res=$(cat jsonfiles/pi2.json) +# RESULT="json:$res" +# do_curl PUT '/a1-p/policytypes/2/policies/pi2' 200 jsonfiles/pi2.json echo "=== API: Update policy instance pi2 of type: 2 ===" RESULT="" do_curl PUT '/a1-p/policytypes/2/policies/pi2' 202 jsonfiles/pi2.json +echo "=== API: Update policy instance pi2 of type: 2 ===" +RESULT="" +do_curl PUT "/a1-p/policytypes/2/policies/pi2?notificationDestination=http://localhost:${PORT}/statustest" 202 jsonfiles/pi2.json + +echo "=== Send status for pi2===" +RESULT="OK" +do_curl POST '/sendstatus?policyid=pi2' 201 jsonfiles/pi2.json + echo "=== API: Get instances for type 1, shall contain pi1 ===" RESULT="json:[ \"pi1\" ]" do_curl GET '/a1-p/policytypes/1/policies' jsonfiles/200 @@ -227,6 +246,9 @@ else echo "=== API: Create policy instance pi11 (copy of pi1) of type: 1. Shall succeed ===" RESULT="" do_curl PUT '/a1-p/policytypes/1/policies/pi11' 202 jsonfiles/pi1.json + # res=$(cat jsonfiles/pi1.json) + # RESULT="json:$res" + # do_curl PUT '/a1-p/policytypes/1/policies/pi11' 200 jsonfiles/pi1.json echo "=== Delete policy instance: pi11 ===" RESULT="" @@ -241,13 +263,15 @@ else echo "=== API: Create policy instance pi3 (copy of pi1) of type: 1. Shall succeed ===" RESULT="" do_curl PUT '/a1-p/policytypes/1/policies/pi3' 202 jsonfiles/pi1.json + # res=$(cat jsonfiles/pi1.json) + # RESULT="json:$res" + # do_curl PUT '/a1-p/policytypes/1/policies/pi3' 200 jsonfiles/pi1.json echo "=== Delete policy instance: pi3 ===" RESULT="" do_curl DELETE '/a1-p/policytypes/1/policies/pi3' 202 fi - echo "=== API: Get instances for type 1, shall contain pi1 ===" RESULT="json:[ \"pi1\" ]" do_curl GET '/a1-p/policytypes/1/policies' jsonfiles/200 @@ -256,8 +280,6 @@ echo "=== API: Get instances for type 2, shall contain pi2 ===" RESULT="json:[ \"pi2\" ]" do_curl GET '/a1-p/policytypes/2/policies' 200 - - echo "=== Set force response code 401. ===" RESULT="*" do_curl POST '/forceresponse?code=401' 200 @@ -271,7 +293,7 @@ RESULT="Force delay: 10 sec set for all A1 responses" do_curl POST '/forcedelay?delay=10' 200 echo "=== API: Get policy status for pi1. Shall delay 10 sec ===" -RESULT="json:{\"instance_status\": \"NOT IN EFFECT\", \"has_been_deleted\": \"false\", \"created_at\": \"????\"}" +RESULT="json:{\"enforceStatus\": \"NOT_ENFORCED\", \"enforceReason\": \"OTHER_REASON\"}" do_curl GET '/a1-p/policytypes/1/policies/pi1/status' 200 echo "=== Reset force delay. ===" @@ -279,19 +301,19 @@ RESULT="Force delay: None sec set for all A1 responses" do_curl POST '/forcedelay' 200 echo "=== Set status for pi1 ===" -RESULT="Status set to IN EFFECT for policy: pi1" -do_curl PUT '/status?policyid=pi1&status=IN%20EFFECT' 200 +RESULT="Status set to ENFORCED for policy: pi1" +do_curl PUT '/status?policyid=pi1&status=ENFORCED' 200 echo "=== API: Get policy status for pi1 ===" -RESULT="json:{\"instance_status\": \"IN EFFECT\", \"has_been_deleted\": \"false\", \"created_at\": \"????\"}" +RESULT="json:{\"enforceStatus\": \"ENFORCED\", \"enforceReason\": null}" do_curl GET '/a1-p/policytypes/1/policies/pi1/status' 200 echo "=== Set status for pi1 ===" -RESULT="Status set to IN EFFECT and has_been_deleted set to true and created_at set to 2020-03-30 12:00:00 for policy: pi1" -do_curl PUT '/status?policyid=pi1&status=IN%20EFFECT&deleted=true&created_at=2020-03-30%2012:00:00' 200 +RESULT="Status set to ENFORCED for policy: pi1" +do_curl PUT '/status?policyid=pi1&status=ENFORCED' 200 echo "=== API: Get policy status for pi1 ===" -RESULT="json:{\"instance_status\": \"IN EFFECT\", \"has_been_deleted\": \"true\", \"created_at\": \"2020-03-30 12:00:00\"}" +RESULT="json:{\"enforceStatus\": \"ENFORCED\", \"enforceReason\": null}" do_curl GET '/a1-p/policytypes/1/policies/pi1/status' 200 echo "=== Get counter: instances ===" @@ -326,6 +348,10 @@ echo "=== Get counter: instances ===" RESULT="1" do_curl GET /counter/num_instances 200 +echo "=== Get counter: datadelivery ===" +RESULT="0" +do_curl GET /counter/datadelivery 200 + echo "********************" echo "*** All tests ok ***" echo "********************" diff --git a/near-rt-ric-simulator/tests/test_osc_2_1_0.py b/near-rt-ric-simulator/tests/test_osc_2_1_0.py index deff757..5c9b780 100644 --- a/near-rt-ric-simulator/tests/test_osc_2_1_0.py +++ b/near-rt-ric-simulator/tests/test_osc_2_1_0.py @@ -1,5 +1,5 @@ # ============LICENSE_START=============================================== -# Copyright (C) 2021 Nordix Foundation. All rights reserved. +# Copyright (C) 2021-2023 Nordix Foundation. All rights reserved. # ======================================================================== # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,19 +15,59 @@ # ============LICENSE_END================================================= # -# This test case test the OSC_2.1.0 version of the simulator +# This test case tests the OSC_2.1.0 version of the simulator -import json -#Version of simulator +# Version of simulator INTERFACE_VERSION="OSC_2.1.0" -from unittest_setup import SERVER_URL, setup_env, get_testdata_dir, client +import json +import pytest +import requests +import threading +from unittest_setup import SERVER_URL, PORT_NUMBER, setup_env, get_testdata_dir, client +# from unittest_setup import run_flask_app -#Setup env and import paths +# Setup env and import paths setup_env(INTERFACE_VERSION) from compare_json import compare +from models.enforceStatus import EnforceStatus + +def test_enforce_reason(client): + """ + Test that we can set a valid enforce status and reason, and that we reject invalid cases. + """ + enforceStatus = EnforceStatus() + + enforceStatus.enforce_status = 'NOT_ENFORCED' + enforceStatus.enforce_reason = 'SCOPE_NOT_APPLICABLE' + enforce_dict = enforceStatus.to_dict() + assert enforce_dict['enforceStatus'] == 'NOT_ENFORCED' + assert enforce_dict['enforceReason'] == 'SCOPE_NOT_APPLICABLE' + + enforceStatus.enforce_status = 'ENFORCED' + enforceStatus.enforce_reason = 'STATEMENT_NOT_APPLICABLE' + enforce_dict = enforceStatus.to_dict() + assert enforce_dict['enforceStatus'] == 'ENFORCED' + assert enforce_dict['enforceReason'] == 'STATEMENT_NOT_APPLICABLE' + + enforceStatus.enforce_reason = 'OTHER_REASON' + enforce_dict = enforceStatus.to_dict() + assert enforce_dict['enforceReason'] == 'OTHER_REASON' + + enforce_status = enforceStatus.enforce_status + assert str(enforce_status) == 'ENFORCED' + + enforce_reason = enforceStatus.enforce_reason + assert str(enforce_reason) == 'OTHER_REASON' + + with pytest.raises(ValueError): + enforceStatus.enforce_status = 'ERROR' + + with pytest.raises(ValueError): + enforceStatus.enforce_reason = 'ERROR' + def test_apis(client): @@ -176,9 +216,8 @@ def test_apis(client): # API: Get policy status policy_status = { - "instance_status" : "NOT IN EFFECT", - "has_been_deleted" : "false", - "created_at" : "????" + "enforceStatus" : "NOT_ENFORCED", + "enforceReason" : "OTHER_REASON", } response=client.get(SERVER_URL+'a1-p/policytypes/1/policies/pi1/status') assert response.status_code == 200 @@ -315,9 +354,8 @@ def test_apis(client): # API: Get policy status for pi1. Shall delay 10 sec policy_status = { - "instance_status" : "NOT IN EFFECT", - "has_been_deleted" : "false", - "created_at" : "????" + "enforceStatus" : "NOT_ENFORCED", + "enforceReason" : "OTHER_REASON", } response=client.get(SERVER_URL+'a1-p/policytypes/1/policies/pi1/status') assert response.status_code == 200 @@ -331,14 +369,13 @@ def test_apis(client): assert response.data == b"Force delay: None sec set for all A1 responses" # Set status for pi1 - response=client.put(SERVER_URL+'status?policyid=pi1&status=IN%20EFFECT') + response=client.put(SERVER_URL+'status?policyid=pi1&status=ENFORCED') assert response.status_code == 200 # API: Get policy status for pi1 policy_status = { - "instance_status" : "IN EFFECT", - "has_been_deleted" : "false", - "created_at" : "????" + "enforceStatus" : "ENFORCED", + "enforceReason" : None, } response=client.get(SERVER_URL+'a1-p/policytypes/1/policies/pi1/status') assert response.status_code == 200 @@ -347,14 +384,13 @@ def test_apis(client): assert res == True # Set status for pi1 - response=client.put(SERVER_URL+'status?policyid=pi1&status=IN%20EFFECT&deleted=true&created_at=2020-03-30%2012:00:00') + response=client.put(SERVER_URL+'status?policyid=pi1&status=NOT_ENFORCED&reason=SCOPE_NOT_APPLICABLE') assert response.status_code == 200 # API: Get policy status for pi1 policy_status = { - "instance_status" : "IN EFFECT", - "has_been_deleted" : "true", - "created_at" : "????" + "enforceStatus" : "NOT_ENFORCED", + "enforceReason" : "SCOPE_NOT_APPLICABLE", } response=client.get(SERVER_URL+'a1-p/policytypes/1/policies/pi1/status') assert response.status_code == 200 @@ -362,7 +398,7 @@ def test_apis(client): res=compare(policy_status, result) assert res == True - # Get counter: intstance + # Get counter: num_instances response=client.get(SERVER_URL+'counter/num_instances') assert response.status_code == 200 assert response.data == b"2" @@ -495,14 +531,108 @@ def test_apis(client): response=client.get(SERVER_URL+'a1-p/policytypes/1/policies/pi111/status') assert response.status_code == 404 - # Load policy type, no type in url - shall faill + # Load policy type, no type in url - shall fail with open(testdata+'pt2.json') as json_file: policytype_2 = json.load(json_file) response=client.put(SERVER_URL+'policytype', headers=header, data=json.dumps(policytype_2)) assert response.status_code == 400 - # Load policy type - duplicatee - shall faill + # Load policy type - duplicatee - shall fail with open(testdata+'pt1.json') as json_file: policytype_1 = json.load(json_file) response=client.put(SERVER_URL+'policytype?id=2', headers=header, data=json.dumps(policytype_1)) assert response.status_code == 400 + + # Get counter: data_delivery + response=client.get(SERVER_URL+'counter/datadelivery') + assert response.status_code == 200 + assert response.data == b"0" + + # Send data to data-delivery with empty payload + json_payload={} + response=client.post(SERVER_URL+'data-delivery', headers=header, data=json.dumps(json_payload)) + assert response.status_code == 400 + + # Send invalid data to data-delivery + json_payload={ + "job":"200", + "payload":"payload" + } + response=client.post(SERVER_URL+'data-delivery', headers=header, data=json.dumps(json_payload)) + assert response.status_code == 404 + + # Send data to data-delivery with valid job + json_payload={ + "job":"100", + "payload":"payload" + } + response=client.post(SERVER_URL+'data-delivery', headers=header, data=json.dumps(json_payload)) + assert response.status_code == 200 + + # Send data to data-delivery with valid job + json_payload={ + "job":"101", + "payload":"another payload" + } + response=client.post(SERVER_URL+'data-delivery', headers=header, data=json.dumps(json_payload)) + assert response.status_code == 200 + + # Get counter: data_delivery + response=client.get(SERVER_URL+'counter/datadelivery') + assert response.status_code == 200 + assert response.data == b"2" + + +def test_notificationDestination(client): + testdata=get_testdata_dir() + # Header for json payload + header = { + "Content-Type" : "application/json" + } + + # === API: Update policy instance pi2 of type: 2 ===" + with open(testdata+'pi2.json') as json_file: + policytype_2 = json.load(json_file) + response = client.put(SERVER_URL+"a1-p/policytypes/2/policies/pi2?notificationDestination=http://localhost:8085/statustest", headers=header, data=json.dumps(policytype_2)) + assert response.status_code == 202 + result = response.data + assert result == b"" + +# def test_sendstatus(client): +# testdata=get_testdata_dir() +# # Header for json payload +# header = { +# "Content-Type" : "application/json" +# } + +# # === Send status for pi2===" +# with open(testdata+'pi2.json') as json_file: +# policytype_2 = json.load(json_file) +# response = client.post(SERVER_URL+'sendstatus?policyid=pi2', headers=header, data=json.dumps(policytype_2)) +# assert response.status_code == 201 +# result = response.data +# assert result == b"OK" + + +# def test_multithreaded(client): +# # Create a new thread to run the Flask app +# app_thread = threading.Thread(target=run_flask_app) +# app_thread.start() + +# # Perform your tests here +# testdata=get_testdata_dir() +# # Header for json payload +# header = { +# "Content-Type" : "application/json" +# } + +# # === Send status for pi2===" +# with open(testdata+'pi2.json') as json_file: +# policytype_2 = json.load(json_file) +# response = client.post(SERVER_URL+'sendstatus?policyid=pi2', headers=header, data=json.dumps(policytype_2)) +# assert response.status_code == 201 +# result = response.data +# assert result == b"OK" + +# # Wait for the Flask app thread to finish +# app_thread.join() diff --git a/near-rt-ric-simulator/tests/unittest_setup.py b/near-rt-ric-simulator/tests/unittest_setup.py index 9ffd009..06567cf 100644 --- a/near-rt-ric-simulator/tests/unittest_setup.py +++ b/near-rt-ric-simulator/tests/unittest_setup.py @@ -25,7 +25,7 @@ PORT_NUMBER="2222" HOST_IP="localhost" SERVER_URL="http://"+HOST_IP+":"+PORT_NUMBER+"/" -#Dir for json test data files +# Dir for json test data files testdata="" def setup_env(interface_version): @@ -51,9 +51,14 @@ def setup_env(interface_version): def get_testdata_dir(): return testdata -#Test client for rest calls +# Test client for rest calls @pytest.fixture def client(): from main import app - with app.app.test_client() as c: - yield c \ No newline at end of file + with app.app.test_client() as client: + yield client + +# # Run the Flask app in a separate thread for testing +# def run_flask_app(): +# from main import app +# app.app.run(port=8085, host="127.0.0.1", threaded=True) -- 2.16.6