A1 simulator for STD 2.0.0 09/4909/1
authorBjornMagnussonXA <bjorn.magnusson@est.tech>
Thu, 22 Oct 2020 22:38:18 +0000 (00:38 +0200)
committerBjornMagnussonXA <bjorn.magnusson@est.tech>
Fri, 23 Oct 2020 07:35:05 +0000 (09:35 +0200)
Issue-ID: NONRTRIC-303

Signed-off-by: BjornMagnussonXA <bjorn.magnusson@est.tech>
Change-Id: Ic73473cc4e1c3502cc15495169e1279a5ccd8585

32 files changed:
.gitignore
near-rt-ric-simulator/README.md
near-rt-ric-simulator/api/STD_2.0.0/ORAN_A1-p_V2.0.0_api.yaml [new file with mode: 0644]
near-rt-ric-simulator/src/OSC_2.1.0/.gitignore [new file with mode: 0644]
near-rt-ric-simulator/src/OSC_2.1.0/main.py
near-rt-ric-simulator/src/STD_1.1.3/.gitignore [new file with mode: 0644]
near-rt-ric-simulator/src/STD_1.1.3/main.py
near-rt-ric-simulator/src/STD_2.0.0/.gitignore [new file with mode: 0644]
near-rt-ric-simulator/src/STD_2.0.0/a1.py [new file with mode: 0644]
near-rt-ric-simulator/src/STD_2.0.0/callBack.py [new file with mode: 0644]
near-rt-ric-simulator/src/STD_2.0.0/main.py [new file with mode: 0644]
near-rt-ric-simulator/src/STD_2.0.0/var_declaration.py [new file with mode: 0644]
near-rt-ric-simulator/src/common/.gitignore [new file with mode: 0644]
near-rt-ric-simulator/src/start.sh
near-rt-ric-simulator/test/OSC_2.1.0/basic_test.sh
near-rt-ric-simulator/test/OSC_2.1.0/build_and_start.sh
near-rt-ric-simulator/test/STD_1.1.3/basic_test.sh
near-rt-ric-simulator/test/STD_1.1.3/build_and_start.sh
near-rt-ric-simulator/test/STD_2.0.0/.p.json [new file with mode: 0644]
near-rt-ric-simulator/test/STD_2.0.0/basic_test.sh [new file with mode: 0755]
near-rt-ric-simulator/test/STD_2.0.0/build_and_start.sh [new file with mode: 0755]
near-rt-ric-simulator/test/STD_2.0.0/jsonfiles/pi1.json [new file with mode: 0644]
near-rt-ric-simulator/test/STD_2.0.0/jsonfiles/pi1_updated.json [new file with mode: 0644]
near-rt-ric-simulator/test/STD_2.0.0/jsonfiles/pi2.json [new file with mode: 0644]
near-rt-ric-simulator/test/STD_2.0.0/jsonfiles/std_1.json [new file with mode: 0644]
near-rt-ric-simulator/test/common/.gitignore [new file with mode: 0644]
near-rt-ric-simulator/test/common/test_common.sh
near-rt-ric-simulator/tests/.gitignore [new file with mode: 0644]
near-rt-ric-simulator/tests/test_osc_2_1_0.py
near-rt-ric-simulator/tests/test_std_1_1_3.py
near-rt-ric-simulator/tests/test_std_2_0_0.py [new file with mode: 0644]
tox.ini

index 65910c3..50aeff4 100644 (file)
@@ -7,3 +7,7 @@ docs/_build/
 # IDE
 .project
 .vscode
 # IDE
 .project
 .vscode
