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
+
+.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     |
+| 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.
 
@@ -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} |
-|  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).
@@ -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; |
 
+# 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
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 == "datadelivery"):
+    return Response(str(0),200, 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 == "datadelivery"):
+    return Response(str(0),200, 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
-if [ ${A1_VERSION} == "STD_1.1.3" ]; then
+if [[ ${A1_VERSION} == "STD"* ]]; then
     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 ==="
-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 ==="
index e182782..f376971 100755 (executable)
@@ -25,6 +25,9 @@ cd ../../
 #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
index 77f10a6..3a828d2 100755 (executable)
@@ -48,7 +48,7 @@ RESULT="OK"
 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 ==="
index fa59f49..1d56487 100755 (executable)
@@ -25,6 +25,9 @@ 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_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
-    echo "  CMD:"$curlstr
+    echo "  CMD (${BASH_LINENO[0]}):"$curlstr
     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")
-            echo $res
             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
-    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')
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
-    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')
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
+    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