Added multiple A1 support in Near-RT RIC simulator 71/2971/4
authorBjornMagnussonXA <bjorn.magnusson@est.tech>
Wed, 25 Mar 2020 08:37:14 +0000 (09:37 +0100)
committerBjornMagnussonXA <bjorn.magnusson@est.tech>
Wed, 1 Apr 2020 09:37:05 +0000 (11:37 +0200)
Change-Id: If4927690ea19fca7148809cd0c853cb29887fc7e

Issue-ID: NONRTRIC-120

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

41 files changed:
.gitignore [new file with mode: 0644]
near-rt-ric-simulator/Dockerfile [new file with mode: 0644]
near-rt-ric-simulator/README.md
near-rt-ric-simulator/api/1.1.x-alpha.2/a1-openapi.yaml [moved from near-rt-ric-simulator/a1-openapi.yaml with 100% similarity]
near-rt-ric-simulator/api/OSC_2.1.0/openapi.yaml [new file with mode: 0644]
near-rt-ric-simulator/api/STD_1.1.3/STD_A1.yaml [new file with mode: 0644]
near-rt-ric-simulator/container-tag.yaml [moved from near-rt-ric-simulator/scripts/container-tag.yaml with 100% similarity]
near-rt-ric-simulator/scripts/Dockerfile [deleted file]
near-rt-ric-simulator/scripts/var_declaration.py [deleted file]
near-rt-ric-simulator/src/1.1.x-alpha.2/a1.py [moved from near-rt-ric-simulator/scripts/a1.py with 97% similarity]
near-rt-ric-simulator/src/1.1.x-alpha.2/main.py [moved from near-rt-ric-simulator/scripts/main.py with 90% similarity]
near-rt-ric-simulator/src/1.1.x-alpha.2/var_declaration.py [moved from near-rt-ric-simulator/scripts/start_simulator.sh with 85% similarity, mode: 0644]
near-rt-ric-simulator/src/OSC_2.1.0/a1.py [new file with mode: 0644]
near-rt-ric-simulator/src/OSC_2.1.0/main.py [new file with mode: 0644]
near-rt-ric-simulator/src/OSC_2.1.0/var_declaration.py [new file with mode: 0644]
near-rt-ric-simulator/src/STD_1.1.3/a1.py [new file with mode: 0644]
near-rt-ric-simulator/src/STD_1.1.3/main.py [new file with mode: 0644]
near-rt-ric-simulator/src/STD_1.1.3/var_declaration.py [new file with mode: 0644]
near-rt-ric-simulator/src/common/maincommon.py [new file with mode: 0644]
near-rt-ric-simulator/src/common/utils.py [new file with mode: 0644]
near-rt-ric-simulator/src/start.sh [new file with mode: 0755]
near-rt-ric-simulator/test/1.1.x-alpha.2/build_and_start.sh [new file with mode: 0755]
near-rt-ric-simulator/test/1.1.x-alpha.2/commands.sh [moved from near-rt-ric-simulator/scripts/commands.sh with 100% similarity]
near-rt-ric-simulator/test/1.1.x-alpha.2/example_files/policy_instance_1_STD_QoSNudging_0.2.0.json [moved from near-rt-ric-simulator/scripts/example_files/policy_instance_1_STD_QoSNudging_0.2.0.json with 100% similarity]
near-rt-ric-simulator/test/1.1.x-alpha.2/example_files/policy_instance_1_bis_STD_QoSNudging_0.2.0.json [moved from near-rt-ric-simulator/scripts/example_files/policy_instance_1_bis_STD_QoSNudging_0.2.0.json with 100% similarity]
near-rt-ric-simulator/test/1.1.x-alpha.2/example_files/policy_instance_2_STD_QoSNudging_0.2.0.json [moved from near-rt-ric-simulator/scripts/example_files/policy_instance_2_STD_QoSNudging_0.2.0.json with 100% similarity]
near-rt-ric-simulator/test/1.1.x-alpha.2/example_files/policy_type_STD_QoSNudging_0.2.0.json [moved from near-rt-ric-simulator/scripts/example_files/policy_type_STD_QoSNudging_0.2.0.json with 100% similarity]
near-rt-ric-simulator/test/OSC_2.1.0/basic_test.sh [new file with mode: 0755]
near-rt-ric-simulator/test/OSC_2.1.0/build_and_start.sh [new file with mode: 0755]
near-rt-ric-simulator/test/OSC_2.1.0/jsonfiles/pi1.json [new file with mode: 0644]
near-rt-ric-simulator/test/OSC_2.1.0/jsonfiles/pi2.json [new file with mode: 0644]
near-rt-ric-simulator/test/OSC_2.1.0/jsonfiles/pi2_missing_param.json [new file with mode: 0644]
near-rt-ric-simulator/test/OSC_2.1.0/jsonfiles/pt1.json [new file with mode: 0644]
near-rt-ric-simulator/test/OSC_2.1.0/jsonfiles/pt2.json [new file with mode: 0644]
near-rt-ric-simulator/test/STD_1.1.3/basic_test.sh [new file with mode: 0755]
near-rt-ric-simulator/test/STD_1.1.3/build_and_start.sh [new file with mode: 0755]
near-rt-ric-simulator/test/STD_1.1.3/jsonfiles/pi1.json [new file with mode: 0644]
near-rt-ric-simulator/test/STD_1.1.3/jsonfiles/pi1_updated.json [new file with mode: 0644]
near-rt-ric-simulator/test/STD_1.1.3/jsonfiles/pi2.json [new file with mode: 0644]
near-rt-ric-simulator/test/common/compare_json.py [new file with mode: 0644]
near-rt-ric-simulator/test/common/test_common.sh [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..3a4edf6
--- /dev/null
@@ -0,0 +1 @@
+.project
diff --git a/near-rt-ric-simulator/Dockerfile b/near-rt-ric-simulator/Dockerfile
new file mode 100644 (file)
index 0000000..4eaf9ed
--- /dev/null
@@ -0,0 +1,28 @@
+#  ============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=================================================
+#
+
+FROM python:3.8-slim-buster
+
+WORKDIR /usr/src/app
+
+RUN pip install connexion[swagger-ui]
+
+COPY src src
+
+COPY api api
+
+CMD src/start.sh ${A1_VERSION}
index 5cd45c0..99a6d2e 100644 (file)
@@ -2,7 +2,79 @@
 
 The O-RAN SC Near-RealTime RIC simulates the A1 as an generic REST API which can receive and send northbound messages. The simulator validates the payload and applies policy.
 
-The simulator handles the requests that are defined in the A1 open API yaml file. All these requests are simulated in the a1.py file. The available requests and the addresses are currently:
+The simulator supports multiple A1 interface versions (version of the open API yaml file):
+- OSC 2.1.0,                   version-id: OSC\_2.1.0
+- A1 Standard 1.1.3,           version-id: STD\_1.1.3
+- 1.1.x-alpha.2 ,              version-id: 1.1.x-alpha.2
+
+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 correct version.
+
+The overall folder structure is (relative to the location of this README file):
+.                 Dockerfile and README
+api               The open api yaml for each supported version
+src               Python source code for each supported version
+test              Basic test
+
+The simulator handles the requests that are defined in the A1 open API yaml file. All these requests are in the a1.py file in the source folder. In addition, a number of administrative functions are also supported and implemented by the main.py in the source folder.
+
+The section below outlines the supported open api REST operations as well as the adminstrative REST operations.
+
+# Supported operations in simulator OSC 2.1.0
+
+URIs for A1:
+ - GET, do a healthcheck: http://localhost:8085/healthcheck
+ - GET, get all policy type ids: http://localhost:8085/policytypes
+ - DELETE, delete a policy type: http://localhost:8085/policytypes/{policy\_type\_id}
+ - GET, get a policy type: http://localhost:8085/policytypes/{policy\_type\_id}
+ - PUT, create/update a policy type: http://localhost:8085/policytypes/{policy\_type\_id}
+ - GET, get all policy ids for a type: http://localhost:8085/policytypes/{policy\_type\_id}/policies
+ - DELETE, delete a policy: http://localhost:8085/policytypes/{policy\_type\_id}/policies/{policy\_instance\_id}
+ - GET, get a policy: http://localhost:8085/policytypes/{policy\_type\_id}/policies/{policy\_instance\_id}
+ - PUT, create/update a policy: http://localhost:8085/policytypes/{policy\_type\_id}/policies/{policy\_instance\_id}
+ - GET, get policy status: http://localhost:8085/policytypes/{policy\_type\_id}/policies/{policy\_instance\_id}/status
+<br>Swagger UI at:`http://localhost:8085/ui/`
+
+URIs for admin operations:
+ - 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?responsecode=&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, delete and timestamp: http://localhost:8085/status?status=&lt;status&gt;&ampreason=&lt;reason&gt;[&ampdeleted=&lt;boolean&gt;][&ampcreated\_at=&lt;timestamp&gt;]
+ - POST, send status for policy: http://localhost:8085/sendstatus?policyid=&lt;policyid&gt;
+ - GET a counter: http://localhost:8085/counter/&lt;counter-name&gt;
+   (counter-name: 'num\_instances', 'num\_types' or 'interface')
+
+# Supported operations in simulator A1 Standard 1.1.3
+
+URIs for A1:
+ - GET all policy identities http://localhost:8085/A1-P/v1/policies
+ - 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}
+<br>Swagger UI at:`http://localhost:8085/A1-P/v1/ui/`
+
+URIs for admin operations:
+ - 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
+ - POST, force a specific response code for an A1 operation: http://localhost:8085/forceresponse?responsecode=&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;&ampreason=&lt;reason&gt;
+ - POST, send status for policy: http://localhost:8085/sendstatus?policyid=&lt;policyid&gt;
+ - GET a counter: http://localhost:8085/counter/&lt;counter-name&gt;
+   (counter-name: 'num\_instances', 'num\_types'(always 0) or 'interface')
+
+
+# Supported operations in simulator 1.1.x-alpha.2
+
+ The available requests and the addresses are currently:
  - GET all policy identities (respectively for a policy type if query parameter used): http://localhost:8085/A1-P/v1/policies?policyTypeId={policyTypeId}
  - PUT a policy instance(create or update it): http://localhost:8085/A1-P/v1/policies/{policyId}?policyTypeId={policyTypeId}
  - GET a policy: http://localhost:8085/A1-P/v1/policies/{policyId}
