A1-Simulator - Align with OSC Near-RT-RIC A1 Mediator 47/11547/2
authorDenisGNoonan <denis.noonan@est.tech>
Thu, 3 Aug 2023 15:03:44 +0000 (16:03 +0100)
committerDenisGNoonan <denis.noonan@est.tech>
Fri, 11 Aug 2023 15:56:46 +0000 (16:56 +0100)
Issue-ID: NONRTRIC-892
Change-Id: I8123c492e1f3354df15dd107e73ee71a7e28a22d
Signed-off-by: DenisGNoonan <denis.noonan@est.tech>
14 files changed:
near-rt-ric-simulator/README.md
near-rt-ric-simulator/api/OSC_2.1.0/openapi.yaml
near-rt-ric-simulator/src/OSC_2.1.0/__init__.py [new file with mode: 0644]
near-rt-ric-simulator/src/OSC_2.1.0/controllers/__init__.py [new file with mode: 0644]
near-rt-ric-simulator/src/OSC_2.1.0/controllers/a1_mediator_controller.py [moved from near-rt-ric-simulator/src/OSC_2.1.0/a1.py with 78% similarity]
near-rt-ric-simulator/src/OSC_2.1.0/main.py
near-rt-ric-simulator/src/OSC_2.1.0/models/enforceStatus.py [new file with mode: 0644]
near-rt-ric-simulator/src/OSC_2.1.0/var_declaration.py
near-rt-ric-simulator/src/STD_2.0.0/main.py
near-rt-ric-simulator/src/common/maincommon.py
near-rt-ric-simulator/src/start.sh
near-rt-ric-simulator/test/OSC_2.1.0/basic_test.sh
near-rt-ric-simulator/tests/test_osc_2_1_0.py
near-rt-ric-simulator/tests/unittest_setup.py

index b62324e..2369872 100644 (file)
@@ -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
 
index 828a6bf..39af6c8 100644 (file)
@@ -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.
 #   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 (file)
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 (file)
index 0000000..e69de29
@@ -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.
 #
 
 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():
 
index 18bc03b..736438e 100644 (file)
@@ -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.
 #  ============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=<policyid>&status=<status>[&deleted=<boolean>][&created_at=<timestamp>]
+# Set status and reason
+#/status?policyid=<policyid>&status=<status>[&reason=<reason>]
 @app.route('/status', methods=['PUT'])
 def setstatus():
 
   policy_id=request.args.get('policyid')
   if (policy_id is None):
     return Response('Parameter <policyid> 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 <status> 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/<string:countername>', 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=<policyid>
+@app.route('/sendstatus', methods=['POST'])
+def sendstatus():
+  policyid = request.args.get('policyid')
+  if policyid is None:
+    return Response('Parameter <policyid> 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 (file)
index 0000000..89fafd9
--- /dev/null
@@ -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
index f4ff6ac..f78ca50 100644 (file)
@@ -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.
 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']
index 16e4a31..9733a0f 100644 (file)
@@ -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
index 131bf0d..6239f79 100644 (file)
@@ -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')
 
index 8518541..ad5c677 100755 (executable)
@@ -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
index 2f70295..f97d945 100755 (executable)
@@ -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 "********************"
index deff757..5c9b780 100644 (file)
@@ -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.
 #  ============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()
index 9ffd009..06567cf 100644 (file)
@@ -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)