+
+.coverage
+coverage.xml
+htmlcov/
index fc67bb1..b3c35d2 100644 (file)
@@ -7,6 +7,7 @@ The simulator supports multiple A1 interface versions (version of the open API y
 | --------------------- | ------------------- |
 | OSC 2.1.0,            |      OSC\_2.1.0     |
 | A1 Standard 1.1.3,    |      STD\_1.1.3     |
 | --------------------- | ------------------- |
 | OSC 2.1.0,            |      OSC\_2.1.0     |
 | A1 Standard 1.1.3,    |      STD\_1.1.3     |
+| A1 Standard 2.0.0,    |      STD\_2.0.0     |
 
 All versions are supported by the same container, see section 'Configuring the simulator' below for details about how to the start the simulator with the intended version id.
 
 
 All versions are supported by the same container, see section 'Configuring the simulator' below for details about how to the start the simulator with the intended version id.
 
@@ -87,7 +88,7 @@ URIs for A1:
 |  PUT a policy instance(create or update it) | http://localhost:8085/A1-P/v1/policies/{policyId} |
 |  GET a policy | http://localhost:8085/A1-P/v1/policies/{policyId} |
 |  DELETE a policy instance | http://localhost:8085/A1-P/v1/policies/{policyId} |
 |  PUT a policy instance(create or update it) | http://localhost:8085/A1-P/v1/policies/{policyId} |
 |  GET a policy | http://localhost:8085/A1-P/v1/policies/{policyId} |
 |  DELETE a policy instance | http://localhost:8085/A1-P/v1/policies/{policyId} |
-|  GET a policy status | http://localhost:8085/A1-P/v1/policies/{policyid} |
+|  GET a policy status | http://localhost:8085/A1-P/v1/policies/{policyid}/status |
 Swagger UI at: http://localhost:8085/A1-P/v1/ui/
 
 For the documentation of the admin API, see [A1 Standard 1.1.3](https://docs.o-ran-sc.org/projects/o-ran-sc-sim-a1-interface/en/latest/simulator-api.html#a1-standard-1-1-3).
 Swagger UI at: http://localhost:8085/A1-P/v1/ui/
 
 For the documentation of the admin API, see [A1 Standard 1.1.3](https://docs.o-ran-sc.org/projects/o-ran-sc-sim-a1-interface/en/latest/simulator-api.html#a1-standard-1-1-3).
@@ -105,6 +106,41 @@ URIs for admin operations:
 |  POST, send status for policy | http://localhost:8085/sendstatus?policyid=&lt;policyid&gt; |
 |  GET a counter <br> (counter-name: 'num\_instances', 'num\_types'(always 0), 'interface' or 'remote\_hosts') | http://localhost:8085/counter/&lt;counter-name&gt; |
 
 |  POST, send status for policy | http://localhost:8085/sendstatus?policyid=&lt;policyid&gt; |
 |  GET a counter <br> (counter-name: 'num\_instances', 'num\_types'(always 0), 'interface' or 'remote\_hosts') | http://localhost:8085/counter/&lt;counter-name&gt; |
 
+# Supported operations in simulator A1 Standard 2.0.0
+
+For the complete yaml specification, see [ORAN_A1-p_V2.0.0_api.yaml](../near-rt-ric-simulator/api/STD_2.0.0/ORAN_A1-p_V2.0.0_api.yaml).
+
+URIs for A1:
+| Function              | Path and parameters |
+| --------------------- | ------------------- |
+|  GET all policy type identities | http://localhost:8085/A1-P/v2/policytypes |
+|  GET a policy type | http://localhost:8085/A1-P/v2/policytypes/{policyTypeId} |
+|  GET all policy identities | http://localhost:8085/A1-P/v2/policytypes/{policyTypeId}/policies |
+|  PUT a policy instance(create or update it) | http://localhost:8085/A1-P/v2/policytypes/{policyTypeId}/policies/{policyId} |
+|  GET a policy | http://localhost:8085/A1-P/v2/policytypes/{policyTypeId}/policies/{policyId} |
+|  DELETE a policy instance | http://localhost:8085/A1-P/v2/policytypes/{policyTypeId}/policies/{policyId} |
+|  GET a policy status | http://localhost:8085/A1-P/v2/policytypes/{policyTypeId}/policies/{policyid}/status |
+Swagger UI at: http://localhost:8085/A1-P/v2/ui/
+
+For the documentation of the admin API, see [A1 Standard 2.0.0](https://docs.o-ran-sc.org/projects/o-ran-sc-sim-a1-interface/en/latest/simulator-api.html#a1-standard-2-0-0).
+
+URIs for admin operations:
+| Function              | Path and parameters |
+| --------------------- | ------------------- |
+|  GET, a basic healthcheck | http://localhost:8085/ |
+|  GET, a list of all supported interfaces | http://localhost:8085/container\_interfaces |
+|  POST, delete all policy instances | http://localhost:8085/deleteinstances |
+|  POST, full reset | http://localhost:8085/deleteall |
+|  PUT, create/update a policy type | http://localhost:8085/policytype?id=&lt;policytypeid&gt; |
+|  DELETE, delete a policy type | http://localhost:8085/policytype?id=&lt;policytypeid&gt; |
+|  GET, list of policy type id | http://localhost:8085/policytypes |
+|  POST, force a specific response code for an A1 operation | http://localhost:8085/forceresponse?code=&lt;http-code&gt; |
+|  POST, force delayed response of all A1 operations | http://localhost:8085/forcedelay?delay=&lt;seconds&gt; |
+|  PUT, set status and optional reason | http://localhost:8085/status?status=&lt;status&gt;[&amp;reason=&lt;reason&gt;] |
+|  POST, send status for policy | http://localhost:8085/sendstatus?policyid=&lt;policyid&gt; |
+|  POST, deliver data | http://localhost:8085/datadelivery |
+|  GET a counter <br> (counter-name: 'num\_instances', 'num\_types'(always 0), 'interface', 'remote\_hosts' or 'datadelivery') | http://localhost:8085/counter/&lt;counter-name&gt; |
+
 
 
 # Configuring the simulator
 
 
 # Configuring the simulator
diff --git a/near-rt-ric-simulator/api/STD_2.0.0/ORAN_A1-p_V2.0.0_api.yaml b/near-rt-ric-simulator/api/STD_2.0.0/ORAN_A1-p_V2.0.0_api.yaml
new file mode 100644 (file)
index 0000000..3cb62c5
--- /dev/null
@@ -0,0 +1,341 @@
+openapi: 3.0.0
+info:
+  title: 'A1-P Policy Management Service'
+  version: 2.0.0
+  description: |
+    API for Policy Management Service.
+    © 2020, O-RAN Alliance.
+    All rights reserved.
+externalDocs:
+  description: 'O-RAN.WG2.A1AP-v02.00 A1 interface: Application Protocol'
+  url: 'https://www.o-ran.org/specifications'
+servers:
+  - url: '{apiRoot}/A1-P/v2'
+    variables:
+      apiRoot:
+        default: 'https://example.com'
+        description: 'apiRoot as defined in clause 4.2.1 in ORAN-WG2.A1.AP'
+paths:
+  '/policytypes':
+    get:
+      operationId: a1.get_all_policy_types
+      description: 'Get all policy type identifiers'
+      tags:
+      - All Policy Type Identifiers
+      responses:
+        200:
+          description: 'Array of all policy type identifiers'
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  "$ref": "#/components/schemas/PolicyTypeId"
+                minItems: 0
+        429:
+          "$ref": "#/components/responses/429-TooManyRequests"
+        503:
+          "$ref": "#/components/responses/503-ServiceUnavailable"
+
+  '/policytypes/{policyTypeId}':
+    parameters:
+      - name: policyTypeId
+        in: path
+        required: true
+        schema:
+          "$ref": "#/components/schemas/PolicyTypeId"
+    get:
+      operationId: a1.get_policy_type
+      description: 'Get the schemas for a policy type'
+      tags:
+      - Individual Policy Type
+      responses:
+        200:
+          description: 'The policy type schemas'
+          content:
+            application/json:
+              schema:
+                "$ref": "#/components/schemas/PolicyTypeObject"
+        404:
+          "$ref": "#/components/responses/404-NotFound"
+        429:
+          "$ref": "#/components/responses/429-TooManyRequests"
+        503:
+          "$ref": "#/components/responses/503-ServiceUnavailable"
+
+  '/policytypes/{policyTypeId}/policies':
+    get:
+      operationId: a1.get_all_policy_identities
+      description: 'Get all policy identifiers'
+      tags:
+      - All Policy Identifiers
+      parameters:
+        - name: policyTypeId
+          in: path
+          required: true
+          schema:
+            "$ref": "#/components/schemas/PolicyTypeId"
+      responses:
+        200:
+          description: 'Array of all policy identifiers'
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  "$ref": "#/components/schemas/PolicyId"
+                minItems: 0
+        429:
+          "$ref": "#/components/responses/429-TooManyRequests"
+        503:
+          "$ref": "#/components/responses/503-ServiceUnavailable"
+
+  '/policytypes/{policyTypeId}/policies/{policyId}':
+    parameters:
+      - name: policyTypeId
+        in: path
+        required: true
+        schema:
+          "$ref": "#/components/schemas/PolicyTypeId"
+      - name: policyId
+        in: path
+        required: true
+        schema:
+          "$ref": "#/components/schemas/PolicyId"
+    put:
+      operationId: a1.put_policy
+      description: 'Create, or update, a policy'
+      tags:
+      - Individual Policy Object
+      parameters:
+        - name: notificationDestination
+          in: query
+          required: false
+          schema:
+            "$ref": "#/components/schemas/NotificationDestination"
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              "$ref": "#/components/schemas/PolicyObject"
+      responses:
+        200:
+          description: 'The policy was updated'
+          content:
+            application/json:
+              schema:
+                "$ref": "#/components/schemas/PolicyObject"
+        201:
+          description: 'The policy was created'
+          content:
+            application/json:
+              schema:
+                "$ref": "#/components/schemas/PolicyObject"
+          headers:
+            Location:
+              description: 'Contains the URI of the created policy'
+              required: true
+              schema:
+                type: string
+        400:
+          "$ref": "#/components/responses/400-BadRequest"
+        409:
+          "$ref": "#/components/responses/409-Conflict"
+        429:
+          "$ref": "#/components/responses/429-TooManyRequests"
+        503:
+          "$ref": "#/components/responses/503-ServiceUnavailable"
+        507:
+          "$ref": "#/components/responses/507-InsufficientStorage"
+      callbacks:
+        policyStatusNotification:
+          '{$request.query.notificationDestination}':
+            post:
+              description: 'Notify about status changes for this policy'
+              requestBody:
+                required: true
+                content:
+                  application/json:
+                    schema:
+                      "$ref": "#/components/schemas/PolicyStatusObject"
+              responses:
+                204:
+                  description: 'Notification received'
+    get:
+      operationId: a1.get_policy
+      description: 'Query a policy'
+      tags:
+      - Individual Policy Object
+      responses:
+        200:
+          description: 'The requested policy'
+          content:
+            application/json:
+              schema:
+                "$ref": "#/components/schemas/PolicyObject"
+        404:
+          "$ref": "#/components/responses/404-NotFound"
+        409:
+          "$ref": "#/components/responses/409-Conflict"
+        429:
+          "$ref": "#/components/responses/429-TooManyRequests"
+        503:
+          "$ref": "#/components/responses/503-ServiceUnavailable"
+    delete:
+      operationId: a1.delete_policy
+      description: 'Delete a policy'
+      tags:
+      - Individual Policy Object
+      responses:
+        204:
+          description: 'The policy was deleted'
+        404:
+          "$ref": "#/components/responses/404-NotFound"
+        429:
+          "$ref": "#/components/responses/429-TooManyRequests"
+        503:
+          "$ref": "#/components/responses/503-ServiceUnavailable"
+
+  '/policytypes/{policyTypeId}/policies/{policyId}/status':
+    parameters:
+      - name: policyTypeId
+        in: path
+        required: true
+        schema:
+          "$ref": "#/components/schemas/PolicyTypeId"
+      - name: policyId
+        in: path
+        required: true
+        schema:
+          "$ref": "#/components/schemas/PolicyId"
+    get:
+      operationId: a1.get_policy_status
+      description: 'Query a policy status'
+      tags:
+      - Individual Policy Status Object
+      responses:
+        200:
+          description: 'The requested policy status'
+          content:
+            application/json:
+              schema:
+                "$ref": "#/components/schemas/PolicyStatusObject"
+        404:
+          "$ref": "#/components/responses/404-NotFound"
+        409:
+          "$ref": "#/components/responses/409-Conflict"
+        429:
+          "$ref": "#/components/responses/429-TooManyRequests"
+        503:
+          "$ref": "#/components/responses/503-ServiceUnavailable"
+
+components:
+  schemas:
+    #
+    # Representation objects
+    #
+    PolicyObject:
+      description: 'A generic policy object that can be used to transport any policy. Additionally, a policy shall be valid according to the schema of its specific policy type.'
+      type: object
+
+    PolicyStatusObject:
+      description: 'A generic policy status object that can be used to transport any policy status. Additionally, a policy status shall be valid according to the schema of its specific policy type.'
+      type: object
+
+    PolicyTypeObject:
+      description: 'A definition of a policy type, i.e. the schemas for a policy respectively its status'
+      type: object
+      properties:
+        policySchema:
+          "$ref": "#/components/schemas/JsonSchema"
+        statusSchema:
+          "$ref": "#/components/schemas/JsonSchema"
+      required:
+        - policySchema
+
+    ProblemDetails:
+      description: 'A problem detail to carry details in a HTTP response according to RFC 7807'
+      type: object
+      properties:
+        type:
+          type: string
+        title:
+          type: string
+        status:
+          type: number
+        detail:
+          type: string
+        instance:
+          type: string
+
+    #
+    # Simple data types
+    #
+    JsonSchema:
+      description: 'A JSON schema following http://json-schema.org/draft-07/schema'
+      type: object
+
+    NotificationDestination:
+      description: 'A complete callback URI defined according to IETF RFC 3986 where to send notifications'
+      type: string
+
+    PolicyId:
+      description: 'Policy identifier assigned by the A1-P Consumer when a policy is created'
+      type: string
+
+    PolicyTypeId:
+      description: 'Policy type identifier assigned by the A1-P Provider'
+      type: string
+
+  responses:
+    400-BadRequest:
+      description: 'Object in payload not properly formulated or not related to the method'
+      content:
+        application/problem+json:
+          schema:
+            "$ref": "#/components/schemas/ProblemDetails"
+
+    404-NotFound:
+      description: 'No resource found at the URI'
+      content:
+        application/problem+json:
+          schema:
+            "$ref": "#/components/schemas/ProblemDetails"
+
+    405-MethodNotAllowed:
+      description: 'Method not allowed for the URI'
+      content:
+        application/problem+json:
+          schema:
+            "$ref": "#/components/schemas/ProblemDetails"
+
+    409-Conflict:
+      description: 'Request could not be processed in the current state of the resource'
+      content:
+        application/problem+json:
+          schema:
+            "$ref": "#/components/schemas/ProblemDetails"
+
+    429-TooManyRequests:
+      description: 'Too many requests have been sent in a given amount of time'
+      content:
+        application/problem+json:
+          schema:
+            "$ref": "#/components/schemas/ProblemDetails"
+
+    503-ServiceUnavailable:
+      description: 'The provider is currently unable to handle the request due to a temporary overload'
+      content:
+        application/problem+json:
+          schema:
+            "$ref": "#/components/schemas/ProblemDetails"
+
+    507-InsufficientStorage:
+      description: 'The method could not be performed on the resource because the provider is unable to store the representation needed to successfully complete the request'
+      content:
+        application/problem+json:
+          schema:
+            "$ref": "#/components/schemas/ProblemDetails"
+
+
diff --git a/near-rt-ric-simulator/src/OSC_2.1.0/.gitignore b/near-rt-ric-simulator/src/OSC_2.1.0/.gitignore
new file mode 100644 (file)
index 0000000..bee8a64
--- /dev/null
@@ -0,0 +1 @@
+__pycache__
index 48fbe91..e0766d8 100644 (file)
@@ -206,6 +206,8 @@ def getcounter(countername):
   elif (countername == "remote_hosts"):
     hosts=",".join(hosts_set)
     return str(hosts),200
   elif (countername == "remote_hosts"):
     hosts=",".join(hosts_set)
     return str(hosts),200
+  elif (countername == "datadelivery"):
+    return Response(str(0),200, mimetype=TEXT_PLAIN)
   else:
     return Response("Counter name: "+countername+" not found.",404, mimetype=TEXT_PLAIN)
 
   else:
     return Response("Counter name: "+countername+" not found.",404, mimetype=TEXT_PLAIN)
 
diff --git a/near-rt-ric-simulator/src/STD_1.1.3/.gitignore b/near-rt-ric-simulator/src/STD_1.1.3/.gitignore
new file mode 100644 (file)
index 0000000..bee8a64
--- /dev/null
@@ -0,0 +1 @@
+__pycache__
index b7ecdcd..5742791 100644 (file)
@@ -163,6 +163,8 @@ def getcounter(countername):
   elif (countername == "remote_hosts"):
     hosts=",".join(hosts_set)
     return str(hosts),200
   elif (countername == "remote_hosts"):
     hosts=",".join(hosts_set)
     return str(hosts),200
+  elif (countername == "datadelivery"):
+    return Response(str(0),200, mimetype=TEXT_PLAIN)
   else:
     return Response("Counter name: "+countername+" not found.",404, mimetype=TEXT_PLAIN)
 
   else:
     return Response("Counter name: "+countername+" not found.",404, mimetype=TEXT_PLAIN)
 
diff --git a/near-rt-ric-simulator/src/STD_2.0.0/.gitignore b/near-rt-ric-simulator/src/STD_2.0.0/.gitignore
new file mode 100644 (file)
index 0000000..bee8a64
--- /dev/null
@@ -0,0 +1 @@
+__pycache__
diff --git a/near-rt-ric-simulator/src/STD_2.0.0/a1.py b/near-rt-ric-simulator/src/STD_2.0.0/a1.py
new file mode 100644 (file)
index 0000000..c4aa4ca
--- /dev/null
@@ -0,0 +1,271 @@
+#  ============LICENSE_START===============================================
+#  Copyright (C) 2020 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.
+#  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#  ============LICENSE_END=================================================
+#
+
+import copy
+import datetime
+import json
+import logging
+import collections
+import time
+
+from connexion import NoContent
+from flask import Flask, escape, request, Response, make_response
+from jsonschema import validate
+from var_declaration import policy_instances, policy_types, policy_status, callbacks, forced_settings, policy_fingerprint, hosts_set
+from utils import calcFingerprint
+from maincommon import check_apipath, apipath, get_supported_interfaces_response, extract_host_name
+
+#Constsants
+APPL_JSON='application/json'
+APPL_PROB_JSON='application/problem+json'
+
+# API Function: Get all policy type ids
+def get_all_policy_types():
+
+  extract_host_name(hosts_set, request)
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  res = list(policy_types.keys())
+  return (res, 200)
+
+# API Function: Get a policy type
+def get_policy_type(policyTypeId):
+
+  extract_host_name(hosts_set, request)
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  policy_type_id=str(policyTypeId)
+
+  if (policy_type_id not in policy_types.keys()):
+    pjson=create_problem_json(None, "The policy type does not exist.", 404, None, policy_type_id)
+    return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
+
+  return Response(json.dumps(policy_types[policy_type_id]), 200, mimetype=APPL_JSON)
+
+# API Function: Get all policy ids
+def get_all_policy_identities(policyTypeId):
+
+  extract_host_name(hosts_set, request)
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  policy_type_id=str(policyTypeId)
+
+  if (policy_type_id not in policy_types.keys()):
+    pjson=create_problem_json(None, "The policy type does not exist.", 404, None, policy_type_id)
+    return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
+
+  return (list(policy_instances[policy_type_id].keys()), 200)
+
+# API Function: Create or update a policy
+def put_policy(policyTypeId, policyId):
+
+  extract_host_name(hosts_set, request)
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  policy_type_id=str(policyTypeId)
+  policy_id=str(policyId)
+
+  if (policy_type_id not in policy_types.keys()):
+    pjson=create_problem_json(None, "The policy type does not exist.", 404, None, policy_id)
+    return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
+
+  try:
+    data = request.data
+    data = json.loads(data)
+  except Exception:
+    pjson=create_problem_json(None, "The policy is corrupt or missing.", 400, None, policy_id)
+    return Response(json.dumps(pjson), 400, mimetype=APPL_PROB_JSON)
+
+  try:
+    validate(instance=data, schema=policy_types[policy_type_id]['policySchema'])
+  except Exception:
+    return (None, 400)
+
+  fp_previous=None
+  retcode=201
+  if policy_id in policy_instances[policy_type_id].keys():
+    retcode=200
+    fp_previous=calcFingerprint(policy_instances[policy_type_id][policy_id])
+  else:
+    if (policy_id in policy_fingerprint.values()):
+      return (None, 400)
+
+  fp=calcFingerprint(data)
+  if (fp in policy_fingerprint.keys()):
+    p_id=policy_fingerprint[fp]
+    if (p_id != policy_id):
+      pjson=create_problem_json(None, "Duplicate, the policy json already exists.", 400, None, policy_id)
+      return Response(json.dumps(pjson), 400, mimetype=APPL_PROB_JSON)
+
+  if (fp_previous is not None):
+    del policy_fingerprint[fp_previous]
+
+  policy_fingerprint[fp]=policy_id
+
+  noti=request.args.get('notificationDestination')
+  callbacks[policy_id]=noti
+
+  policy_instances[policy_type_id][policy_id]=data
+
+  if (policy_types[policy_type_id]['statusSchema'] is not None):
+    ps = {}
+    ps["enforceStatus"] = ""
+    ps["enforceReason"] = ""
+    policy_status[policy_id] = ps
+
+  if (retcode == 200):
+    return Response(json.dumps(data), 200, mimetype=APPL_JSON)
+  else:
+    headers={}
+    headers['Location']='/A1-P/v2/policytypes/' + policy_type_id + '/policies/' + policy_id
+    return Response(json.dumps(data), 201, headers=headers, mimetype=APPL_JSON)
+
+# API Function: Get a policy
+def get_policy(policyTypeId, policyId):
+
+  extract_host_name(hosts_set, request)
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  policy_type_id=str(policyTypeId)
+  policy_id=str(policyId)
+
+  if (policy_type_id not in policy_types.keys()):
+    pjson=create_problem_json(None, "The policy type does not exist.", 404, None, policy_id)
+    return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
+
+  if (policy_id not in policy_instances[policy_type_id].keys()):
+    pjson=create_problem_json(None, "The requested policy does not exist.", 404, None, policy_id)
+    return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
+
+  return Response(json.dumps(policy_instances[policy_type_id][policy_id]), 200, mimetype=APPL_JSON)
+
+
+# API Function: Delete a policy
+def delete_policy(policyTypeId, policyId):
+
+  extract_host_name(hosts_set, request)
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  policy_type_id=str(policyTypeId)
+  policy_id=str(policyId)
+
+  if (policy_type_id not in policy_types.keys()):
+    pjson=create_problem_json(None, "The policy type does not exist.", 404, None, policy_id)
+    return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
+
+  if (policy_id not in policy_instances[policy_type_id].keys()):
+    pjson=create_problem_json(None, "The requested policy does not exist.", 404, None, policy_id)
+    return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
+
+  fp_previous=calcFingerprint(policy_instances[policy_type_id][policy_id])
+  policy_fingerprint.pop(fp_previous)
+  policy_instances[policy_type_id].pop(policy_id)
+  policy_status.pop(policy_id)
+  callbacks.pop(policy_id)
+  return Response('', 204, mimetype=APPL_JSON)
+
+
+# API Function: Get status for a policy
+def get_policy_status(policyTypeId, policyId):
+
+  extract_host_name(hosts_set, request)
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  policy_type_id=str(policyTypeId)
+  policy_id=str(policyId)
+
+  if (policy_type_id not in policy_types.keys()):
+    pjson=create_problem_json(None, "The policy type does not exist.", 404, None, policy_id)
+    return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
+
+  if (policy_id not in policy_instances[policy_type_id].keys()):
+    pjson=create_problem_json(None, "The requested policy does not exist.", 404, None, policy_id)
+    return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
+
+  return Response(json.dumps(policy_status[policy_id]), status=200, mimetype=APPL_JSON)
+
+# Helper: Create a response object if forced http response code is set
+def get_forced_response():
+  if (forced_settings['code'] is not None):
+    pjson=create_error_response(forced_settings['code'])
+    forced_settings['code']=None
+    return Response(json.dumps(pjson), pjson['status'], mimetype=APPL_PROB_JSON)
+  return None
+
+# Helper: Delay if delayed response code is set
+def do_delay():
+  if (forced_settings['delay'] is not None):
+    try:
+      val=int(forced_settings['delay'])
+      time.sleep(val)
+    except Exception:
+      return
+
+# Helper: Check if response shall be delayed or a forced response shall be sent
+def check_modified_response():
+  do_delay()
+  return get_forced_response()
+
+# Helper: Create a problem json object
+def create_problem_json(type_of, title, status, detail, instance):
+
+  error = {}
+  if type_of is not None:
+    error["type"] = type_of
+  if title is not None:
+    error["title"] = title
+  if status is not None:
+    error["status"] = status
+  if detail is not None:
+    error["detail"] = detail
+  if instance is not None:
+    error["instance"] = instance
+  return error
+
+# Helper: Create a problem json based on a generic http response code
+def create_error_response(code):
+
+    if code == '400':
+      return(create_problem_json(None, "Bad request", 400, "Object in payload not properly formulated or not related to the method", None))
+    elif code == '404':
+      return(create_problem_json(None, "Not found", 404, "No resource found at the URI", None))
+    elif code == '405':
+      return(create_problem_json(None, "Method not allowed", 405, "Method not allowed for the URI", None))
+    elif code == '409':
+      return(create_problem_json(None, "Conflict", 409, "Request could not be processed in the current state of the resource", None))
+    elif code == '429':
+      return(create_problem_json(None, "Too many requests", 429, "Too many requests have been sent in a given amount of time", None))
+    elif code == '507':
+      return(create_problem_json(None, "Insufficient storage", 507, "The method could not be performed on the resource because the provider is unable to store the representation needed to successfully complete the request", None))
+    elif code == '503':
+      return(create_problem_json(None, "Service unavailable", 503, "The provider is currently unable to handle the request due to a temporary overload", None))
+    else:
+      return(create_problem_json(None, "Unknown", code, "Not implemented response code", None))
diff --git a/near-rt-ric-simulator/src/STD_2.0.0/callBack.py b/near-rt-ric-simulator/src/STD_2.0.0/callBack.py
new file mode 100644 (file)
index 0000000..0e8e416
--- /dev/null
@@ -0,0 +1,43 @@
+#  ============LICENSE_START===============================================
+#  Copyright (C) 2020 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.
+#  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#  ============LICENSE_END=================================================
+#
+
+import json
+from flask import Flask, request, Response
+
+app = Flask(__name__) #NOSONAR
+
+#Check alive function
+@app.route('/', methods=['GET'])
+def test():
+
+  return Response("OK", 200, 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(json.dumps(data), 200, mimetype='application/json')
+
+port_number = 2223
+
+app.run(port=port_number, host="127.0.0.1", threaded=False)
\ No newline at end of file
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
new file mode 100644 (file)
index 0000000..dcf835e
--- /dev/null
@@ -0,0 +1,255 @@
+#  ============LICENSE_START===============================================
+#  Copyright (C) 2020 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.
+#  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#  ============LICENSE_END=================================================
+#
+
+import connexion
+import json
+import sys
+import os
+import requests
+
+
+from pathlib import Path
+from flask import Flask, escape, request, Response
+from jsonschema import validate
+from var_declaration import policy_instances, policy_types, policy_status, callbacks, forced_settings, policy_fingerprint, hosts_set, data_delivery_counter
+from maincommon import check_apipath, apipath, get_supported_interfaces_response, extract_host_name
+
+#Constants
+TEXT_PLAIN='text/plain'
+
+check_apipath()
+
+app = connexion.App(__name__, specification_dir=apipath)
+
+#Check alive function
+@app.route('/', methods=['GET'])
+def test():
+
+  return Response("OK", 200, mimetype=TEXT_PLAIN)
+
+#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 delete_instances():
+
+  for i in policy_instances.keys():
+    policy_instances[i]={}
+  policy_status.clear()
+  callbacks.clear()
+  forced_settings['code']=None
+  forced_settings['delay']=None
+  policy_fingerprint.clear()
+  return Response("All policy instances deleted", 200, mimetype=TEXT_PLAIN)
+
+#Delete all - all reset
+#(same as delete_instances but kept to in order to use the same interface as other version of the simulator)
+@app.route('/deleteall', methods=['POST'])
+def delete_all():
+
+  policy_instances.clear()
+  policy_types.clear()
+  policy_status.clear()
+  callbacks.clear()
+  forced_settings['code']=None
+  forced_settings['delay']=None
+  policy_fingerprint.clear()
+  return Response("All policy instances and types deleted", 200, mimetype=TEXT_PLAIN)
+
+#Load a policy type
+@app.route('/policytype', methods=['PUT'])
+def policytype():
+
+  policy_type_id=request.args.get('id')
+  if (policy_type_id is None):
+    return Response('Parameter <id> missing in request', status=400, mimetype=TEXT_PLAIN)
+
+  try:
+    data = request.data
+    data = json.loads(data)
+  except Exception:
+    return Response("The policy type is corrupt or missing", 400, mimetype=TEXT_PLAIN)
+
+  if ('policySchema' not in data.keys()):
+    return Response("The policy type atribute policySchema is missing", 400, mimetype=TEXT_PLAIN)
+
+  retcode=201
+  if (policy_type_id in policy_types.keys()):
+    retcode=200
+    if (len(policy_instances[policy_type_id]) > 0):
+      return Response("The policy type already exists and instances exists", 400, mimetype=TEXT_PLAIN)
+
+  policy_types[policy_type_id]=data
+  policy_instances[policy_type_id]={}
+  return Response("Policy type " + policy_type_id + " is OK.", retcode, mimetype=TEXT_PLAIN)
+
+#Delete a policy type
+@app.route('/policytype', methods=['DELETE'])
+def del_policytype():
+
+  policy_type_id=request.args.get('id')
+  if (policy_type_id is None):
+    return Response('Parameter <id> missing in request', status=400, mimetype=TEXT_PLAIN)
+
+  if (policy_type_id in policy_types.keys()):
+    if (len(policy_instances[policy_type_id]) > 0):
+      return Response("The policy type already exists and instances exists", 400, mimetype=TEXT_PLAIN)
+
+    del policy_types[policy_type_id]
+    del policy_instances[policy_type_id]
+    return Response("Policy type " + policy_type_id + " is OK.", 204, mimetype=TEXT_PLAIN)
+
+  return Response("Policy type " + policy_type_id + " not found.", 204, mimetype=TEXT_PLAIN)
+
+
+# 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
+#/forceresponse?code=<responsecode>
+@app.route('/forceresponse', methods=['POST'])
+def forceresponse():
+
+  try:
+    forced_settings['code']=request.args.get('code')
+  except Exception:
+    forced_settings['code']=None
+  return Response("Force response code: " + str(forced_settings['code']) + " set for one single A1 response", 200, mimetype=TEXT_PLAIN)
+
+#Set force delay response, in seconds, for all A1 responses
+#/froceesponse?delay=<seconds>
+@app.route('/forcedelay', methods=['POST'])
+def forcedelay():
+
+  try:
+    forced_settings['delay']=request.args.get('delay')
+  except Exception:
+    forced_settings['delay']=None
+  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>[&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)
+  reason=request.args.get('reason')
+  ps = {}
+  ps["enforceStatus"] = status
+  msg="Status set to "+status
+  if (reason is not None):
+    ps["enforceReason"] = reason
+    msg=msg+" and "+reason
+  policy_status[policy_id] = ps
+  msg=msg+" for policy: " + policy_id
+  return Response(msg, 200, mimetype=TEXT_PLAIN)
+
+#Send status
+#/status?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=callbacks[policyid]
+  try:
+    print("Callback url: " + str(cb))
+    resp=requests.post(cb,json=json.dumps(ps), verify=False) # NOSONAR
+  except:
+    return Response('Post status failed, could not send to: '+str(cb), status=500, mimetype=TEXT_PLAIN)
+  if (resp.status_code<199 & resp.status_code > 299):
+    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')
+
+#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(json.dumps(data), 200, mimetype='application/json')
+
+#Receive a data delivery package
+#/datadelivery
+@app.route('/datadelivery', methods=['POST'])
+def datadelivery():
+  global data_delivery_counter
+  try:
+    data = request.data
+    data = json.loads(data)
+  except Exception:
+    return Response("The data is corrupt or missing.", 400, mimetype=TEXT_PLAIN)
+  data_delivery_counter += 1
+  return Response("", 200, mimetype=TEXT_PLAIN)
+
+#Metrics function
+#Get a named counter
+@app.route('/counter/<string:countername>', methods=['GET'])
+def getcounter(countername):
+
+  if (countername == "num_instances"):
+    return Response(str(len(policy_fingerprint)), 200, mimetype=TEXT_PLAIN)
+  elif (countername == "num_types"):
+    return Response(str(len(policy_instances)),200, mimetype=TEXT_PLAIN)
+  elif (countername == "interface"):
+    p=Path(os.getcwd())
+    pp=p.parts
+    return Response(str(pp[len(pp)-1]),200, mimetype=TEXT_PLAIN)
+  elif (countername == "remote_hosts"):
+    hosts=",".join(hosts_set)
+    return str(hosts),200
+  elif (countername == "datadelivery"):
+    return Response(str(data_delivery_counter),200, mimetype=TEXT_PLAIN)
+  else:
+    return Response("Counter name: "+countername+" not found.",404, mimetype=TEXT_PLAIN)
+
+port_number = 2222
+if len(sys.argv) >= 2:
+  if isinstance(sys.argv[1], int):
+    port_number = sys.argv[1]
+
+app.add_api('ORAN_A1-p_V2.0.0_api.yaml')
+
+if __name__ == '__main__':
+  app.run(port=port_number, host="127.0.0.1", threaded=False)
\ No newline at end of file
diff --git a/near-rt-ric-simulator/src/STD_2.0.0/var_declaration.py b/near-rt-ric-simulator/src/STD_2.0.0/var_declaration.py
new file mode 100644 (file)
index 0000000..5136404
--- /dev/null
@@ -0,0 +1,27 @@
+#  ============LICENSE_START===============================================
+#  Copyright (C) 2020 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.
+#  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#  ============LICENSE_END=================================================
+#
+
+policy_types={}
+policy_instances = {}
+policy_status = {}
+callbacks = {}
+forced_settings = {}
+forced_settings['code']=None
+forced_settings['delay']=None
+policy_fingerprint={}
+hosts_set=set()
+data_delivery_counter=0
diff --git a/near-rt-ric-simulator/src/common/.gitignore b/near-rt-ric-simulator/src/common/.gitignore
new file mode 100644 (file)
index 0000000..bee8a64
--- /dev/null
@@ -0,0 +1 @@
+__pycache__
index 3fe7d4b..feb5a1d 100755 (executable)
@@ -41,7 +41,7 @@ cd $1
 nginx -c /usr/src/app/nginx.conf
 
 #start callBack server
 nginx -c /usr/src/app/nginx.conf
 
 #start callBack server
-if [ ${A1_VERSION} == "STD_1.1.3" ]; then
+if [[ ${A1_VERSION} == "STD"* ]]; then
     echo "Path to callBack.py: "$PWD
     python -u callBack.py &
 fi
     echo "Path to callBack.py: "$PWD
     python -u callBack.py &
 fi
index 41ff2d1..bc2413d 100755 (executable)
@@ -47,7 +47,7 @@ RESULT="OK"
 do_curl GET / 200
 
 echo "=== Check used and implemented interfaces ==="
 do_curl GET / 200
 
 echo "=== Check used and implemented interfaces ==="
-RESULT="Current interface: OSC_2.1.0 All supported A1 interface yamls in this container: ['OSC_2.1.0', 'STD_1.1.3']"
+RESULT="Current interface: OSC_2.1.0 All supported A1 interface yamls in this container: ['OSC_2.1.0', 'STD_1.1.3', 'STD_2.0.0']"
 do_curl GET /container_interfaces 200
 
 echo "=== Reset simulator instances ==="
 do_curl GET /container_interfaces 200
 
 echo "=== Reset simulator instances ==="
index e182782..f376971 100755 (executable)
@@ -25,6 +25,9 @@ cd ../../
 #Build the image
 docker build -t a1test .
 
 #Build the image
 docker build -t a1test .
 
+docker stop a1OscSimulator > /dev/null 2>&1
+docker rm -f a1OscSimulator > /dev/null 2>&1
+
 echo "Starting $1 mode"
 #Run the container in interactive mode, unsecure port 8085, secure port 8185.
 docker run -it -p 8085:8085 -p 8185:8185 -e A1_VERSION=OSC_2.1.0 -e ALLOW_HTTP=true -e REMOTE_HOSTS_LOGGING=1 --volume "$PWD/certificate:/usr/src/app/cert" --name a1OscSimulator a1test
 echo "Starting $1 mode"
 #Run the container in interactive mode, unsecure port 8085, secure port 8185.
 docker run -it -p 8085:8085 -p 8185:8185 -e A1_VERSION=OSC_2.1.0 -e ALLOW_HTTP=true -e REMOTE_HOSTS_LOGGING=1 --volume "$PWD/certificate:/usr/src/app/cert" --name a1OscSimulator a1test
index 77f10a6..3a828d2 100755 (executable)
@@ -48,7 +48,7 @@ RESULT="OK"
 do_curl GET / 200
 
 echo "=== Check used and implemented interfaces ==="
 do_curl GET / 200
 
 echo "=== Check used and implemented interfaces ==="
-RESULT="Current interface: STD_1.1.3 All supported A1 interface yamls in this container: ['OSC_2.1.0', 'STD_1.1.3']"
+RESULT="Current interface: STD_1.1.3 All supported A1 interface yamls in this container: ['OSC_2.1.0', 'STD_1.1.3', 'STD_2.0.0']"
 do_curl GET /container_interfaces 200
 
 echo "=== Reset simulator instances ==="
 do_curl GET /container_interfaces 200
 
 echo "=== Reset simulator instances ==="
index fa59f49..1d56487 100755 (executable)
@@ -25,6 +25,9 @@ cd ../../
 #Build the image
 docker build -t a1test .
 
 #Build the image
 docker build -t a1test .
 
+docker stop a1StdSimulator > /dev/null 2>&1
+docker rm -f a1StdSimulator > /dev/null 2>&1
+
 echo "Starting ric-sim"
 #Run the container in interactive mode, unsecure port 8085, secure port 8185
 docker run -it -p 8085:8085 -p 8185:8185 -e A1_VERSION=STD_1.1.3 -e ALLOW_HTTP=true -e REMOTE_HOSTS_LOGGING=1 --volume "$PWD/certificate:/usr/src/app/cert" --name a1StdSimulator a1test
 echo "Starting ric-sim"
 #Run the container in interactive mode, unsecure port 8085, secure port 8185
 docker run -it -p 8085:8085 -p 8185:8185 -e A1_VERSION=STD_1.1.3 -e ALLOW_HTTP=true -e REMOTE_HOSTS_LOGGING=1 --volume "$PWD/certificate:/usr/src/app/cert" --name a1StdSimulator a1test
diff --git a/near-rt-ric-simulator/test/STD_2.0.0/.p.json b/near-rt-ric-simulator/test/STD_2.0.0/.p.json
new file mode 100644 (file)
index 0000000..0967ef4
--- /dev/null
@@ -0,0 +1 @@
+{}
diff --git a/near-rt-ric-simulator/test/STD_2.0.0/basic_test.sh b/near-rt-ric-simulator/test/STD_2.0.0/basic_test.sh
new file mode 100755 (executable)
index 0000000..576bd5e
--- /dev/null
@@ -0,0 +1,288 @@
+#!/bin/bash
+
+#  ============LICENSE_START===============================================
+#  Copyright (C) 2020 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.
+#  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#  ============LICENSE_END=================================================
+#
+
+# Script for basic test of the simulator.
+# Run the build_and_start with the same arg as this script
+if [ $# -ne 1 ]; then
+    echo "Usage: ./basic_test.sh nonsecure|secure"
+    exit 1
+fi
+if [ "$1" != "nonsecure" ] && [ "$1" != "secure" ]; then
+    echo "Usage: ./basic_test.sh nonsecure|secure"
+    exit 1
+fi
+
+if [ $1 == "nonsecure" ]; then
+    #Default http port for the simulator
+    PORT=8085
+    # Set http protocol
+    HTTPX="http"
+else
+    #Default https port for the simulator
+    PORT=8185
+    # Set https protocol
+    HTTPX="https"
+fi
+
+. ../common/test_common.sh
+
+
+echo "=== Simulator hello world ==="
+RESULT="OK"
+do_curl GET / 200
+
+echo "=== Check used and implemented interfaces ==="
+RESULT="Current interface: STD_2.0.0 All supported A1 interface yamls in this container: ['OSC_2.1.0', 'STD_1.1.3', 'STD_2.0.0']"
+do_curl GET /container_interfaces 200
+
+echo "=== Reset simulator instances ==="
+RESULT="All policy instances deleted"
+do_curl POST /deleteinstances 200
+
+echo "=== Reset simulator, all ==="
+RESULT="All policy instances and types deleted"
+do_curl POST /deleteall 200
+
+echo "=== Get counter: interface ==="
+RESULT="STD_2.0.0"
+do_curl GET /counter/interface 200
+
+echo "=== Get counter: remote hosts ==="
+RESULT="*"
+do_curl GET /counter/remote_hosts 200
+
+echo "=== Get counter: intstance ==="
+RESULT="0"
+do_curl GET /counter/num_instances 200
+
+echo "=== Get counter: types (shall be 0)==="
+RESULT="0"
+do_curl GET /counter/num_types 200
+
+echo "=== API: Get policy types, shall be empty array =="
+RESULT="json:[]"
+do_curl GET /A1-P/v2/policytypes 200
+
+echo "=== API: Get policy instances for type 1, type not found=="
+RESULT="json:{\"title\": \"The policy type does not exist.\", \"status\": 404, \"instance\": \"1\"}"
+do_curl GET /A1-P/v2/policytypes/1/policies 404
+
+echo "=== API: Get policy instances, type not found=="
+RESULT="json:{\"title\": \"The policy type does not exist.\", \"status\": 404, \"instance\": \"test\"}"
+do_curl GET /A1-P/v2/policytypes/test/policies 404
+
+echo "=== Put a policy type: STD_1 ==="
+RESULT="Policy type STD_1 is OK."
+do_curl PUT  '/policytype?id=STD_1' 201 jsonfiles/std_1.json
+
+echo "=== Put a policy type: STD_1, again ==="
+RESULT="Policy type STD_1 is OK."
+do_curl PUT  '/policytype?id=STD_1' 200 jsonfiles/std_1.json
+
+echo "=== API: Get policy type ids, shall contain type STD_1 =="
+RESULT="json:[ \"STD_1\" ]"
+do_curl GET /A1-P/v2/policytypes 200
+
+echo "=== Delete a policy type: STD_1 ==="
+RESULT=""
+do_curl DELETE  '/policytype?id=STD_1' 204
+
+echo "=== API: Get policy type ids, shall be empty =="
+RESULT="json:[]"
+do_curl GET  /A1-P/v2/policytypes 200
+
+echo "=== Put a policy type: STD_1 ==="
+RESULT="Policy type STD_1 is OK."
+do_curl PUT  '/policytype?id=STD_1' 201 jsonfiles/std_1.json
+
+echo "=== API: Get policy type ids, shall contain type STD_1 =="
+RESULT="json:[ \"STD_1\" ]"
+do_curl GET /A1-P/v2/policytypes 200
+
+echo "=== Get counter: types (shall be 1)==="
+RESULT="1"
+do_curl GET /counter/num_types 200
+
+echo "=== API: Get policy type: STD_1 ==="
+res=$(cat jsonfiles/std_1.json)
+RESULT="json:$res"
+do_curl GET /A1-P/v2/policytypes/STD_1 200
+
+echo "=== API: Get policy instances, shall be empty=="
+RESULT="json:[ ]"
+do_curl GET /A1-P/v2/policytypes/STD_1/policies 200
+
+echo "=== API: Create policy instance pi1 of type: STD_1 ==="
+res=$(cat jsonfiles/pi1.json)
+RESULT="json:$res"
+do_curl PUT /A1-P/v2/policytypes/STD_1/policies/pi1 201 jsonfiles/pi1.json
+
+echo "=== API: Get policy instance pi1 of type: STD_1 ==="
+res=$(cat jsonfiles/pi1.json)
+RESULT="json:$res"
+do_curl GET /A1-P/v2/policytypes/STD_1/policies/pi1 200
+
+echo "=== API: Update policy instance pi1 of type: STD_1==="
+res=$(cat jsonfiles/pi1.json)
+RESULT="json:$res"
+do_curl PUT /A1-P/v2/policytypes/STD_1/policies/pi1 200 jsonfiles/pi1.json
+
+echo "=== API: Update policy instance pi1 of type: STD_1==="
+res=$(cat jsonfiles/pi1_updated.json)
+RESULT="json:$res"
+do_curl PUT /A1-P/v2/policytypes/STD_1/policies/pi1 200 jsonfiles/pi1_updated.json
+
+echo "=== API: Duplicate policy instance pi2 of type: STD_1==="
+res=$(cat jsonfiles/pi1_updated.json)
+RESULT="json:{\"title\": \"Duplicate, the policy json already exists.\", \"status\": 400, \"instance\": \"pi2\"}"
+do_curl PUT /A1-P/v2/policytypes/STD_1/policies/pi2 400 jsonfiles/pi1_updated.json
+
+echo "=== API: Get policy instances, shall contain pi1=="
+RESULT="json:[ \"pi1\" ]"
+do_curl GET /A1-P/v2/policytypes/STD_1/policies 200
+
+echo "=== Get counter: types (shall be 1)==="
+RESULT="1"
+do_curl GET /counter/num_types 200
+
+echo "=== Get counter: intstance ==="
+RESULT="1"
+do_curl GET /counter/num_instances 200
+
+
+echo "=== Set force response code 409. ==="
+RESULT="*"
+do_curl POST '/forceresponse?code=409' 200
+
+echo "=== API: Get policy instances, shall fail with 409 =="
+RESULT="json:{\"title\": \"Conflict\", \"status\": 409, \"detail\": \"Request could not be processed in the current state of the resource\"}"
+do_curl GET /A1-P/v2/policytypes/STD_1/policies 409
+
+echo "=== API: Get policy status ==="
+RESULT="json:{\"enforceStatus\": \"\", \"enforceReason\": \"\"}"
+do_curl GET /A1-P/v2/policytypes/STD_1/policies/pi1/status 200
+
+echo "=== API: Create policy instance pi2 of type: STD_1 ==="
+res=$(cat jsonfiles/pi2.json)
+RESULT="json:$res"
+do_curl PUT /A1-P/v2/policytypes/STD_1/policies/pi2 201 jsonfiles/pi2.json
+
+echo "=== API: Update policy instance pi2 of type: STD_1 ==="
+res=$(cat jsonfiles/pi2.json)
+RESULT="json:$res"
+do_curl PUT '/A1-P/v2/policytypes/STD_1/policies/pi2?notificationDestination=http://localhost:2223/statustest' 200 jsonfiles/pi2.json
+
+echo "=== API: Get policy instances, shall contain pi1 and pi2=="
+RESULT="json:[ \"pi1\", \"pi2\" ]"
+do_curl GET /A1-P/v2/policytypes/STD_1/policies 200
+
+echo "=== Get counter: types (shall be 1)==="
+RESULT="1"
+do_curl GET /counter/num_types 200
+
+echo "=== Get counter: intstance ==="
+RESULT="2"
+do_curl GET /counter/num_instances 200
+
+echo "=== Set force delay 10. ==="
+RESULT="Force delay: 10 sec set for all A1 responses"
+do_curl POST '/forcedelay?delay=10' 200
+
+echo "=== API: Get policy instances, shall contain pi1 and pi2=="
+RESULT="json:[ \"pi1\", \"pi2\" ]"
+do_curl GET /A1-P/v2/policytypes/STD_1/policies 200
+
+echo "=== Reset force delay. ==="
+RESULT="Force delay: None sec set for all A1 responses"
+do_curl POST /forcedelay 200
+
+echo "=== API: Get policy instance pi1 of type: STD_1 ==="
+res=$(cat jsonfiles/pi1_updated.json)
+RESULT="json:$res"
+do_curl GET /A1-P/v2/policytypes/STD_1/policies/pi1 200
+
+echo "=== API: Get policy instance pi2 of type: STD_1 ==="
+res=$(cat jsonfiles/pi2.json)
+RESULT="json:$res"
+do_curl GET /A1-P/v2/policytypes/STD_1/policies/pi2 200
+
+echo "=== API: DELETE policy instance pi1 ==="
+RESULT=""
+do_curl DELETE /A1-P/v2/policytypes/STD_1/policies/pi1 204
+
+echo "=== API: Get policy instances, shall contain pi1 and pi2=="
+RESULT="json:[ \"pi2\" ]"
+do_curl GET /A1-P/v2/policytypes/STD_1/policies 200
+
+echo "=== API: Get policy status ==="
+RESULT="json:{\"enforceStatus\": \"\", \"enforceReason\": \"\"}"
+do_curl GET /A1-P/v2/policytypes/STD_1/policies/pi2/status 200
+
+echo "=== Set status for policy instance pi2 ==="
+RESULT="Status set to OK for policy: pi2"
+do_curl PUT '/status?policyid=pi2&status=OK' 200
+
+echo "=== API: Get policy status ==="
+RESULT="json:{\"enforceStatus\": \"OK\"}"
+do_curl GET /A1-P/v2/policytypes/STD_1/policies/pi2/status 200
+
+echo "=== Set status for policy instance pi2 ==="
+RESULT="Status set to NOTOK and notok_reason for policy: pi2"
+do_curl PUT '/status?policyid=pi2&status=NOTOK&reason=notok_reason' 200
+
+echo "=== API: Get policy status ==="
+RESULT="json:{\"enforceStatus\": \"NOTOK\", \"enforceReason\":\"notok_reason\"}"
+do_curl GET /A1-P/v2/policytypes/STD_1/policies/pi2/status 200
+
+echo "=== Send status for pi2==="
+RESULT="json:{\"enforceStatus\": \"NOTOK\", \"enforceReason\": \"notok_reason\"}"
+do_curl POST '/sendstatus?policyid=pi2' 200
+
+echo "=== Get counter: datadelivery ==="
+RESULT="0"
+do_curl GET /counter/datadelivery 200
+
+echo "=== Send data ==="
+echo "{}" > .p.json
+RESULT=""
+do_curl POST /datadelivery 200 .p.json
+
+echo "=== Get counter: datadelivery ==="
+RESULT="1"
+do_curl GET /counter/datadelivery 200
+
+echo "=== Get counter: intstance ==="
+RESULT="1"
+do_curl GET /counter/num_instances 200
+
+echo "=== Get counter: types (shall be 0)==="
+RESULT="1"
+do_curl GET /counter/num_types 200
+
+echo "=== Get counter: interface ==="
+RESULT="STD_2.0.0"
+do_curl GET /counter/interface 200
+
+echo "=== Get counter: remote hosts ==="
+RESULT="*"
+do_curl GET /counter/remote_hosts 200
+
+echo "********************"
+echo "*** All tests ok ***"
+echo "********************"
diff --git a/near-rt-ric-simulator/test/STD_2.0.0/build_and_start.sh b/near-rt-ric-simulator/test/STD_2.0.0/build_and_start.sh
new file mode 100755 (executable)
index 0000000..2cba8c1
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+#  ============LICENSE_START===============================================
+#  Copyright (C) 2020 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.
+#  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#  ============LICENSE_END=================================================
+#
+
+# Script to build and start the container
+
+echo "Building image"
+cd ../../
+
+#Build the image
+docker build -t a1test .
+
+docker stop a1StdSimulator > /dev/null 2>&1
+docker rm -f a1StdSimulator > /dev/null 2>&1
+
+echo "Starting ric-sim"
+#Run the container in interactive mode, unsecure port 8085, secure port 8185
+docker run -it -p 8085:8085 -p 8185:8185 -e A1_VERSION=STD_2.0.0 -e ALLOW_HTTP=true -e REMOTE_HOSTS_LOGGING=1 --volume "$PWD/certificate:/usr/src/app/cert" --name a1StdSimulator a1test
diff --git a/near-rt-ric-simulator/test/STD_2.0.0/jsonfiles/pi1.json b/near-rt-ric-simulator/test/STD_2.0.0/jsonfiles/pi1.json
new file mode 100644 (file)
index 0000000..25247a1
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "scope": {
+    "ueId": "ue1",
+    "qosId": "qos1"
+  },
+  "statement": {
+    "priorityLevel": 5
+  }
+}
+
diff --git a/near-rt-ric-simulator/test/STD_2.0.0/jsonfiles/pi1_updated.json b/near-rt-ric-simulator/test/STD_2.0.0/jsonfiles/pi1_updated.json
new file mode 100644 (file)
index 0000000..7b4ada7
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "scope": {
+    "ueId": "ue1",
+    "qosId": "qos1"
+  },
+  "statement": {
+    "priorityLevel": 6
+  }
+}
+
diff --git a/near-rt-ric-simulator/test/STD_2.0.0/jsonfiles/pi2.json b/near-rt-ric-simulator/test/STD_2.0.0/jsonfiles/pi2.json
new file mode 100644 (file)
index 0000000..441a9cf
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "scope": {
+    "ueId": "ue2",
+    "qosId": "qos2"
+  },
+  "statement": {
+    "priorityLevel": 5
+  }
+}
+
diff --git a/near-rt-ric-simulator/test/STD_2.0.0/jsonfiles/std_1.json b/near-rt-ric-simulator/test/STD_2.0.0/jsonfiles/std_1.json
new file mode 100644 (file)
index 0000000..9b0c302
--- /dev/null
@@ -0,0 +1,56 @@
+{
+  "policySchema": {
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "title": "STD_1_0.2.0",
+    "description": "STD 1 policy type",
+    "type": "object",
+    "properties": {
+      "scope": {
+        "type": "object",
+        "properties": {
+          "ueId": {
+            "type": "string"
+          },
+          "qosId": {
+            "type": "string"
+          }
+        },
+        "additionalProperties": false,
+        "required": [
+          "ueId",
+          "qosId"
+        ]
+      },
+      "statement": {
+        "type": "object",
+        "properties": {
+          "priorityLevel": {
+            "type": "number"
+          }
+        },
+        "additionalProperties": false,
+        "required": [
+          "priorityLevel"
+        ]
+      }
+    }
+  },
+  "statusSchema": {
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "title": "STD_1_0.2.0",
+    "description": "STD 1 policy type status",
+    "type": "object",
+    "properties": {
+      "enforceStatus": {
+        "type": "string"
+      },
+      "enforceReason": {
+        "type": "string"
+      },
+      "additionalProperties": false,
+      "required": [
+        "enforceStatus"
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/near-rt-ric-simulator/test/common/.gitignore b/near-rt-ric-simulator/test/common/.gitignore
new file mode 100644 (file)
index 0000000..bee8a64
--- /dev/null
@@ -0,0 +1 @@
+__pycache__
index 6c669c1..a45a6ff 100755 (executable)
@@ -36,7 +36,7 @@ do_curl() {
     if [ $# -gt 3 ]; then
         curlstr=$curlstr" -H Content-Type:application/json --data-binary @"$4
     fi
     if [ $# -gt 3 ]; then
         curlstr=$curlstr" -H Content-Type:application/json --data-binary @"$4
     fi
-    echo "  CMD:"$curlstr
+    echo "  CMD (${BASH_LINENO[0]}):"$curlstr
     res=$($curlstr)
     status=${res:${#res}-3}
     body=${res:0:${#res}-3}
     res=$($curlstr)
     status=${res:${#res}-3}
     body=${res:0:${#res}-3}
@@ -53,7 +53,6 @@ do_curl() {
         elif [[ "$RESULT" == "json:"* ]]; then
             result=${RESULT:5:${#RESULT}} #Remove 'json:' from the result string
             res=$(python ../common/compare_json.py "$result" "$body")
         elif [[ "$RESULT" == "json:"* ]]; then
             result=${RESULT:5:${#RESULT}} #Remove 'json:' from the result string
             res=$(python ../common/compare_json.py "$result" "$body")
-            echo $res
             if [ $res -eq 0 ]; then
                 echo "  Expected json body :"$result
                 echo "  Body as expected"
             if [ $res -eq 0 ]; then
                 echo "  Expected json body :"$result
                 echo "  Body as expected"
diff --git a/near-rt-ric-simulator/tests/.gitignore b/near-rt-ric-simulator/tests/.gitignore
new file mode 100644 (file)
index 0000000..bee8a64
--- /dev/null
@@ -0,0 +1 @@
+__pycache__
index 31e70fd..9d70ed0 100644 (file)
@@ -40,7 +40,7 @@ def test_apis(client):
     # Check used and implemented interfaces
     response=client.get(SERVER_URL+'container_interfaces')
     assert response.status_code == 200
     # Check used and implemented interfaces
     response=client.get(SERVER_URL+'container_interfaces')
     assert response.status_code == 200
-    assert response.data ==  b"Current interface: OSC_2.1.0  All supported A1 interface yamls in this container: ['OSC_2.1.0', 'STD_1.1.3']"
+    assert response.data ==  b"Current interface: OSC_2.1.0  All supported A1 interface yamls in this container: ['OSC_2.1.0', 'STD_1.1.3', 'STD_2.0.0']"
 
     # Reset simulator instances
     response=client.post(SERVER_URL+'deleteinstances')
 
     # Reset simulator instances
     response=client.post(SERVER_URL+'deleteinstances')
index 0d2959a..721d7a6 100644 (file)
@@ -38,7 +38,7 @@ def test_apis(client):
     # Check used and implemented interfaces
     response=client.get(SERVER_URL+'container_interfaces')
     assert response.status_code == 200
     # Check used and implemented interfaces
     response=client.get(SERVER_URL+'container_interfaces')
     assert response.status_code == 200
-    assert response.data ==  b"Current interface: STD_1.1.3  All supported A1 interface yamls in this container: ['OSC_2.1.0', 'STD_1.1.3']"
+    assert response.data ==  b"Current interface: STD_1.1.3  All supported A1 interface yamls in this container: ['OSC_2.1.0', 'STD_1.1.3', 'STD_2.0.0']"
 
     # Reset simulator instances
     response=client.post(SERVER_URL+'deleteinstances')
 
     # Reset simulator instances
     response=client.post(SERVER_URL+'deleteinstances')
diff --git a/near-rt-ric-simulator/tests/test_std_2_0_0.py b/near-rt-ric-simulator/tests/test_std_2_0_0.py
new file mode 100644 (file)
index 0000000..d63d4c3
--- /dev/null
@@ -0,0 +1,436 @@
+#  ============LICENSE_START===============================================
+#  Copyright (C) 2020 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.
+#  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#  ============LICENSE_END=================================================
+#
+
+# This test case test the STD_2.0.0 version of the simulator
+
+import json
+import time
+
+#Version of simulator
+INTERFACE_VERSION="STD_2.0.0"
+
+from unittest_setup import SERVER_URL, HOST_IP, PORT_NUMBER, setup_env, get_testdata_dir, client
+
+#Setup env and import paths
+setup_env(INTERFACE_VERSION)
+
+from compare_json import compare
+
+def test_apis(client):
+
+    testdata=get_testdata_dir()
+
+    # Header for json payload
+    header = {
+        "Content-Type" : "application/json"
+    }
+
+    # Simulator hello world
+    response=client.get(SERVER_URL)
+    assert response.status_code == 200
+
+    # Check used and implemented interfaces
+    response=client.get(SERVER_URL+'container_interfaces')
+    assert response.status_code == 200
+    assert response.data ==  b"Current interface: STD_2.0.0  All supported A1 interface yamls in this container: ['OSC_2.1.0', 'STD_1.1.3', 'STD_2.0.0']"
+
+    # Reset simulator instances
+    response=client.post(SERVER_URL+'deleteinstances')
+    assert response.status_code == 200
+
+    # Reset simulator, all
+    response=client.post(SERVER_URL+'deleteall')
+    assert response.status_code == 200
+
+    # Get counter: interface
+    response=client.get(SERVER_URL+'counter/interface')
+    assert response.status_code == 200
+    assert response.data ==  b"STD_2.0.0"
+
+    # Get counter: remote hosts
+    response=client.get(SERVER_URL+'counter/remote_hosts')
+    assert response.status_code == 200
+
+    # Get counter: intstance
+    response=client.get(SERVER_URL+'counter/num_instances')
+    assert response.status_code == 200
+    assert response.data ==  b"0"
+
+    # Get counter: types
+    response=client.get(SERVER_URL+'counter/num_types')
+    assert response.status_code == 200
+    assert response.data ==  b"0"
+
+    # API: Get policy type, shall be empty
+    data_response = [ ]
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes')
+    assert response.status_code == 200
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+    # API: Get policy instances for type 1, type not found
+    data_response = {"title": "The policy type does not exist.", "status": 404, "instance": "1"}
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes/1/policies')
+    assert response.status_code == 404
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+    # API: Get policy instances, type not found
+    data_response = {"title": "The policy type does not exist.", "status": 404, "instance": "test"}
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes/test/policies')
+    assert response.status_code == 404
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+    # Put a policy type: STD_1
+    with open(testdata+'std_1.json') as json_file:
+        data_response = b"Policy type STD_1 is OK."
+        json_payload=json.load(json_file)
+        response=client.put(SERVER_URL+'policytype?id=STD_1', headers=header, data=json.dumps(json_payload))
+        assert response.status_code == 201
+        assert data_response == response.data
+
+    # Put a policy type: STD_1, again
+    with open(testdata+'std_1.json') as json_file:
+        data_response = b"Policy type STD_1 is OK."
+        json_payload=json.load(json_file)
+        response=client.put(SERVER_URL+'policytype?id=STD_1', headers=header, data=json.dumps(json_payload))
+        assert response.status_code == 200
+        assert data_response == response.data
+
+    # API: Get policy type ids, shall contain type STD_1
+    data_response = [ "STD_1" ]
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes')
+    assert response.status_code == 200
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+    # Delete a policy type: STD_1
+    data_response = b""
+    response=client.delete(SERVER_URL+'policytype?id=STD_1')
+    assert response.status_code == 204
+    assert data_response == response.data
+
+    # API: Get policy type ids, shall be empty
+    data_response = [  ]
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes')
+    assert response.status_code == 200
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+    # Put a policy type: STD_1
+    with open(testdata+'std_1.json') as json_file:
+        data_response = b"Policy type STD_1 is OK."
+        json_payload=json.load(json_file)
+        response=client.put(SERVER_URL+'policytype?id=STD_1', headers=header, data=json.dumps(json_payload))
+        assert response.status_code == 201
+        assert data_response == response.data
+
+    # API: Get policy type ids, shall contain type STD_1
+    data_response = [ "STD_1" ]
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes')
+    assert response.status_code == 200
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+    # Get counter: types (shall be 1)
+    response=client.get(SERVER_URL+'counter/num_types')
+    assert response.status_code == 200
+    assert response.data ==  b"1"
+
+    # API: Get policy type: STD_1
+    with open(testdata+'std_1.json') as json_file:
+        data_response = json.load(json_file)
+        response=client.get(SERVER_URL+'A1-P/v2/policytypes/STD_1')
+        assert response.status_code == 200
+        result=json.loads(response.data)
+        res=compare(data_response, result)
+        assert res == True
+
+    # API: API: Get policy instances, shall be empty
+    data_response = []
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies')
+    assert response.status_code == 200
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+    # API: Create policy instance pi1 of type: STD_1
+    with open(testdata+'pi1.json') as json_file:
+        json_payload=json.load(json_file)
+        response=client.put(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies/pi1', headers=header, data=json.dumps(json_payload))
+        assert response.status_code == 201
+        result=json.loads(response.data)
+        res=compare(json_payload, result)
+        assert res == True
+
+    # API: API: Get policy instance pi1 of type: STD_1
+    with open(testdata+'pi1.json') as json_file:
+        data_response = json.load(json_file)
+        response=client.get(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies/pi1')
+        assert response.status_code == 200
+        result=json.loads(response.data)
+        res=compare(data_response, result)
+        assert res == True
+
+    # API: Update policy instance pi1 of type: STD_1
+    with open(testdata+'pi1.json') as json_file:
+        json_payload=json.load(json_file)
+        response=client.put(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies/pi1', headers=header, data=json.dumps(json_payload))
+        assert response.status_code == 200
+        result=json.loads(response.data)
+        res=compare(json_payload, result)
+        assert res == True
+
+    # API: Update policy instance pi1 of type: STD_1
+    with open(testdata+'pi1_updated.json') as json_file:
+        json_payload=json.load(json_file)
+        response=client.put(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies/pi1', headers=header, data=json.dumps(json_payload))
+        assert response.status_code == 200
+        result=json.loads(response.data)
+        res=compare(json_payload, result)
+        assert res == True
+
+    # API: Duplicate policy instance pi2 of type: STD_1
+    with open(testdata+'pi1_updated.json') as json_file:
+        data_response = {"title": "Duplicate, the policy json already exists.", "status": 400, "instance": "pi2"}
+        json_payload=json.load(json_file)
+        response=client.put(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies/pi2', headers=header, data=json.dumps(json_payload))
+        assert response.status_code == 400
+        result=json.loads(response.data)
+        res=compare(data_response, result)
+        assert res == True
+
+    # API: Get policy instances, shall contain pi1
+    data_response = ["pi1"]
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies')
+    assert response.status_code == 200
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+    # Get counter: intstance
+    response=client.get(SERVER_URL+'counter/num_instances')
+    assert response.status_code == 200
+    assert response.data ==  b"1"
+
+    # Get counter: types
+    response=client.get(SERVER_URL+'counter/num_types')
+    assert response.status_code == 200
+    assert response.data ==  b"1"
+
+    # Set force response code 409. ==="
+    response=client.post(SERVER_URL+'forceresponse?code=409')
+    assert response.status_code == 200
+
+    # API: Get policy instances, shall fail
+    data_response = {"title" : "Conflict", "status" : 409, "detail" : "Request could not be processed in the current state of the resource"}
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies')
+    assert response.status_code == 409
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+    # API: API: Get policy status
+    data_response = {"enforceStatus" : "", "enforceReason" : ""}
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies/pi1/status')
+    assert response.status_code == 200
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+    # API: Create policy instance pi2 of type: STD_1
+    with open(testdata+'pi2.json') as json_file:
+        json_payload=json.load(json_file)
+        response=client.put(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies/pi2', headers=header, data=json.dumps(json_payload))
+        assert response.status_code == 201
+        result=json.loads(response.data)
+        res=compare(json_payload, result)
+        assert res == True
+
+    # API: Update policy instance pi2 of type: STD_1
+    with open(testdata+'pi2.json') as json_file:
+        json_payload=json.load(json_file)
+        response=client.put(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies/pi2', headers=header, data=json.dumps(json_payload))
+        assert response.status_code == 200
+        result=json.loads(response.data)
+        res=compare(json_payload, result)
+        assert res == True
+
+    # API: Get policy instances, shall contain pi1 and pi2
+    data_response = ["pi1","pi2"]
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies')
+    assert response.status_code == 200
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+    # Get counter: intstance
+    response=client.get(SERVER_URL+'counter/num_instances')
+    assert response.status_code == 200
+    assert response.data ==  b"2"
+
+    # Get counter: types
+    response=client.get(SERVER_URL+'counter/num_types')
+    assert response.status_code == 200
+    assert response.data ==  b"1"
+
+    # Set force delay 10
+    response=client.post(SERVER_URL+'forcedelay?delay=10')
+    assert response.status_code == 200
+    assert response.data ==  b"Force delay: 10 sec set for all A1 responses"
+
+    #start time stamp
+    start=time.time()
+
+    # API: Get policy instances, shall contain pi1 and pi2 and delayed 10 sec
+    data_response = ["pi1","pi2"]
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies')
+    assert response.status_code == 200
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+    end=time.time()
+
+    assert (end-start) > 9
+
+    # Reset force delay
+    response=client.post(SERVER_URL+'forcedelay')
+    assert response.status_code == 200
+    assert response.data ==  b"Force delay: None sec set for all A1 responses"
+
+    # API: API: Get policy instance pi1 of type: STD_1
+    with open(testdata+'pi1_updated.json') as json_file:
+        data_response = json.load(json_file)
+        response=client.get(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies/pi1')
+        assert response.status_code == 200
+        result=json.loads(response.data)
+        res=compare(data_response, result)
+        assert res == True
+
+    # API: API: Get policy instance pi2 of type: STD_1
+    with open(testdata+'pi2.json') as json_file:
+        data_response = json.load(json_file)
+        response=client.get(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies/pi2')
+        assert response.status_code == 200
+        result=json.loads(response.data)
+        res=compare(data_response, result)
+        assert res == True
+
+    # API: DELETE policy instance pi1
+    data_response = b""
+    response=client.delete(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies/pi1')
+    assert response.status_code == 204
+    assert data_response == response.data
+
+    # API: Get policy instances, shall contain pi2
+    data_response = ["pi2"]
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies')
+    assert response.status_code == 200
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+    # API: API: Get policy status
+    data_response = {"enforceStatus" : "", "enforceReason" : ""}
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies/pi2/status')
+    assert response.status_code == 200
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+    # Set status for policy instance pi2
+    response=client.put(SERVER_URL+'status?policyid=pi2&status=OK')
+    assert response.status_code == 200
+
+    # API: API: Get policy status
+    data_response = {"enforceStatus" : "OK"}
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies/pi2/status')
+    assert response.status_code == 200
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    print(response.data)
+    assert res == True
+
+    # Set status for policy instance pi2
+    response=client.put(SERVER_URL+'status?policyid=pi2&status=NOTOK&reason=notok_reason')
+    assert response.status_code == 200
+
+    # API: API: Get policy status
+    data_response = {"enforceStatus" : "NOTOK", "enforceReason" : "notok_reason"}
+    response=client.get(SERVER_URL+'A1-P/v2/policytypes/STD_1/policies/pi2/status')
+    assert response.status_code == 200
+    result=json.loads(response.data)
+    res=compare(data_response, result)
+    assert res == True
+
+
+    # #Found no way to test these functions
+    # #'sendstatus' will send a http request that will fail
+    # #since no server will receive the call
+    # #These function is instead tested when running the bash script in the 'test' dir
+    # # # Send status for pi2
+    # # response=client.post(SERVER_URL+'sendstatus?policyid=pi2')
+    # # assert response.status_code == 200
+    # # result=json.loads(response.data)
+    # # res=compare(data_get_status, result)
+    # # assert res == True
+
+    # # # Send status, shall fail
+    # # response=client.post(SERVER_URL+'sendstatus')
+    # # 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
+    json_payload={}
+    response=client.post(SERVER_URL+'datadelivery', 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"1"
+
+    # Get counter: interface
+    response=client.get(SERVER_URL+'counter/interface')
+    assert response.status_code == 200
+    assert response.data ==  b"STD_2.0.0"
+
+    # Get counter: remote hosts
+    response=client.get(SERVER_URL+'counter/remote_hosts')
+    assert response.status_code == 200
+
+    # Get counter: intstance
+    response=client.get(SERVER_URL+'counter/num_instances')
+    assert response.status_code == 200
+    assert response.data ==  b"1"
+
+    # Get counter: types
+    response=client.get(SERVER_URL+'counter/num_types')
+    assert response.status_code == 200
+    assert response.data ==  b"1"
\ No newline at end of file
diff --git a/tox.ini b/tox.ini
index e27e15d..8250799 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -41,6 +41,8 @@ commands =
     {toxinidir}/near-rt-ric-simulator/tests/test_osc_2_1_0.py
     pytest --cov-append --cov {toxinidir}/near-rt-ric-simulator --cov-report xml --cov-report term-missing --cov-report html \
     --cov-fail-under=70 {toxinidir}/near-rt-ric-simulator/tests/test_std_1_1_3.py
     {toxinidir}/near-rt-ric-simulator/tests/test_osc_2_1_0.py
     pytest --cov-append --cov {toxinidir}/near-rt-ric-simulator --cov-report xml --cov-report term-missing --cov-report html \
     --cov-fail-under=70 {toxinidir}/near-rt-ric-simulator/tests/test_std_1_1_3.py
+    pytest --cov-append --cov {toxinidir}/near-rt-ric-simulator --cov-report xml --cov-report term-missing --cov-report html \
+    --cov-fail-under=70 {toxinidir}/near-rt-ric-simulator/tests/test_std_2_0_0.py
     coverage xml -i
 
 # doc jobs
     coverage xml -i
 
 # doc jobs