@@ -22,21 +94,33 @@ Additionally, there are requests that are defined in main.py as an administrativ
  - PUT a status to a policy instance with an enforceStatus parameter only: http://localhost:8085/{policyId}/{enforceStatus}
  - PUT a status to a policy instance with both enforceStatus and enforceReason: http://localhost:8085/{policyId}/{enforceStatus}/{enforceReason}
 
-The backend server publishes live API documentation at the URL `http://your-host-name-here:8080/swagger-ui.html`
+The backend server publishes live API documentation at the URL `http://localhost:8085/A1-P/v1/ui/`
+
+# Configuring the simulator
+A env variable, A1\_VERSION need to be passed to the container at start to select the desired interface version. The variable shall be set to one of the version-ids shown in the table in the first section. For example A1\_VERSIION=STD\_1.1.3.
+In docker run the full command could look like this 'docker run -it -p 8085:8085 -e A1\_VERSION=STD\_1.1.3 a1test' where the variable is set with the '-e' flag.
 
-# Starting up the simulator
+# Updating the openapi specs
+The openapi specifications are stored in the 'api/&lt;version&gt;/'. If adding/replacing with a new file, make sure to copy the 'operationId' parameter for each operation to the new file.
+
+# Start and test of the simulator
 First, download the sim/a1-interface repo on gerrit:
 git clone "https://gerrit.o-ran-sc.org/oransc/sim/a1-interface"
 
-Then, build the docker container:
-docker build -t simulator .
+Goto the main directory, 'a1-interface/near-rt-ric-simulator'.
+There is a folder 'test/&lt;version&gt;/' for each supported simulator version. This folder contains a script to build and start the simulator (as a container in interactive mode), a script for basic testing as well as json files for the test script.
+
+Go to the test folder of the selected version, 'test/&lt;version&gt;/.
 
-To run it, use the command:
-docker run -it -p 8085:8085 simulator
+Build and start the simulator container using: ./build\_and\_start.sh
+This will build and start the container in interactive mode. The built container only resides in the local docker repository.
+Note, the default port is 8085 which can be easily changed in the the script above as well as in the test script.
 
-Note: -p 8085:8085 allows to map the port inside the container to any port you choose. One can for example choose -p 8084:8085; in that case, all the addresses mentioned above should be modified accordingly.
+In a second terminal, go to the same folder and run the basic test script, basic\_test.sh or commands.sh depending on version.
+This script runs a number of tests towards the simulator to make sure it works properply.
 
-Let the simulator run in one terminal; in another terminal, one can run the command ./commands.sh. It contains the main requests, and will eventually leave the user with a policy type STD_QoSNudging_0.2.0 and a policy instance pi1 with an enforceStatus set to NOT_ENFORCED and an enforce Reason set to 300.
+Only for version 1.1.x-alpha.2
+Let the simulator run in one terminal; in another terminal, one can run the command ./commands.sh. It contains the main requests, and will eventually leave the user with a policy type STD\_QoSNudging\_0.2.0 and a policy instance pi1 with an enforceStatus set to NOT\_ENFORCED and an enforce Reason set to 300.
 All the response codes should be 20X, otherwise something went wrong.
 
 ## License
diff --git a/near-rt-ric-simulator/api/OSC_2.1.0/openapi.yaml b/near-rt-ric-simulator/api/OSC_2.1.0/openapi.yaml
new file mode 100644 (file)
index 0000000..828a6bf
--- /dev/null
@@ -0,0 +1,349 @@
+# ==================================================================================
+#       Copyright (c) 2019-2020 Nokia
+#       Copyright (c) 2018-2020 AT&T Intellectual Property.
+#       Copyright (c) 2020 Nordix Foundation, Modifications
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   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.
+# ==================================================================================
+openapi: 3.0.0
+info:
+  version: 2.1.0
+  title: RIC A1
+paths:
+  '/a1-p/healthcheck':
+    get:
+      description: >
+        Perform a healthcheck on a1
+      tags:
+        - A1 Mediator
+      operationId: a1.get_healthcheck
+      responses:
+        200:
+          description: >
+            A1 is healthy.
+            Anything other than a 200 should be considered a1 as failing
+
+  '/a1-p/policytypes':
+    get:
+      description: "Get a list of all registered policy type ids"
+      tags:
+        - A1 Mediator
+      operationId: a1.get_all_policy_types
+      responses:
+        200:
+          description: "list of all registered policy type ids"
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  "$ref": "#/components/schemas/policy_type_id"
+              example: [20000, 20020]
+        503:
+          description: "Potentially transient backend database error. Client should attempt to retry later."
+
+  '/a1-p/policytypes/{policy_type_id}':
+    parameters:
+      - name: policy_type_id
+        in: path
+        required: true
+        schema:
+          "$ref": "#/components/schemas/policy_type_id"
+    get:
+      description: >
+        Get this policy type
+      tags:
+        - A1 Mediator
+      operationId: a1.get_policy_type
+      responses:
+        '200':
+          description: "policy type successfully found"
+          content:
+            application/json:
+              schema:
+                "$ref": "#/components/schemas/policy_type_schema"
+        '404':
+          description: >
+            policy type not found
+        '503':
+          description: "Potentially transient backend database error. Client should attempt to retry later."
+    delete:
+      description: >
+        Delete this policy type. Can only be performed if there are no instances of this type
+      tags:
+        - A1 Mediator
+      operationId: a1.delete_policy_type
+      responses:
+        '204':
+          description: >
+            policy type successfully deleted
+        '400':
+          description: >
+            Policy type cannot be deleted because there are instances
+            All instances must be removed before a policy type can be deleted
+        '404':
+          description: >
+            policy type not found
+        '503':
+          description: "Potentially transient backend database error. Client should attempt to retry later."
+    put:
+      description: >
+        Create a new policy type .
+        Replace is not currently allowed; to replace, for now do a DELETE and then a PUT again.
+
+      tags:
+        - A1 Mediator
+      operationId: a1.create_policy_type
+      requestBody:
+        content:
+          application/json:
+            schema:
+               "$ref": "#/components/schemas/policy_type_schema"
+            example:
+              name: admission_control_policy
+              description: various parameters to control admission of dual connection
+              policy_type_id: 20000
+              create_schema:
+                $schema: 'http://json-schema.org/draft-07/schema#'
+                type: object
+                properties:
+                  enforce:
+                    type: boolean
+                    default: true
+                  window_length:
+                    type: integer
+                    default: 1
+                    minimum: 1
+                    maximum: 60
+                    description: Sliding window length (in minutes)
+                  blocking_rate:
+                    type: number
+                    default: 10
+                    minimum: 1
+                    maximum: 100
+                    description: '% Connections to block'
+                  trigger_threshold:
+                    type: integer
+                    default: 10
+                    minimum: 1
+                    description: Minimum number of events in window to trigger blocking
+                additionalProperties: false
+
+      responses:
+        '201':
+          description: "policy type successfully created"
+        '400':
+          description: "illegal ID, or object already existed"
+        '503':
+          description: "Potentially transient backend database error. Client should attempt to retry later."
+
+  '/a1-p/policytypes/{policy_type_id}/policies':
+    parameters:
+      - name: policy_type_id
+        in: path
+        required: true
+        schema:
+          "$ref": "#/components/schemas/policy_type_id"
+    get:
+      description: "get a list of all policy instance ids for this policy type id"
+      tags:
+        - A1 Mediator
+      operationId: a1.get_all_policy_identities
+      responses:
+        200:
+          description: "list of all policy instance ids for this policy type id"
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  "$ref": "#/components/schemas/policy_instance_id"
+              example: ["3d2157af-6a8f-4a7c-810f-38c2f824bf12", "06911bfc-c127-444a-8eb1-1bffad27cc3d"]
+        '503':
+          description: "Potentially transient backend database error. Client should attempt to retry later."
+
+
+  '/a1-p/policytypes/{policy_type_id}/policies/{policy_instance_id}':
+    parameters:
+      - name: policy_type_id
+        in: path
+        required: true
+        schema:
+          "$ref": "#/components/schemas/policy_type_id"
+
+      - name: policy_instance_id
+        in: path
+        required: true
+        schema:
+          "$ref": "#/components/schemas/policy_instance_id"
+
+    get:
+      description: >
+        Retrieve the policy instance
+
+      tags:
+        - A1 Mediator
+      operationId: a1.get_policy_instance
+      responses:
+        '200':
+          description: >
+            The policy instance.
+            the schema of this object is defined by the create_schema field of the policy type
+          content:
+            application/json:
+              schema:
+                type: object
+        '404':
+          description: >
+            there is no policy instance with this policy_instance_id or there is no policy type with this policy_type_id
+        '503':
+          description: "Potentially transient backend database error. Client should attempt to retry later."
+
+    delete:
+      description: >
+        Delete this policy instance
+
+      tags:
+        - A1 Mediator
+      operationId: a1.delete_policy_instance
+      responses:
+        '202':
+          description: >
+            policy instance deletion initiated
+        '404':
+          description: >
+            there is no policy instance with this policy_instance_id or there is no policy type with this policy_type_id
+        '503':
+          description: "Potentially transient backend database error. Client should attempt to retry later."
+
+    put:
+      description: >
+        Create or replace a policy instance of type policy_type_id.
+        The schema of the PUT body is defined by the create_schema field of the policy type.
+
+      tags:
+        - A1 Mediator
+      operationId: a1.create_or_replace_policy_instance
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              description: >
+                  the schema of this object is defined by the create_schema field of the policy type
+            example:
+              enforce: true
+              window_length: 10
+              blocking_rate: 20
+              trigger_threshold: 10
+
+      responses:
+        '202':
+          description: >
+            Policy instance creation initiated
+        '400':
+          description: >
+            Bad PUT body for this policy instance
+        '404':
+          description: >
+            There is no policy type with this policy_type_id
+        '503':
+          description: "Potentially transient backend database error. Client should attempt to retry later."
+
+  '/a1-p/policytypes/{policy_type_id}/policies/{policy_instance_id}/status':
+    parameters:
+      - name: policy_type_id
+        in: path
+        required: true
+        schema:
+          "$ref": "#/components/schemas/policy_type_id"
+
+      - name: policy_instance_id
+        in: path
+        required: true
+        schema:
+          "$ref": "#/components/schemas/policy_instance_id"
+
+    get:
+      description: >
+        Retrieve the policy instance status across all handlers of the policy
+        If this endpoint returns successfully (200), it is either IN EFFECT or NOT IN EFFECT.
+        IN EFFECT is returned if at least one policy handler in the RIC is implementing the policy
+        NOT IN EFFECT is returned otherwise
+        If a policy instance is successfully deleted, this endpoint will return a 404 (not a 200)
+      tags:
+        - A1 Mediator
+      operationId: a1.get_policy_instance_status
+      responses:
+        '200':
+          description: >
+            successfully retrieved the status
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  instance_status:
+                    type: string
+                    enum:
+                     - IN EFFECT
+                     - NOT IN EFFECT
+                  has_been_deleted:
+                    type: boolean
+                  created_at:
+                    type: string
+                    format: date-time
+
+        '404':
+          description: >
+            there is no policy instance with this policy_instance_id or there is no policy type with this policy_type_id
+        '503':
+          description: "Potentially transient backend database error. Client should attempt to retry later."
+
+components:
+  schemas:
+    policy_type_schema:
+      type: object
+      required:
+      - name
+      - description
+      - policy_type_id
+      - create_schema
+      additionalProperties: false
+      properties:
+        name:
+          type: string
+          description: name of the policy type
+        description:
+          type: string
+          description: description of the policy type
+        policy_type_id:
+          description: the integer of the policy type
+          type: integer
+        create_schema:
+          type: object
+          description: >
+            jsonschema (following http://json-schema.org/draft-07/schema) of the CREATE payload to be sent to handlers of this policy
+
+    policy_type_id:
+      description: >
+        represents a policy type identifier. Currently this is restricted to an integer range.
+      type: integer
+      minimum: 1
+      maximum: 2147483647
+
+    policy_instance_id:
+      description: >
+        represents a policy instance identifier. UUIDs are advisable but can be any string
+      type: string
+      example: "3d2157af-6a8f-4a7c-810f-38c2f824bf12"
diff --git a/near-rt-ric-simulator/api/STD_1.1.3/STD_A1.yaml b/near-rt-ric-simulator/api/STD_1.1.3/STD_A1.yaml
new file mode 100644 (file)
index 0000000..71638f4
--- /dev/null
@@ -0,0 +1,258 @@
+openapi: 3.0.0
+info:
+  title: 'A1-P Policy Management Service'
+  version: 1.1.3
+  description: |
+    API for Policy Management Service.
+    Â© 2019, O-RAN Alliance.
+    All rights reserved.
+externalDocs:
+  description: 'ORAN-WG2.A1.AP-v01.01 A1 interface: Application protocol'
+  url: 'https://www.o-ran.org/specifications'
+servers:
+  - url: '{apiRoot}/A1-P/v1'
+    variables:
+      apiRoot:
+        default: 'https://example.com'
+        description: 'apiRoot as defined in clause 4.2.1 in ORAN-WG2.A1.AP'
+paths:
+  '/policies':
+    get:
+      operationId: a1.get_all_policy_identities
+      description: 'Get all policy identities'
+      tags:
+      - All Policy Identities
+      responses:
+        200:
+          description: 'Array of all policy identities'
+          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"
+
+  '/policies/{policyId}':
+    parameters:
+      - 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"
+
+  '/policies/{policyId}/status':
+    parameters:
+      - 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
+
+    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
+    #
+    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
+
+  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"
\ No newline at end of file
diff --git a/near-rt-ric-simulator/scripts/Dockerfile b/near-rt-ric-simulator/scripts/Dockerfile
deleted file mode 100644 (file)
index 0b9319f..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# ============LICENSE_START=======================================================
-#  Copyright (C) 2020 Nordix Foundation.
-# ================================================================================
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ============LICENSE_END=========================================================
-#
-
-FROM python:3.8-slim-buster
-
-WORKDIR /usr/src/app
-
-RUN pip install connexion[swagger-ui]
-
-
-COPY ./near-rt-ric-simulator/scripts/example_files/policy_instance_1_STD_QoSNudging_0.2.0.json policy_instance_1_STD_QoSNudging_0.2.0.json
-COPY ./near-rt-ric-simulator/scripts/example_files/policy_instance_1_bis_STD_QoSNudging_0.2.0.json policy_instance_1_bis_STD_QoSNudging_0.2.0.json
-COPY ./near-rt-ric-simulator/scripts/example_files/policy_instance_2_STD_QoSNudging_0.2.0.json policy_instance_2_STD_QoSNudging_0.2.0.json
-COPY ./near-rt-ric-simulator/scripts/example_files/policy_type_STD_QoSNudging_0.2.0.json policy_type_STD_QoSNudging_0.2.0.json
-
-COPY ./near-rt-ric-simulator/scripts/a1.py a1.py
-COPY ./near-rt-ric-simulator/scripts/main.py main.py
-COPY ./near-rt-ric-simulator/scripts/var_declaration.py var_declaration.py
-
-COPY ./near-rt-ric-simulator/scripts/commands.sh commands.sh
-COPY ./near-rt-ric-simulator/scripts/start_simulator.sh start_simulator.sh
-
-COPY ./near-rt-ric-simulator/a1-openapi.yaml a1-openapi.yaml
-
-CMD ["/bin/bash", "./run_me.sh"]
diff --git a/near-rt-ric-simulator/scripts/var_declaration.py b/near-rt-ric-simulator/scripts/var_declaration.py
deleted file mode 100644 (file)
index 54621ad..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/env python3
-
-policy_instances = {}
-policy_types = {}
-policy_status = {}
-policy_type_per_instance = {}
similarity index 97%
rename from near-rt-ric-simulator/scripts/a1.py
rename to near-rt-ric-simulator/src/1.1.x-alpha.2/a1.py
index 006d04d..567047e 100644 (file)
@@ -1,4 +1,3 @@
-#!/usr/bin/python3
 #  ============LICENSE_START===============================================
 #  Copyright (C) 2020 Nordix Foundation. All rights reserved.
 #  ========================================================================
@@ -20,7 +19,6 @@ import copy
 import datetime
 import json
 import logging
-#import requests
 
 from connexion import NoContent
 from flask import Flask, escape, request, make_response
@@ -183,6 +181,6 @@ def send_error_code(args):
     elif code == '503':
       return(set_error(None, "Service unavailable", 503, "The provider is currently unable to handle the request due to a temporary overload", None, None, None, None))
     else:
-      return(set_error(None, "Not found", 400, "No resource found at the URI", None, None, None, None))
+      return(set_error(None, "Not found", 400, "Object in payload not properly formulated or not related to the method", None, None, None, None))
   else:
-    return(set_error(None, "Not found", 400, "No resource found at the URI", None, None, None, None))
+    return(set_error(None, "Not found", 404, "No resource found at the URI", None, None, None, None))
similarity index 90%
rename from near-rt-ric-simulator/scripts/main.py
rename to near-rt-ric-simulator/src/1.1.x-alpha.2/main.py
index adbf1e5..636ce13 100644 (file)
@@ -1,4 +1,3 @@
-#!/usr/bin/python3
 #  ============LICENSE_START===============================================
 #  Copyright (C) 2020 Nordix Foundation. All rights reserved.
 #  ========================================================================
@@ -20,12 +19,20 @@ import connexion
 import fileinput
 import json
 import sys
+import os
 
+import maincommon
+
+from pathlib import Path
 from flask import Flask, escape, request, make_response
 from jsonschema import validate
 from var_declaration import policy_instances, policy_types, policy_status, policy_type_per_instance
+from maincommon import *
+
+
+check_apipath()
 
-app = connexion.App(__name__, specification_dir='.')
+app = connexion.App(__name__, specification_dir=apipath)
 
 @app.route('/policytypes/<string:policyTypeId>', methods=['PUT','DELETE'])
 def policy_type(policyTypeId):
@@ -111,11 +118,15 @@ def set_status_with_reason(policyId, enforceStatus, enforceReason):
 @app.route('/counter/<string:countername>', methods=['GET'])
 def getCounter(countername):
     if (countername == "num_instances"):
-        return str(len(policy_instances)),200
+      return str(len(policy_instances)),200
     elif (countername == "num_types"):
-        return str(len(policy_types)),200
+      return str(len(policy_types)),200
+    elif (countername == "interface"):
+      p=Path(os.getcwd())
+      pp=p.parts
+      return str(pp[len(pp)-1]),200
     else:
-        return "Counter name: "+countername+" not found.",404
+      return "Counter name: "+countername+" not found.",404
 
 
 port_number = 8085
@@ -123,6 +134,6 @@ if len(sys.argv) >= 2:
   if isinstance(sys.argv[1], int):
     port_number = sys.argv[1]
 
-app.add_api('../a1-openapi.yaml')
+app.add_api('a1-openapi.yaml')
 app.run(port=port_number)
 
old mode 100755 (executable)
new mode 100644 (file)
similarity index 85%
rename from near-rt-ric-simulator/scripts/start_simulator.sh
rename to near-rt-ric-simulator/src/1.1.x-alpha.2/var_declaration.py
index fa08b4f..b5a344a
@@ -1,4 +1,3 @@
-#!/bin/bash
 #  ============LICENSE_START===============================================
 #  Copyright (C) 2020 Nordix Foundation. All rights reserved.
 #  ========================================================================
@@ -16,6 +15,7 @@
 #  ============LICENSE_END=================================================
 #
 
-# One argument can be used along with the script call: it is the port on which one wish to run the simulator.
-
-python3 ./main.py $*
+policy_instances = {}
+policy_types = {}
+policy_status = {}
+policy_type_per_instance = {}
diff --git a/near-rt-ric-simulator/src/OSC_2.1.0/a1.py b/near-rt-ric-simulator/src/OSC_2.1.0/a1.py
new file mode 100644 (file)
index 0000000..6db2599
--- /dev/null
@@ -0,0 +1,247 @@
+#  ============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
+import datetime
+import time
+
+from datetime import datetime
+from connexion import NoContent
+from flask import Flask, request, Response
+from jsonschema import validate
+from var_declaration import policy_instances, policy_types, policy_status, policy_fingerprint, forced_settings
+from utils import calcFingerprint
+from maincommon import *
+
+
+# API Function: Health check
+def get_healthcheck():
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  return (None, 200)
+
+# API Function: Get all policy type ids
+def get_all_policy_types():
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  return (list(policy_instances.keys()), 200)
+
+# API Function: Get a policy type
+def get_policy_type(policy_type_id):
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  policy_type_id=str(policy_type_id)
+
+  if (policy_type_id not in policy_types.keys()):
+    return (None, 404)
+
+  return Response(json.dumps(policy_types[policy_type_id]), 200, mimetype='application/json')
+
+# API Function: Delete a policy type
+def delete_policy_type(policy_type_id):
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  policy_type_id=str(policy_type_id)
+
+  if (policy_type_id not in policy_instances.keys()):
+    return (None, 404)
+
+  if (len(policy_instances[policy_type_id]) > 0):
+    return (None, 400)
+
+  del policy_instances[policy_type_id]
+  del policy_types[policy_type_id]
+
+  return (None, 204)
+
+
+# API Function: Create a policy type
+def create_policy_type(policy_type_id):
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  policy_type_id=str(policy_type_id)
+
+  if (policy_type_id in policy_instances.keys()):
+    if (len(policy_instances[policy_type_id]) > 0):
+      return (None, 400)
+
+  try:
+    data = request.data
+    data = json.loads(data)
+  except:
+    return (None, 400)
+
+  if (('name' not in data.keys()) or ('description' not in data.keys()) or ('policy_type_id' not in data.keys()) or ('create_schema' not in data.keys())):
+    return (None, 400)
+
+  if (policy_type_id not in policy_instances.keys()):
+    policy_instances[policy_type_id]={}
+
+  policy_types[policy_type_id]=data
+
+  return (None, 201)
+
+
+# API Function: Get all policy ids for a type
+def get_all_policy_identities(policy_type_id):
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  policy_type_id=str(policy_type_id)
+
+  if (policy_type_id not in policy_instances.keys()):
+    return (None, 404)
+  return (list(policy_instances[policy_type_id].keys()), 200)
+
+# API Function: Get a policy instance
+def get_policy_instance(policy_type_id, policy_instance_id):
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  policy_type_id=str(policy_type_id)
+
+  if (policy_type_id not in policy_instances.keys()):
+    return (None, 404)
+
+  if (policy_instance_id not in policy_instances[policy_type_id].keys()):
+    return (None, 404)
+
+  return Response(json.dumps(policy_instances[policy_type_id][policy_instance_id]), 200, mimetype='application/json')
+
+# API function: Delete a policy
+def delete_policy_instance(policy_type_id, policy_instance_id):
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  policy_type_id=str(policy_type_id)
+
+  if (policy_type_id not in policy_instances.keys()):
+    return (None, 404)
+
+  if (policy_instance_id not in policy_instances[policy_type_id].keys()):
+    return (None, 404)
+
+  fpPrevious=calcFingerprint(policy_instances[policy_type_id][policy_instance_id])
+  del policy_fingerprint[fpPrevious]
+  del policy_instances[policy_type_id][policy_instance_id]
+  del policy_status[policy_instance_id]
+
+  return (None, 202)
+
+# API function: Create/update a policy
+def create_or_replace_policy_instance(policy_type_id, policy_instance_id):
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  policy_type_id=str(policy_type_id)
+
+  if (policy_type_id not in policy_instances.keys()):
+    return (None, 404)
+
+  try:
+    data = request.data
+    data = json.loads(data)
+  except:
+    return (None, 400)
+
+  try:
+    validate(instance=data, schema=policy_types[policy_type_id]['create_schema'])
+  except:
+    return (None, 400)
+
+  fpPrevious=None
+  retcode=201
+  if policy_instance_id in policy_instances[policy_type_id].keys():
+    retcode=200
+    fpPrevious=calcFingerprint(policy_instances[policy_type_id][policy_instance_id])
+  else:
+    if (policy_instance_id in policy_fingerprint.values()):
+      return (None, 400)
+
+  fp=calcFingerprint(data)
+  if (fp in policy_fingerprint.keys()):
+    id=policy_fingerprint[fp]
+    if (id != policy_instance_id):
+      return (None, 400)
+
+  if (fpPrevious is not None):
+    del policy_fingerprint[fpPrevious]
+
+  policy_fingerprint[fp]=policy_instance_id
+
+  policy_instances[policy_type_id][policy_instance_id]=data
+  ps={}
+  ps["instance_status"] = "NOT IN EFFECT"
+  ps["has_been_deleted"] = "false"
+  ps["created_at"] = str(datetime.now().strftime("%m/%d/%Y, %H:%M:%S"))
+  policy_status[policy_instance_id]=ps
+
+  return (None, 202)
+
+# API function: Get policy status
+def get_policy_instance_status(policy_type_id, policy_instance_id):
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  policy_type_id=str(policy_type_id)
+  if (policy_type_id not in policy_instances.keys()):
+    return (None, 404)
+
+  if (policy_instance_id not in policy_instances[policy_type_id].keys()):
+    return (None, 404)
+
+  return Response(json.dumps(policy_status[policy_instance_id]), 200, mimetype='application/json')
+
+# Helper: Create a response object if forced http response code is set
+def get_forced_response():
+
+  response_code=forced_settings['code']
+  if (response_code is not None):
+    forced_settings['code'] = None
+    return (None, response_code)
+  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:
+      return
+  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()
\ No newline at end of file
diff --git a/near-rt-ric-simulator/src/OSC_2.1.0/main.py b/near-rt-ric-simulator/src/OSC_2.1.0/main.py
new file mode 100644 (file)
index 0000000..b158861
--- /dev/null
@@ -0,0 +1,206 @@
+#  ============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, policy_fingerprint, forced_settings
+from maincommon import *
+
+
+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 deleteinstances():
+
+  for i in policy_instances.keys():
+    policy_instances[i]={}
+
+  policy_status.clear()
+  forced_settings.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
+@app.route('/deleteall', methods=['POST'])
+def deleteall():
+
+  policy_instances.clear()
+  policy_types.clear()
+  policy_status.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():
+
+  policyTypeId=request.args.get('id')
+  if (policyTypeId is None):
+    return Response('Parameter <id> missing in request', status=400, mimetype='text/plain')
+  try:
+    val=int(policyTypeId)
+  except:
+    return Response("The policy type id is not an int", 400, mimetype='text/plain')
+  try:
+    data = request.data
+    data = json.loads(data)
+  except:
+    return Response("The policy type is corrupt or missing", 400, mimetype='text/plain')
+
+  if ('name' not in data.keys() or 'description' not in data.keys() or 'policy_type_id' not in data.keys() or'create_schema' not in data.keys()):
+    return Response("The policy type missing atributes", 400, mimetype='text/plain')
+
+  retcode=201
+  if (policyTypeId in policy_types.keys()):
+    retcode=200
+    if (len(policy_instances[policyTypeId]) > 0):
+      return Response("The policy type already exists and instances exists", 400, mimetype='text/plain')
+
+  policy_types[policyTypeId]=data
+  policy_instances[policyTypeId]={}
+  return Response("Policy type " + policyTypeId + " is OK.", retcode, mimetype='text/plain')
+
+#Delete a policy type
+@app.route('/policytype', methods=['DELETE'])
+def del_policytype():
+
+  policyTypeId=request.args.get('id')
+  if (policyTypeId is None):
+    return Response('Parameter <policytypeid> missing in request', status=400, mimetype='text/plain')
+  try:
+    val=int(policyTypeId)
+  except:
+    return Response("The policy type id is not an int", 400, mimetype='text/plain')
+
+  if (policyTypeId in policy_types.keys()):
+    if (len(policy_instances[policyTypeId]) > 0):
+      return Response("The policy type already exists and instances exists", 400, mimetype='text/plain')
+
+    del policy_types[policyTypeId]
+    del policy_instances[policyTypeId]
+    return Response("Policy type " + policyTypeId + " is OK.", 204, mimetype='text/plain')
+
+  return Response("Policy type " + policyTypeId + " 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']=int(request.args.get('code'))
+  except:
+    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']=int(request.args.get('delay'))
+  except:
+    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>[&deleted=<boolean>][&created_at=<timestamp>]
+@app.route('/status', methods=['PUT'])
+def setstatus():
+
+  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')
+  status=request.args.get('status')
+  if (status is None):
+    return Response('Parameter <status> missing in request', status=400, mimetype='text/plain')
+  policy_status[policyId]["instance_status"]=status
+  msg = "Status set to "+status
+  deleted_policy=request.args.get('deleted')
+  if (deleted_policy is not None):
+    policy_status[policyId]["has_been_deleted"]=deleted_policy
+    msg = msg + " and has_been_deleted set to "+deleted_policy
+  created_at = request.args.get('created_at')
+  if (created_at is not None):
+    policy_status[policyId]["created_at"]=created_at
+    msg = msg + " and created_at set to "+created_at
+  msg=msg + " for policy: " + policyId
+  return Response(msg, 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')
+  else:
+    return Response("Counter name: "+countername+" not found.",404, mimetype='text/plain')
+
+port_number = 8085
+if len(sys.argv) >= 2:
+  if isinstance(sys.argv[1], int):
+    port_number = sys.argv[1]
+
+app.add_api('openapi.yaml')
+app.run(port=port_number)
+
diff --git a/near-rt-ric-simulator/src/OSC_2.1.0/var_declaration.py b/near-rt-ric-simulator/src/OSC_2.1.0/var_declaration.py
new file mode 100644 (file)
index 0000000..f18fbca
--- /dev/null
@@ -0,0 +1,24 @@
+#  ============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={}
+forced_settings={}
+forced_settings['code']=None
+forced_settings['delay']=None
+policy_fingerprint={}
diff --git a/near-rt-ric-simulator/src/STD_1.1.3/a1.py b/near-rt-ric-simulator/src/STD_1.1.3/a1.py
new file mode 100644 (file)
index 0000000..dfc81e6
--- /dev/null
@@ -0,0 +1,182 @@
+#  ============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 var_declaration import policy_instances, policy_status, callbacks, forced_settings, policy_fingerprint
+from utils import calcFingerprint
+
+# API Function: Get all policy ids
+def get_all_policy_identities():
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  return (list(policy_instances.keys()), 200)
+
+# API Function: Create or update a policy
+def put_policy(policyId):
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  try:
+    data = request.data
+    data = json.loads(data)
+  except:
+    pjson=create_problem_json(None, "The policy is corrupt or missing.", 400, None, policyId)
+    return Response(json.dumps(pjson), 400, mimetype='application/problem+json')
+
+  fpPrevious=None
+  retcode=201
+  if policyId in policy_instances.keys():
+    retcode=200
+    fpPrevious=calcFingerprint(policy_instances[policyId])
+
+  fp=calcFingerprint(data)
+  if (fp in policy_fingerprint.keys()):
+    id=policy_fingerprint[fp]
+    if (id != policyId):
+      pjson=create_problem_json(None, "The policy json already exists.", 400, None, policyId)
+      return Response(json.dumps(pjson), 400, mimetype='application/problem+json')
+
+  if (fpPrevious is not None):
+    del policy_fingerprint[fpPrevious]
+
+  policy_fingerprint[fp]=policyId
+
+  noti=request.args.get('notificationDestination')
+  callbacks[policyId]=noti
+
+  policy_instances[policyId]=data
+  ps={}
+  ps["enforceStatus"] = "UNDEFINED"
+  policy_status[policyId]=ps
+
+  if (retcode == 200):
+    return Response(json.dumps(data), 200, mimetype='application/json')
+  else:
+    headers={}
+    headers['Location']='/A1-P/v1/policies/' + policyId
+    return Response(json.dumps(data), 201, headers=headers, mimetype='application/json')
+
+# API Function: Get a policy
+def get_policy(policyId):
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  if policyId in policy_instances.keys():
+    return Response(json.dumps(policy_instances[policyId]), 200, mimetype='application/json')
+
+  pjson=create_problem_json(None, "The requested policy does not exist.", 404, None, policyId)
+  return Response(json.dumps(pjson), 404, mimetype='application/problem+json')
+
+# API Function: Delete a policy
+def delete_policy(policyId):
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  if policyId in policy_instances.keys():
+    fpPrevious=calcFingerprint(policy_instances[policyId])
+    policy_fingerprint.pop(fpPrevious)
+    policy_instances.pop(policyId)
+    policy_status.pop(policyId)
+    callbacks.pop(policyId)
+    return Response('', 204, mimetype='application/json')
+
+  pjson=create_problem_json(None, "The policy identity does not exist.", 404, "No policy instance has been deleted.", policyId)
+  return Response(json.dumps(pjson), 404, mimetype='application/problem+json')
+
+# API Function: Get status for a policy
+def get_policy_status(policyId):
+
+  if ((r := check_modified_response()) is not None):
+    return r
+
+  if policyId in policy_instances.keys():
+    return Response(json.dumps(policy_status[policyId]), status=200, mimetype='application/json')
+
+  pjson=create_problem_json(None, "The policy identity does not exist.", 404, "There is no existing policy instance with the identity: " + policyId, policyId)
+  return Response(json.dumps(pjson), 404, mimetype='application/problem+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='application/problem+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:
+      return
+  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_1.1.3/main.py b/near-rt-ric-simulator/src/STD_1.1.3/main.py
new file mode 100644 (file)
index 0000000..18798ab
--- /dev/null
@@ -0,0 +1,169 @@
+#  ============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_status, callbacks, forced_settings, policy_fingerprint
+from maincommon import *
+
+
+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():
+
+  policy_instances.clear()
+  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():
+  return delete_instances()
+
+#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:
+    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:
+    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():
+
+  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_instances.keys():
+    return Response('Policyid: '+policyId+' 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[policyId] = ps
+  msg=msg+" for policy: " + policyId
+  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:
+    resp=requests.post(cb,json=json.dumps(ps))
+  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:
+    return Response("The status data is corrupt or missing.", 400, mimetype='text/plain')
+
+  return Response(json.dumps(data), 200, mimetype='application/json')
+
+#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_instances)), 200, mimetype='text/plain')
+  elif (countername == "num_types"):
+    return Response("0",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')
+  else:
+    return Response("Counter name: "+countername+" not found.",404, mimetype='text/plain')
+
+port_number = 8085
+if len(sys.argv) >= 2:
+  if isinstance(sys.argv[1], int):
+    port_number = sys.argv[1]
+
+app.add_api('STD_A1.yaml')
+app.run(port=port_number)
diff --git a/near-rt-ric-simulator/src/STD_1.1.3/var_declaration.py b/near-rt-ric-simulator/src/STD_1.1.3/var_declaration.py
new file mode 100644 (file)
index 0000000..08fdb0c
--- /dev/null
@@ -0,0 +1,23 @@
+#  ============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_instances = {}
+policy_status = {}
+callbacks = {}
+forced_settings = {}
+forced_settings['code']=None
+forced_settings['delay']=None
+policy_fingerprint={}
diff --git a/near-rt-ric-simulator/src/common/maincommon.py b/near-rt-ric-simulator/src/common/maincommon.py
new file mode 100644 (file)
index 0000000..d0b9802
--- /dev/null
@@ -0,0 +1,39 @@
+#  ============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 os
+import sys
+from pathlib import Path
+from flask import Response
+
+apipath=os.environ['APIPATH']
+
+# Make sure the api path for the interface yaml file is set, otherwise exit
+def check_apipath():
+    if (apipath is None):
+        print("Env APIPATH not set. Exiting....")
+        sys.exit(1)
+
+# Return a Response of currently supported interfaces
+def get_supported_interfaces_response():
+    p=Path(os.getcwd())
+    pp=p.parts
+    arr = os.listdir("../")
+    del arr[arr.index('common')] # Remove the common lib
+    del arr[arr.index('start.sh')] # Remove the start script
+    return Response("Current interface: " + str(pp[len(pp)-1]) + "  All supported A1 interface yamls in this container: "+str(arr), 200, mimetype='text/plain')
+
diff --git a/near-rt-ric-simulator/src/common/utils.py b/near-rt-ric-simulator/src/common/utils.py
new file mode 100644 (file)
index 0000000..1ed2a7e
--- /dev/null
@@ -0,0 +1,26 @@
+#  ============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=================================================
+#
+
+#Calculate a fingerprint from sorted items of a dict
+def calcFingerprint(p):
+  m=''
+  if (p is dict):
+    for i in sorted (p.keys()):
+      m = m+str(i)+calcFingerprint(p[i])
+  else:
+    return str(p)
+  return m
\ No newline at end of file
diff --git a/near-rt-ric-simulator/src/start.sh b/near-rt-ric-simulator/src/start.sh
new file mode 100755 (executable)
index 0000000..a424b5a
--- /dev/null
@@ -0,0 +1,41 @@
+#!/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=================================================
+#
+
+if [ $# -ne 1 ]; then
+    echo "Expected folder name of simulator."
+    echo "The container shall be started with env variable 'A1_VERSION' set to the folder name of the A1 version to use."
+    echo "Exiting...."
+    exit 1
+fi
+echo "Version folder for simulator: "$1
+
+#Set path to open api
+export APIPATH=$PWD/api/$1
+echo "APIPATH set to: "$APIPATH
+
+cd src
+
+#Include common module(s)
+export PYTHONPATH=$PWD/common
+echo "PYTHONPATH set to: "$PYTHONPATH
+
+cd $1
+
+echo "Path to main.py: "$PWD
+python -u main.py
diff --git a/near-rt-ric-simulator/test/1.1.x-alpha.2/build_and_start.sh b/near-rt-ric-simulator/test/1.1.x-alpha.2/build_and_start.sh
new file mode 100755 (executable)
index 0000000..bd2acdd
--- /dev/null
@@ -0,0 +1,30 @@
+#!/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
+
+
+cd ../../
+
+#Build the image
+docker build -t a1test .
+
+#Run the container in interactive mode
+docker run -it -p 8085:8085 -e A1_VERSION=1.1.x-alpha.2 a1test
+
diff --git a/near-rt-ric-simulator/test/OSC_2.1.0/basic_test.sh b/near-rt-ric-simulator/test/OSC_2.1.0/basic_test.sh
new file mode 100755 (executable)
index 0000000..5cc1214
--- /dev/null
@@ -0,0 +1,251 @@
+#!/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=================================================
+#
+
+#Default port for the simulator
+PORT=8085
+
+. ../common/test_common.sh
+
+echo "=== Simulator hello world ==="
+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: ['1.1.x-alpha.2', 'STD_1.1.3', 'OSC_2.1.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 "=== API: Healthcheck ==="
+RESULT=""
+do_curl get /a1-p/healthcheck 200
+
+echo "=== API: Get policy types, shall be empty array =="
+RESULT="json:[]"
+do_curl GET /a1-p/policytypes 200
+
+echo "=== API: Delete a policy type, shall fail =="
+RESULT=""
+do_curl DELETE /a1-p/policytypes/1 404
+
+echo "=== API: Get policy instances for type 1, shall fail =="
+RESULT=""
+do_curl GET /a1-p/policytypes/1/policies 404
+
+echo "=== API: Put a policy type: 1 ==="
+RESULT=""
+do_curl PUT /a1-p/policytypes/1 201 jsonfiles/pt1.json
+
+echo "=== API: Put a policy type: 1, again ==="
+RESULT=""
+do_curl PUT /a1-p/policytypes/1 201 jsonfiles/pt1.json
+
+echo "=== API: Delete a policy type: 1 ==="
+RESULT=""
+do_curl DELETE /a1-p/policytypes/1 204
+
+echo "=== API: Get policy type ids, shall be empty =="
+RESULT="json:[]"
+do_curl GET /a1-p/policytypes 200
+
+echo "=== API: Load a policy type: 1 ==="
+RESULT=""
+do_curl PUT /a1-p/policytypes/1 201 jsonfiles/pt1.json
+
+echo "=== API: Get policy type ids, shall contain type 1 =="
+RESULT="json:[ \"1\" ]"
+do_curl GET /a1-p/policytypes 200
+
+echo "=== API: Get instances for type 1, shall be empty ==="
+RESULT="json:[]"
+do_curl GET '/a1-p/policytypes/1/policies' 200
+
+echo "=== API: Create policy instance pi1 of type: 1 ==="
+RESULT=""
+do_curl PUT '/a1-p/policytypes/1/policies/pi1' 202 jsonfiles/pi1.json
+
+echo "=== API: Update policy instance pi1 of type: 1 ==="
+RESULT=""
+do_curl PUT '/a1-p/policytypes/1/policies/pi1' 202 jsonfiles/pi1.json
+
+echo "=== API: Load a policy type: 1, shall fail ==="
+RESULT=""
+do_curl PUT /a1-p/policytypes/1 400 jsonfiles/pt1.json
+
+echo "=== API: Get instances for type 1, shall contain pi1 ==="
+RESULT="json:[ \"pi1\" ]"
+do_curl GET '/a1-p/policytypes/1/policies' 200
+
+echo "=== API: Create policy instance pi2 (copy of pi1) of type: 1. Shall fail ==="
+RESULT=""
+do_curl PUT '/a1-p/policytypes/1/policies/pi2' 400 jsonfiles/pi1.json
+
+echo "=== Set force response code 401. ==="
+RESULT="*"
+do_curl POST '/forceresponse?code=401' 200
+
+echo "=== API: Get policy type 1. Shall fail with forced code ==="
+RESULT=""
+do_curl GET '/a1-p/policytypes/1' 401
+
+echo "=== API: Get policy status ==="
+RESULT="json:{\"instance_status\": \"NOT IN EFFECT\", \"has_been_deleted\": \"false\", \"created_at\": \"????\"}"
+do_curl GET '/a1-p/policytypes/1/policies/pi1/status' 200
+
+echo "=== Load a policy type: 2 ==="
+RESULT="Policy type 2 is OK."
+do_curl PUT '/policytype?id=2' 201 jsonfiles/pt1.json
+
+echo "=== Load a policy type: 2 again. ==="
+RESULT="Policy type 2 is OK."
+do_curl PUT '/policytype?id=2' 200 jsonfiles/pt1.json
+
+echo "=== API: Get policy type ids, shall contain type 1 and 2 =="
+RESULT="json:[ \"1\", \"2\" ]"
+do_curl GET /a1-p/policytypes 200
+
+echo "=== Get policy type ids, shall contain type 1 and 2 =="
+RESULT="json:[\"1\", \"2\"]"
+do_curl GET /policytypes 200
+
+echo "=== API: Get policy type 2 =="
+RESULT="json:{\"name\": \"pt1\", \"description\": \"pt1 policy type\", \"policy_type_id\": 1, \"create_schema\": {\"\$schema\": \"http://json-schema.org/draft-07/schema#\", \"title\": \"STD_QoSNudging_0.2.0\", \"description\": \"QoS 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\"]}}}}"
+do_curl GET /a1-p/policytypes/2 200
+
+echo "=== Delete policy type: 2 ==="
+RESULT=""
+do_curl DELETE '/policytype?id=2' 204 jsonfiles/pt1.json
+
+echo "=== API: Get policy type ids, shall contain type 1 =="
+RESULT="json:[ \"1\" ]"
+do_curl GET /a1-p/policytypes 200
+
+echo "=== Load a policy type: 2 ==="
+RESULT="Policy type 2 is OK."
+do_curl PUT '/policytype?id=2' 201 jsonfiles/pt2.json
+
+echo "=== API: Get policy type 2 =="
+RESULT="json:{\"name\": \"pt1\", \"description\": \"pt1 policy type\", \"policy_type_id\": 1, \"create_schema\": {\"\$schema\": \"http://json-schema.org/draft-07/schema#\", \"title\": \"STD_QoSNudging_0.2.0\", \"description\": \"QoS 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\"]}}}}"
+do_curl GET /a1-p/policytypes/1 200
+
+echo "=== API: Get instances for type 2, shall be empty ==="
+RESULT="json:[]"
+do_curl GET '/a1-p/policytypes/2/policies' 200
+
+echo "=== API: Create policy instance pi1 of type: 2, shall fail==="
+RESULT=""
+do_curl PUT '/a1-p/policytypes/2/policies/pi1' 400 jsonfiles/pi1.json
+
+echo "=== API: Create policy instance pi2 of type: 2. Missing param, shall fail. ==="
+RESULT=""
+do_curl PUT '/a1-p/policytypes/2/policies/pi2' 400 jsonfiles/pi2_missing_param.json
+
+echo "=== API: Create policy instance pi2 of type: 2 ==="
+RESULT=""
+do_curl PUT '/a1-p/policytypes/2/policies/pi2' 202 jsonfiles/pi2.json
+
+echo "=== API: Update policy instance pi2 of type: 2 ==="
+RESULT=""
+do_curl PUT '/a1-p/policytypes/2/policies/pi2' 202 jsonfiles/pi2.json
+
+echo "=== API: Get instances for type 1, shall contain pi1 ==="
+RESULT="json:[ \"pi1\" ]"
+do_curl GET '/a1-p/policytypes/1/policies' jsonfiles/200
+
+echo "=== API: Get instances for type 2, shall contain pi2 ==="
+RESULT="json:[ \"pi2\" ]"
+do_curl GET '/a1-p/policytypes/2/policies' 200
+
+echo "=== API: Create policy instance pi11 (copy of pi1) of type: 1. Shall fail ==="
+RESULT=""
+do_curl PUT '/a1-p/policytypes/1/policies/pi11' 400 jsonfiles/pi1.json
+
+echo "=== Set force response code 401. ==="
+RESULT="*"
+do_curl POST '/forceresponse?code=401' 200
+
+echo "=== API: Get policy status for pi1, shall fail ==="
+RESULT=""
+do_curl GET '/a1-p/policytypes/1/policies/pi1/status' 401
+
+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 status for pi1. Shall delay 10 sec ==="
+RESULT="json:{\"instance_status\": \"NOT IN EFFECT\", \"has_been_deleted\": \"false\", \"created_at\": \"????\"}"
+do_curl GET '/a1-p/policytypes/1/policies/pi1/status' 200
+
+echo "=== Reset force delay. ==="
+RESULT="Force delay: None sec set for all A1 responses"
+do_curl POST '/forcedelay' 200
+
+echo "=== Set status for pi1 ==="
+RESULT="Status set to IN EFFECT for policy: pi1"
+do_curl PUT '/status?policyid=pi1&status=IN%20EFFECT' 200
+
+echo "=== API: Get policy status for pi1 ==="
+RESULT="json:{\"instance_status\": \"IN EFFECT\", \"has_been_deleted\": \"false\", \"created_at\": \"????\"}"
+do_curl GET '/a1-p/policytypes/1/policies/pi1/status' 200
+
+echo "=== Set status for pi1 ==="
+RESULT="Status set to IN EFFECT and has_been_deleted set to true and created_at set to 2020-03-30 12:00:00 for policy: pi1"
+do_curl PUT '/status?policyid=pi1&status=IN%20EFFECT&deleted=true&created_at=2020-03-30%2012:00:00' 200
+
+echo "=== API: Get policy status for pi1 ==="
+RESULT="json:{\"instance_status\": \"IN EFFECT\", \"has_been_deleted\": \"true\", \"created_at\": \"2020-03-30 12:00:00\"}"
+do_curl GET '/a1-p/policytypes/1/policies/pi1/status' 200
+
+echo "=== Get counter: instances ==="
+RESULT="2"
+do_curl GET '/counter/num_instances' 200
+
+echo "=== Get counter: types ==="
+RESULT="2"
+do_curl GET '/counter/num_types' 200
+
+echo "=== Get counter: interface ==="
+RESULT="OSC_2.1.0"
+do_curl GET '/counter/interface' 200
+
+echo "=== DELETE policy pi1 ==="
+RESULT=""
+do_curl DELETE /a1-p/policytypes/1/policies/pi1 202
+
+echo "=== API: Get instances for type 1, shall be empty ==="
+RESULT="[]"
+do_curl GET /a1-p/policytypes/1/policies 200
+
+echo "=== API: Get instances for type 2, shall contain pi2 ==="
+RESULT="[ \"pi2\" ]"
+do_curl GET /a1-p/policytypes/2/policies 200
+
+echo "=== Get counter: instances ==="
+RESULT="1"
+do_curl GET /counter/num_instances 200
+
+echo "********************"
+echo "*** All tests ok ***"
+echo "********************"
diff --git a/near-rt-ric-simulator/test/OSC_2.1.0/build_and_start.sh b/near-rt-ric-simulator/test/OSC_2.1.0/build_and_start.sh
new file mode 100755 (executable)
index 0000000..afa5609
--- /dev/null
@@ -0,0 +1,30 @@
+#!/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
+
+
+cd ../../
+
+#Build the image
+docker build -t a1test .
+
+#Run the container in interactive mode
+docker run -it -p 8085:8085 -e A1_VERSION=OSC_2.1.0 a1test
+
diff --git a/near-rt-ric-simulator/test/OSC_2.1.0/jsonfiles/pi1.json b/near-rt-ric-simulator/test/OSC_2.1.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/OSC_2.1.0/jsonfiles/pi2.json b/near-rt-ric-simulator/test/OSC_2.1.0/jsonfiles/pi2.json
new file mode 100644 (file)
index 0000000..ef076e4
--- /dev/null
@@ -0,0 +1,9 @@
+{
+    "scope": {
+      "ueId": "ue2",
+      "qosId": "qos2"
+    },
+    "statement": {
+      "priorityLevel": 10
+    }
+  }
\ No newline at end of file
diff --git a/near-rt-ric-simulator/test/OSC_2.1.0/jsonfiles/pi2_missing_param.json b/near-rt-ric-simulator/test/OSC_2.1.0/jsonfiles/pi2_missing_param.json
new file mode 100644 (file)
index 0000000..11e325d
--- /dev/null
@@ -0,0 +1,8 @@
+{
+    "scope": {
+      "qosId": "qos2"
+    },
+    "statement": {
+      "priorityLevel": 10
+    }
+  }
\ No newline at end of file
diff --git a/near-rt-ric-simulator/test/OSC_2.1.0/jsonfiles/pt1.json b/near-rt-ric-simulator/test/OSC_2.1.0/jsonfiles/pt1.json
new file mode 100644 (file)
index 0000000..ba0c6d1
--- /dev/null
@@ -0,0 +1,41 @@
+{
+  "name": "pt1",
+  "description": "pt1 policy type",
+  "policy_type_id": 1,
+  "create_schema": {
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "title": "STD_QoSNudging_0.2.0",
+    "description": "QoS 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"
+        ]
+      }
+    }
+  }
+}
diff --git a/near-rt-ric-simulator/test/OSC_2.1.0/jsonfiles/pt2.json b/near-rt-ric-simulator/test/OSC_2.1.0/jsonfiles/pt2.json
new file mode 100644 (file)
index 0000000..68c5e73
--- /dev/null
@@ -0,0 +1,41 @@
+{
+    "name": "pt2",
+    "description": "pt2 policy type",
+    "policy_type_id": 2,
+    "create_schema": {
+      "$schema": "http://json-schema.org/draft-07/schema#",
+      "title": "STD_QoSNudging_0.2.0",
+      "description": "QoS 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"
+          ]
+        }
+      }
+    }
+  }
\ No newline at end of file
diff --git a/near-rt-ric-simulator/test/STD_1.1.3/basic_test.sh b/near-rt-ric-simulator/test/STD_1.1.3/basic_test.sh
new file mode 100755 (executable)
index 0000000..9274eaa
--- /dev/null
@@ -0,0 +1,148 @@
+#!/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=================================================
+#
+
+#Default port for the simulator
+PORT=8085
+
+. ../common/test_common.sh
+
+
+echo "=== Simulator hello world ==="
+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: ['1.1.x-alpha.2', 'STD_1.1.3', 'OSC_2.1.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 deleted"
+do_curl POST /deleteall 200
+
+echo "=== API: Get policy instances, shall be empty=="
+RESULT="json:[]"
+do_curl GET /A1-P/v1/policies 200
+
+echo "=== API: Create policy instance pi1 ==="
+RESULT="json:{\"scope\": {\"ueId\": \"ue1\", \"groupId\": \"group1\", \"sliceId\": \"slice1\", \"qosId\": \"qos1\", \"cellId\": \"cell1\"}, \"statement\": {\"priorityLevel\": 5}}"
+do_curl PUT /A1-P/v1/policies/pi1 201 jsonfiles/pi1.json
+
+echo "=== API: Update policy instance pi1 ==="
+RESULT="json:{\"scope\": {\"ueId\": \"ue1\", \"groupId\": \"group1\", \"sliceId\": \"slice1\", \"qosId\": \"qos1\", \"cellId\": \"cell1\"}, \"statement\": {\"priorityLevel\": 6}}"
+do_curl PUT /A1-P/v1/policies/pi1 200 jsonfiles/pi1_updated.json
+
+echo "=== API: Get policy instances, shall contain pi1=="
+RESULT="json:[ \"pi1\" ]"
+do_curl GET /A1-P/v1/policies 200
+
+echo "=== API: Create policy instance pi2 (copy of pi1). Shall fail ==="
+RESULT="json:{\"title\": \"The policy json already exists.\", \"status\": 400, \"instance\": \"pi2\"}"
+do_curl PUT /A1-P/v1/policies/pi2 400 jsonfiles/pi1_updated.json
+
+echo "=== Set force response code 409. ==="
+RESULT="*"
+do_curl POST '/forceresponse?code=409' 200
+
+echo "=== API: Get policy instances, shall fail =="
+RESULT="json:{\"title\": \"Conflict\", \"status\": 409, \"detail\": \"Request could not be processed in the current state of the resource\"}"
+do_curl GET /A1-P/v1/policies 409
+
+echo "=== API: Get policy status ==="
+RESULT="json:{\"enforceStatus\": \"UNDEFINED\"}"
+do_curl GET /A1-P/v1/policies/pi1/status 200
+
+echo "=== API: Create policy instance pi2 ==="
+RESULT="json:{\"scope\": {\"ueId\": \"ue2\", \"groupId\": \"group2\", \"sliceId\": \"slice2\", \"qosId\": \"qos2\", \"cellId\": \"cell2\"}, \"statement\": {\"priorityLevel\": 10}}"
+do_curl PUT /A1-P/v1/policies/pi2 201 jsonfiles/pi2.json
+
+echo "=== API: Update policy instance pi2 ==="
+RESULT="json:{\"scope\": {\"ueId\": \"ue2\", \"groupId\": \"group2\", \"sliceId\": \"slice2\", \"qosId\": \"qos2\", \"cellId\": \"cell2\"}, \"statement\": {\"priorityLevel\": 10}}"
+do_curl PUT '/A1-P/v1/policies/pi2?notificationDestination=http://localhost:8085/statustest' 200 jsonfiles/pi2.json
+
+echo "=== API: Get policy instances, shall contain pi1 and pi2=="
+RESULT="json:[ \"pi1\", \"pi2\" ]"
+do_curl GET /A1-P/v1/policies 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 and delayed 10 sec=="
+RESULT="json:[ \"pi1\", \"pi2\" ]"
+do_curl GET /A1-P/v1/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 ==="
+RESULT="json:{\"scope\": {\"ueId\": \"ue1\", \"groupId\": \"group1\", \"sliceId\": \"slice1\", \"qosId\": \"qos1\", \"cellId\": \"cell1\"}, \"statement\": {\"priorityLevel\": 6}}"
+do_curl GET /A1-P/v1/policies/pi1 200
+
+echo "=== API: GET policy instance pi2 ==="
+RESULT="json:{\"scope\": {\"ueId\": \"ue2\", \"groupId\": \"group2\", \"sliceId\": \"slice2\", \"qosId\": \"qos2\", \"cellId\": \"cell2\"}, \"statement\": {\"priorityLevel\": 10}}"
+do_curl GET /A1-P/v1/policies/pi2 200
+
+echo "=== API: DELETE policy instance pi1 ==="
+RESULT=""
+do_curl DELETE /A1-P/v1/policies/pi1 204
+
+echo "=== API: Get policy status for pi1, shall fail==="
+RESULT="json:{\"title\": \"The policy identity does not exist.\", \"status\": 404, \"detail\": \"There is no existing policy instance with the identity: pi1\", \"instance\": \"pi1\"}"
+do_curl GET /A1-P/v1/policies/pi1/status 404
+
+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 for pi2==="
+RESULT="json:{\"enforceStatus\": \"OK\"}"
+do_curl GET /A1-P/v1/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 for pi2 ==="
+RESULT="json:{\"enforceStatus\": \"NOTOK\", \"enforceReason\": \"notok_reason\"}"
+do_curl GET /A1-P/v1/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: intstance ==="
+RESULT="1"
+do_curl GET /counter/num_instances 200
+
+echo "=== Get counter: types (shall be 0)==="
+RESULT="0"
+do_curl GET /counter/num_types 200
+
+echo "=== Get counter: interface ==="
+RESULT="STD_1.1.3"
+do_curl GET /counter/interface 200
+
+echo "********************"
+echo "*** All tests ok ***"
+echo "********************"
diff --git a/near-rt-ric-simulator/test/STD_1.1.3/build_and_start.sh b/near-rt-ric-simulator/test/STD_1.1.3/build_and_start.sh
new file mode 100755 (executable)
index 0000000..dd1ac8a
--- /dev/null
@@ -0,0 +1,29 @@
+#!/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
+
+
+cd ../../
+
+#Build the image
+docker build -t a1test .
+
+#Run the container in interactive mode
+docker run -it -p 8085:8085 -e A1_VERSION=STD_1.1.3 a1test
\ No newline at end of file
diff --git a/near-rt-ric-simulator/test/STD_1.1.3/jsonfiles/pi1.json b/near-rt-ric-simulator/test/STD_1.1.3/jsonfiles/pi1.json
new file mode 100644 (file)
index 0000000..74f22c7
--- /dev/null
@@ -0,0 +1,13 @@
+{
+  "scope": {
+    "ueId": "ue1",
+    "groupId": "group1",
+    "sliceId": "slice1",
+    "qosId": "qos1",
+    "cellId": "cell1"
+  },
+  "statement": {
+    "priorityLevel": 5
+  }
+}
+
diff --git a/near-rt-ric-simulator/test/STD_1.1.3/jsonfiles/pi1_updated.json b/near-rt-ric-simulator/test/STD_1.1.3/jsonfiles/pi1_updated.json
new file mode 100644 (file)
index 0000000..c6970ef
--- /dev/null
@@ -0,0 +1,13 @@
+{
+  "scope": {
+    "ueId": "ue1",
+    "groupId": "group1",
+    "sliceId": "slice1",
+    "qosId": "qos1",
+    "cellId": "cell1"
+  },
+  "statement": {
+    "priorityLevel": 6
+  }
+}
+
diff --git a/near-rt-ric-simulator/test/STD_1.1.3/jsonfiles/pi2.json b/near-rt-ric-simulator/test/STD_1.1.3/jsonfiles/pi2.json
new file mode 100644 (file)
index 0000000..f86f1bc
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "scope": {
+    "ueId": "ue2",
+    "groupId": "group2",
+    "sliceId": "slice2",
+    "qosId": "qos2",
+    "cellId": "cell2"
+  },
+  "statement": {
+    "priorityLevel": 10
+  }
+}
\ No newline at end of file
diff --git a/near-rt-ric-simulator/test/common/compare_json.py b/near-rt-ric-simulator/test/common/compare_json.py
new file mode 100644 (file)
index 0000000..53bddb1
--- /dev/null
@@ -0,0 +1,66 @@
+
+#  ============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=================================================
+#
+
+# Deep compare of two json obects
+# If a parameter value in the target json is set to '????' then the result json value is not checked for that parameter
+# Any included json array will be sorted before comparison
+
+import sys
+import json
+
+def compare_json(jsonTarget,jsonResult):
+
+
+    if isinstance(jsonTarget, dict):
+        if (len(jsonTarget) != len(jsonResult)):
+            return 1
+        for key in jsonTarget.keys():
+            if (jsonResult.get(key) is None):
+                return 1
+            res=compare_json(jsonTarget.get(key), jsonResult.get(key))
+            if (res != 0):
+                return 1
+    elif isinstance(jsonTarget, list):
+        if (len(jsonTarget) != len(jsonResult)):
+            return 1
+        jsonTarget.sort()
+        jsonResult.sort()
+        for i in range(len(jsonTarget)):
+            res=compare_json(jsonTarget[i], jsonResult[i])
+            if (res != 0):
+                return 1
+    else:
+        if (jsonTarget != "????") and (jsonTarget != jsonResult):
+            return 1
+    return 0
+
+
+try:
+    jsonTarget = json.loads(sys.argv[1])
+    jsonResult = json.loads(sys.argv[2])
+
+    print(compare_json(jsonTarget,jsonResult))
+
+except Exception as e:
+    print (1)
+sys.exit()
+
+
+
+
+
diff --git a/near-rt-ric-simulator/test/common/test_common.sh b/near-rt-ric-simulator/test/common/test_common.sh
new file mode 100755 (executable)
index 0000000..b142ce8
--- /dev/null
@@ -0,0 +1,74 @@
+#!/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=================================================
+#
+
+# Function to execute curl and compare + print result
+
+#args: <http-operation> <url> <response-code> [file]
+#Expects the env $RESULT to contain the expected RESULT.
+#If json, the RESULT shall begin with 'json:'.
+#Any json parameter with unknown value shall be given as "????" to skip checking the value.
+do_curl() {
+    if [ $# -lt 3 ]; then
+        echo "Need 3 or more parameters, <http-operation> <url> <response-code> [file]: "$@
+        echo "Exiting test script....."
+        exit 1
+    fi
+    curlstr="curl -X "$1" -sw %{http_code} localhost:"${PORT}${2}" -H accept:*/*"
+    if [ $# -gt 3 ]; then
+        curlstr=$curlstr" -H Content-Type:application/json --data-binary @"$4
+    fi
+    echo "  CMD:"$curlstr
+    res=$($curlstr)
+    status=${res:${#res}-3}
+    body=${res:0:${#res}-3}
+    if [ $status -ne $3 ]; then
+        echo "  Error status :"$status" Expected status: "$3
+        echo "  Body         :"$body
+        echo "Exiting test script....."
+        exit 1
+    else
+        echo "  OK, code           :"$status"     (Expected)"
+        echo "  Body               :"$body
+        if [ "$RESULT" == "*" ]; then
+            echo "  Body contents not checked"
+        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"
+            else
+                echo "  Expected json body :"$result
+                echo "Exiting....."
+                exit 1
+            fi
+        else
+            body="$(echo $body | tr -d '\n' )"
+            if [ "$RESULT" == "$body" ]; then
+                echo "  Expected body      :"$RESULT
+                echo "  Body as expected"
+            else
+                echo "  Expected body      :"$RESULT
+                echo "Exiting....."
+                exit 1
+            fi
+        fi
+    fi
+}
\ No newline at end of file