First commit of the near-rt-ric simulator 26/2826/2
authormaximesson <maxime.bonneau@est.tech>
Thu, 12 Mar 2020 16:24:41 +0000 (17:24 +0100)
committermaximesson <maxime.bonneau@est.tech>
Thu, 19 Mar 2020 18:20:05 +0000 (19:20 +0100)
Change-Id: Ieba6bec9cf745cc2bcfeaef0a11be946f3356153
Issue-ID: NONRTRIC-88
Signed-off-by: maximesson <maxime.bonneau@est.tech>
13 files changed:
near-rt-ric-simulator/README.md [new file with mode: 0644]
near-rt-ric-simulator/a1-openapi.yaml [new file with mode: 0644]
near-rt-ric-simulator/scripts/Dockerfile [new file with mode: 0644]
near-rt-ric-simulator/scripts/a1.py [new file with mode: 0644]
near-rt-ric-simulator/scripts/commands.sh [new file with mode: 0755]
near-rt-ric-simulator/scripts/container-tag.yaml [new file with mode: 0644]
near-rt-ric-simulator/scripts/example_files/policy_instance_1_STD_QoSNudging_0.2.0.json [new file with mode: 0644]
near-rt-ric-simulator/scripts/example_files/policy_instance_1_bis_STD_QoSNudging_0.2.0.json [new file with mode: 0644]
near-rt-ric-simulator/scripts/example_files/policy_instance_2_STD_QoSNudging_0.2.0.json [new file with mode: 0644]
near-rt-ric-simulator/scripts/example_files/policy_type_STD_QoSNudging_0.2.0.json [new file with mode: 0644]
near-rt-ric-simulator/scripts/main.py [new file with mode: 0644]
near-rt-ric-simulator/scripts/start_simulator.sh [new file with mode: 0755]
near-rt-ric-simulator/scripts/var_declaration.py [new file with mode: 0644]

diff --git a/near-rt-ric-simulator/README.md b/near-rt-ric-simulator/README.md
new file mode 100644 (file)
index 0000000..5cd45c0
--- /dev/null
@@ -0,0 +1,57 @@
+# O-RAN-SC Near-RealTime RIC Simulator
+
+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:
+ - 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}
+ - DELETE a policy instance: http://localhost:8085/A1-P/v1/policies/{policyId}
+ - GET a policy status: http://localhost:8085/A1-P/v1/policystatus
+ - GET all policy types: http://localhost:8085/A1-P/v1/policytypes
+ - GET the schemas for a policy type: http://localhost:8085/A1-P/v1/policytypes/{policyTypeId}
+
+Nota Bene: It could happen that this page is not updated as soon as the yaml file is. The yaml file can be found under /near-rt-ric-simulator/a1-openapi.yaml.
+
+Additionally, there are requests that are defined in main.py as an administrative API. The goal is to handle information that couldn't be handled using the A1 interface. The available requests and the addresses are currently:
+ - GET, a basic healthcheck: http://localhost:8085/
+ - PUT a policy type: http://localhost:8085/policytypes/{policyTypeId}
+ - DELETE a policy type: http://localhost:8085/policytypes/{policyTypeId}
+ - DELETE all policy instances: http://localhost:8085/deleteinstances
+ - DELETE all policy types: http://localhost:8085/deletetypes
+ - 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`
+
+# Starting up 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 .
+
+To run it, use the command:
+docker run -it -p 8085:8085 simulator
+
+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.
+
+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
+
+Copyright (C) 2019 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.
+
+For more information about license please see the [LICENSE](LICENSE.txt) file for details.
diff --git a/near-rt-ric-simulator/a1-openapi.yaml b/near-rt-ric-simulator/a1-openapi.yaml
new file mode 100644 (file)
index 0000000..223d1bd
--- /dev/null
@@ -0,0 +1,318 @@
+openapi: 3.0.0
+info:
+  title: 'A1-P Policy Management Service'
+  version: 1.1.x-alpha.2
+  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
+      parameters:
+        - name: policyTypeId
+          in: query
+          required: false
+          schema:
+            "$ref": "#/components/schemas/PolicyTypeId"
+      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: policyTypeId
+          in: query
+          required: false
+          schema:
+            "$ref": "#/components/schemas/PolicyTypeId"
+      requestBody:
+        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"
+        429:
+          "$ref": "#/components/responses/429-TooManyRequests"
+        503:
+          "$ref": "#/components/responses/503-ServiceUnavailable"
+        507:
+          "$ref": "#/components/responses/507-InsufficientStorage"
+    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"
+        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"
+
+  '/policystatus/{policyId}':
+    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"
+        429:
+          "$ref": "#/components/responses/429-TooManyRequests"
+        503:
+          "$ref": "#/components/responses/503-ServiceUnavailable"
+
+  '/policytypes':
+    get:
+      operationId: a1.get_all_policytypes_identities
+      description: 'Get all policy type identities'
+      tags:
+      - All Policy Type Identities
+      responses:
+        200:
+          description: 'Array of all policy type identities'
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  "$ref": "#/components/schemas/PolicyTypeId"
+                minItems: 0
+        429:
+          "$ref": "#/components/responses/429-TooManyRequests"
+        503:
+          "$ref": "#/components/responses/503-ServiceUnavailable"
+
+  '/policytypes/{policyTypeId}':
+    parameters:
+      - name: policyTypeId
+        in: path
+        required: true
+        schema:
+          "$ref": "#/components/schemas/PolicyTypeId"
+    get:
+      operationId: a1.get_policytypes
+      description: 'Get the schemas for a policy type'
+      tags:
+      - Individual Policy Type
+      responses:
+        200:
+          description: 'The policy type schemas'
+          content:
+            application/json:
+              schema:
+                "$ref": "#/components/schemas/PolicyTypeObject"
+        404:
+          "$ref": "#/components/responses/404-NotFound"
+        429:
+          "$ref": "#/components/responses/429-TooManyRequests"
+        503:
+          "$ref": "#/components/responses/503-ServiceUnavailable"
+
+components:
+  schemas:
+    #
+    # Representation objects
+    #
+    PolicyObject:
+      description: 'A generic policy object that can be used to transport any policy. Additionally, a policy shall be valid according to the schema of its specific policy type.'
+      type: object
+
+    PolicyStatusObject:
+      description: 'A generic policy status object that can be used to transport any policy status. Additionally, a policy status shall be valid according to the schema of its specific policy type.'
+      type: object
+
+    PolicyTypeObject:
+      description: 'A definition of a policy type, i.e. the schemas for a policy respectively its status'
+      type: object
+      properties:
+        policySchema:
+          "$ref": "#/components/schemas/JsonSchema"
+        statusSchema:
+          "$ref": "#/components/schemas/JsonSchema"
+      required:
+        - policySchema
+
+    ProblemDetails:
+      description: 'A problem detail to carry details in a HTTP response according to RFC 7807 extended with A1 specific attributes'
+      type: object
+      properties:
+        type:
+          type: string
+        title:
+          type: string
+        status:
+          type: number
+        detail:
+          type: string
+        instance:
+          type: string
+        cause:
+          type: string
+        invalidParams:
+          type: array
+          items:
+            "$ref": "#/components/schemas/InvalidParam"
+          minItems: 1
+
+    #
+    # Structured data types
+    #
+    InvalidParam:
+      description: 'Used in a ProblemDetails to indicate a specific invalid parameter'
+      type: object
+      properties:
+        param:
+          type: string
+        reason:
+          type: string
+      required:
+        - param
+
+    #
+    # Simple data types
+    #
+    JsonSchema:
+      description: 'A JSON schema following http://json-schema.org/draft-07/schema'
+      type: object
+
+    PolicyId:
+      description: 'Policy identifier assigned by the A1-P Consumer when a policy is created'
+      type: string
+
+    PolicyTypeId:
+      description: 'Policy type identifier assigned by the A1-P Provider'
+      type: string
+
+  responses:
+    400-BadRequest:
+      description: 'Object in payload not properly formulated or not related to the method'
+      content:
+        application/problem+json:
+          schema:
+            "$ref": "#/components/schemas/ProblemDetails"
+
+    404-NotFound:
+      description: 'No resource found at the URI'
+      content:
+        application/problem+json:
+          schema:
+            "$ref": "#/components/schemas/ProblemDetails"
+
+    405-MethodNotAllowed:
+      description: 'Method not allowed for the URI'
+      content:
+        application/problem+json:
+          schema:
+            "$ref": "#/components/schemas/ProblemDetails"
+
+    429-TooManyRequests:
+      description: 'Too many requests have been sent in a given amount of time'
+      content:
+        application/problem+json:
+          schema:
+            "$ref": "#/components/schemas/ProblemDetails"
+
+    503-ServiceUnavailable:
+      description: 'The provider is currently unable to handle the request due to a temporary overload'
+      content:
+        application/problem+json:
+          schema:
+            "$ref": "#/components/schemas/ProblemDetails"
+
+    507-InsufficientStorage:
+      description: 'The method could not be performed on the resource because the provider is unable to store the representation needed to successfully complete the request'
+      content:
+        application/problem+json:
+          schema:
+            "$ref": "#/components/schemas/ProblemDetails"
diff --git a/near-rt-ric-simulator/scripts/Dockerfile b/near-rt-ric-simulator/scripts/Dockerfile
new file mode 100644 (file)
index 0000000..6d33914
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# ============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 example_files/policy_instance_1_STD_QoSNudging_0.2.0.json policy_instance_1_STD_QoSNudging_0.2.0.json
+COPY example_files/policy_instance_1_bis_STD_QoSNudging_0.2.0.json policy_instance_1_bis_STD_QoSNudging_0.2.0.json
+COPY example_files/policy_instance_2_STD_QoSNudging_0.2.0.json policy_instance_2_STD_QoSNudging_0.2.0.json
+COPY example_files/policy_type_STD_QoSNudging_0.2.0.json policy_type_STD_QoSNudging_0.2.0.json
+
+COPY a1.py a1.py
+COPY main.py main.py
+COPY var_declaration.py var_declaration.py
+
+COPY commands.sh commands.sh
+COPY start_simulator.sh start_simulator.sh
+
+COPY a1-openapi.yaml a1-openapi.yaml
+
+CMD ["/bin/bash", "./run_me.sh"]
diff --git a/near-rt-ric-simulator/scripts/a1.py b/near-rt-ric-simulator/scripts/a1.py
new file mode 100644 (file)
index 0000000..13ea7dc
--- /dev/null
@@ -0,0 +1,188 @@
+#!/usr/bin/python3
+#  ============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 requests
+
+from connexion import NoContent
+from flask import Flask, escape, request, make_response
+from jsonschema import validate
+from random import random, choice
+from var_declaration import policy_instances, policy_types, policy_status, policy_type_per_instance
+
+def get_all_policy_identities():
+  if len(request.args) == 0:
+    return(list(policy_instances.keys()), 200)
+  elif 'policyTypeId' in request.args:
+    policyTypeId = request.args.get('policyTypeId')
+    if policyTypeId not in list(policy_types.keys()):
+      return(set_error(None, "The policy type provided does not exist.", 400, "The policy type " + data["policyTypeId"] + " is not defined as a policy type.", None, None, "policyTypeId", None))
+    else:
+      return(list({key for key in policy_instances.keys() if policy_type_per_instance[key]==policyTypeId}), 200)
+  else:
+    return(send_error_code(request.args))
+
+def put_policy(policyId):
+  data = request.data.decode("utf-8")
+  data = data.replace("'", "\"")
+  data = json.loads(data)
+  ps = {}
+  if 'policyTypeId' in request.args:
+    policyTypeId = request.args.get('policyTypeId')
+
+    if policyTypeId not in list(policy_types.keys()):
+      return(set_error(None, "The policy type provided does not exist.", 400, "The policy type " + policyTypeId + " is not defined as a policy type.", None, None, "policyTypeId", None))
+
+    policy_schema = policy_types[policyTypeId]["policySchema"]
+    try:
+      validate(instance=data, schema=policy_schema)
+    except:
+      return(set_error(None, "The json does not validate against the schema.", 400, None, None, None, None, None))
+
+    for i in list(policy_instances.keys()):
+      if policyId != i and \
+         data == policy_instances[i] and \
+         policyTypeId == policy_type_per_instance[i]:
+        return(set_error(None, "The policy already exists with a different id.", 400, "No action has been taken. The id of the existing policy instance is: " + i + ".", None, None, None, None))
+
+  if policyId in list(policy_instances.keys()):
+    if data["scope"] != policy_instances[policyId]["scope"]:
+      return(set_error(None, "The policy already exists with a different scope.", 400, "The policy put involves a modification of the existing scope, which is not allowed.", None, None, "scope", None))
+
+  if 'code' in request.args:
+    return(send_error_code(request.args))
+
+  if policyId in policy_instances.keys():
+    code = 201
+  else:
+    code = 200
+
+  policy_instances[policyId] = data
+  policy_status[policyId] = set_status("UNDEFINED")
+  if 'policyTypeId' in request.args:
+    status_schema = policy_types[policyTypeId]["statusSchema"]
+    try:
+      validate(instance=policy_status[policyId], schema=status_schema)
+    except:
+      return(set_error(None, "The json does not validate against the status schema.", 400, None, None, None, None, None))
+    policy_type_per_instance[policyId] = policyTypeId
+  else:
+    policy_type_per_instance[policyId] = "UNDEFINED"
+
+  response = make_response(policy_instances[policyId], code)
+  if code == 201:
+    response.headers['Location'] = "http://localhost:8085/A1-P/v1/policies/" + policyId
+  return response
+
+def set_status(*args):
+  ps = {}
+  ps["enforceStatus"] = args[0]
+  if len(args) == 2:
+    ps["enforceReason"] = args[1]
+  if len(args) > 2:
+    return(set_error(None, "Too many arguments", 400, "There should be no more than two status arguments: enforceStatus and enforceReason", None, None, None, None))
+  return ps
+
+def get_policy(policyId):
+  if len(request.args) == 0:
+    if policyId in policy_instances.keys():
+      res = policy_instances[policyId]
+      res["enforceStatus"] = policy_status[policyId]["enforceStatus"]
+      return(res, 200)
+    else:
+      return(set_error(None, "The requested policy does not exist.", 404, None, None, None, "policyId", None))
+  else:
+    return(send_error_code(request.args))
+
+def delete_policy(policyId):
+  if len(request.args) == 0:
+    if policyId in policy_instances.keys():
+      policy_instances.pop(policyId)
+      policy_status.pop(policyId)
+      policy_type_per_instance.pop(policyId)
+      return(None, 204)
+    else:
+      return(set_error(None, "The policy identity does not exist.", 404, "No policy instance has been deleted.", None, None, "policyId", None))
+  else:
+    return(send_error_code(request.args))
+
+def get_policy_status(policyId):
+  if len(request.args) == 0:
+    if policyId in policy_instances.keys():
+      return(policy_status[policyId], 200)
+    else:
+      return(set_error(None, "The policy identity does not exist.", 404, "There is no existing policy instance with the identity: " + policyId, None, None, "policyId", None))
+  else:
+    return(send_error_code(request.args))
+
+def get_all_policytypes_identities():
+  if len(request.args) == 0:
+    return(list(policy_types.keys()), 200)
+  else:
+    return(send_error_code(request.args))
+
+def get_policytypes(policyTypeId):
+  if len(request.args) == 0:
+    if policyTypeId in policy_types.keys():
+      return(policy_types[policyTypeId], 200)
+    else:
+      return(set_error(None, "The requested policy type does not exist.", 404, None, None, None, "policyTypeId", None))
+  else:
+    return(send_error_code(request.args))
+
+def set_error(type_of, title, status, detail, instance, cause, param, reason):
+  error = {}
+  params = {}
+  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
+  if cause is not None:
+    error["cause"] = cause
+  if param is not None:
+    params["param"] = param
+  if reason is not None:
+    params["reason"] = reason
+  if params:
+    error["invalidParams"] = params
+  return(error, error["status"])
+
+def send_error_code(args):
+  if 'code' in args.keys():
+    code = args['code']
+    if code == '405':
+      return(set_error(None, "Method not allowed", 405, "Method not allowed for the URI", None, None, None, None))
+    elif code == '429':
+      return(set_error(None, "Too many requests", 429, "Too many requests have been sent in a given amount of time", None, None, None, None))
+    elif code == '507':
+      return(set_error(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, None, None, None))
+    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))
+  else:
+    return(set_error(None, "Not found", 400, "No resource found at the URI", None, None, None, None))
diff --git a/near-rt-ric-simulator/scripts/commands.sh b/near-rt-ric-simulator/scripts/commands.sh
new file mode 100755 (executable)
index 0000000..75c1d22
--- /dev/null
@@ -0,0 +1,57 @@
+#!/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=================================================
+#
+
+# Different commands for the simulator.
+# By running this, nothing should return an error.
+
+# Make a test
+curl -v "http://localhost:8085/"
+
+# PUT a policy type STD_QoSNudging_0.2.0
+curl -X PUT -v "http://localhost:8085/policytypes/STD_QoSNudging_0.2.0" -H "accept: application/json" -H "Content-Type: application/json" --data-binary @example_files/policy_type_STD_QoSNudging_0.2.0.json
+
+# GET policy types
+curl -v "http://localhost:8085/A1-P/v1/policytypes"
+
+# GET policy type STD_QoSNudging_0.2.0
+curl -v "http://localhost:8085/A1-P/v1/policytypes/STD_QoSNudging_0.2.0"
+
+# PUT a policy instance pi1
+curl -X PUT -v "http://localhost:8085/A1-P/v1/policies/pi1?policyTypeId=STD_QoSNudging_0.2.0" -H "accept: application/json" -H "Content-Type: application/json" --data-binary @example_files/policy_instance_1_STD_QoSNudging_0.2.0.json
+
+# PUT a policy instance pi2
+curl -X PUT -v "http://localhost:8085/A1-P/v1/policies/pi2?policyTypeId=STD_QoSNudging_0.2.0" -H "accept: application/json" -H "Content-Type: application/json" --data-binary @example_files/policy_instance_2_STD_QoSNudging_0.2.0.json
+
+# SET status for pi1 and pi2
+curl -X PUT "http://localhost:8085/pi1/NOT_ENFORCED/300"
+curl -X PUT "http://localhost:8085/pi2/ENFORCED"
+
+# GET policies
+curl -v "http://localhost:8085/A1-P/v1/policies"
+
+# DELETE policy instance pi2
+curl -X DELETE -v "http://localhost:8085/A1-P/v1/policies/pi2"
+
+# PUT a different policy instance pi1 (i.e. update it)
+curl -X PUT -v "http://localhost:8085/A1-P/v1/policies/pi1?policyTypeId=STD_QoSNudging_0.2.0" -H "accept: application/json" -H "Content-Type: application/json" --data-binary @example_files/policy_instance_1_bis_STD_QoSNudging_0.2.0.json
+
+# GET policy instance pi1
+curl -v "http://localhost:8085/A1-P/v1/policies/pi1"
+
+# GET policy status for pi1
+curl -v "http://localhost:8085/A1-P/v1/policystatus/pi1"
diff --git a/near-rt-ric-simulator/scripts/container-tag.yaml b/near-rt-ric-simulator/scripts/container-tag.yaml
new file mode 100644 (file)
index 0000000..24f0ff7
--- /dev/null
@@ -0,0 +1,2 @@
+---
+tag: 1.0.1-SNAPSHOT
diff --git a/near-rt-ric-simulator/scripts/example_files/policy_instance_1_STD_QoSNudging_0.2.0.json b/near-rt-ric-simulator/scripts/example_files/policy_instance_1_STD_QoSNudging_0.2.0.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/scripts/example_files/policy_instance_1_bis_STD_QoSNudging_0.2.0.json b/near-rt-ric-simulator/scripts/example_files/policy_instance_1_bis_STD_QoSNudging_0.2.0.json
new file mode 100644 (file)
index 0000000..8d2e985
--- /dev/null
@@ -0,0 +1,11 @@
+{
+  "scope": {
+    "ueId": "ue1",
+    "groupId": "group1",
+    "sliceId": "slice1",
+    "qosId": "qos1",
+    "cellId": "cell1"},
+  "statement": {
+    "priorityLevel": 4
+  }
+}
diff --git a/near-rt-ric-simulator/scripts/example_files/policy_instance_2_STD_QoSNudging_0.2.0.json b/near-rt-ric-simulator/scripts/example_files/policy_instance_2_STD_QoSNudging_0.2.0.json
new file mode 100644 (file)
index 0000000..257298c
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "scope": {
+    "ueId": "ue2",
+    "groupId": "group2",
+    "sliceId": "slice2",
+    "qosId": "qos2",
+    "cellId": "cell2"
+  },
+  "statement": {
+    "priorityLevel": 5
+  }
+}
diff --git a/near-rt-ric-simulator/scripts/example_files/policy_type_STD_QoSNudging_0.2.0.json b/near-rt-ric-simulator/scripts/example_files/policy_type_STD_QoSNudging_0.2.0.json
new file mode 100644 (file)
index 0000000..baf1bbe
--- /dev/null
@@ -0,0 +1,46 @@
+{
+  "policySchema": {
+    "$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": true,
+        "required": ["ueId", "qosId"]
+      },
+      "statement": {
+        "type": "object",
+        "properties": {
+          "priorityLevel": {"type": "number"}
+        },
+        "additionalProperties": false,
+        "required": ["priorityLevel"]
+      }
+    }
+  },
+  "statusSchema": {
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "title": "statusSchema",
+    "description": "statusSchema",
+    "type": "object",
+    "properties": {
+      "enforceStatus": {
+        "type": "string",
+        "enum": ["UNDEFINED", "ENFORCED", "NOT_ENFORCED"]
+      },
+      "enforceReason": {
+        "type": "string",
+        "enum": ["100", "200", "300", "800"]
+      },
+      "additionalProperties": false
+    },
+    "if": {"properties": {"enforceStatus": {"const": "NOT_ENFORCED"}}},
+    "then": {"required": ["enforceReason"]}
+  }
+}
diff --git a/near-rt-ric-simulator/scripts/main.py b/near-rt-ric-simulator/scripts/main.py
new file mode 100644 (file)
index 0000000..749058d
--- /dev/null
@@ -0,0 +1,128 @@
+#!/usr/bin/python3
+#  ============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 fileinput
+import json
+import sys
+
+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
+
+app = connexion.App(__name__, specification_dir='.')
+
+@app.route('/policytypes/<string:policyTypeId>', methods=['PUT','DELETE'])
+def policy_type(policyTypeId):
+  if request.method == 'PUT':
+    data = request.data.decode("utf-8")
+    data = data.replace("'", "\"")
+    data = json.loads(data)
+    policy_types[policyTypeId] = data
+    return ('The policy type was either created or updated for policy type id: ' + policyTypeId)
+  elif request.method == 'DELETE':
+    if policyTypeId in policy_types.keys():
+      policy_types.pop(policyTypeId)
+      return make_response("policy type successfully deleted for policy type id: " + policyTypeId, 200)
+    else:
+      return make_response("No policy type defined for the specified id", 404)
+
+@app.route('/', methods=['GET'])
+def test():
+    return("Everything is fine", 200)
+
+@app.route('/deleteinstances', methods=['DELETE'])
+def delete_instances():
+  global policy_instances
+  global policy_status
+  global policy_type_per_instance
+  policy_instances.clear()
+  policy_status.clear()
+  policy_type_per_instance.clear()
+  return("All policy instances deleted", 200)
+
+@app.route('/deletetypes', methods=['DELETE'])
+def delete_types():
+  global policy_types
+  policy_types.clear()
+  return("All policy types deleted", 200)
+
+@app.route('/<string:policyId>/<string:enforceStatus>', methods=['PUT'])
+def set_status(policyId, enforceStatus):
+  if policyId in policy_instances.keys():
+    if policy_type_per_instance[policyId] == "UNDEFINED":
+      ps = {}
+      ps["policyId"] = policyId
+      ps["enforceStatus"] = enforceStatus
+    else:
+      policy_type_id = policy_type_per_instance[policyId]
+      status_schema = policy_types[policy_type_id]["statusSchema"]
+      ps = {}
+      ps["policyId"] = policyId
+      ps["enforceStatus"] = enforceStatus
+      try:
+        validate(instance=ps, schema=status_schema)
+      except:
+        return(set_error(None, "The json does not validate against the status schema.", 400, None, None, None, None, None))
+  policy_status.pop(policyId)
+  policy_status[policyId] = ps
+  return("Status updated for policy: " + policyId, 200)
+
+@app.route('/<string:policyId>/<string:enforceStatus>/<string:enforceReason>', methods=['PUT'])
+def set_status_with_reason(policyId, enforceStatus, enforceReason):
+  if policyId in policy_instances.keys():
+    if policy_type_per_instance[policyId] == "UNDEFINED":
+      ps = {}
+      ps["policyId"] = policyId
+      ps["enforceStatus"] = enforceStatus
+      ps["enforceReason"] = enforceReason
+    else:
+      policy_type_id = policy_type_per_instance[policyId]
+      status_schema = policy_types[policy_type_id]["statusSchema"]
+      ps = {}
+      ps["policyId"] = policyId
+      ps["enforceStatus"] = enforceStatus
+      ps["enforceReason"] = enforceReason
+      try:
+        validate(instance=ps, schema=status_schema)
+      except:
+        return(set_error(None, "The json does not validate against the status schema.", 400, None, None, None, None, None))
+  policy_status.pop(policyId)
+  policy_status[policyId] = ps
+  return("Status updated for policy: " + policyId, 200)
+
+#Metrics function
+
+@app.route('/counter/<string:countername>', methods=['GET'])
+def getCounter(countername):
+    if (countername == "num_instances"):
+        return str(len(policy_instances)),200
+    elif (countername == "num_types"):
+        return str(len(policy_types)),200
+    else:
+        return "Counter name: "+countername+" not found.",404
+
+
+port_number = 8085
+if len(sys.argv) >= 2:
+  if isinstance(sys.argv[1], int):
+    port_number = sys.argv[1]
+
+app.add_api('../a1-openapi.yaml')
+app.run(port=port_number)
+
diff --git a/near-rt-ric-simulator/scripts/start_simulator.sh b/near-rt-ric-simulator/scripts/start_simulator.sh
new file mode 100755 (executable)
index 0000000..d5da893
--- /dev/null
@@ -0,0 +1,26 @@
+#!/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=================================================
+#
+
+# One argument can be used along with the script call: it is the port on which one wish to run the simulator.
+
+if [ $# -eq 0 ]
+then
+  python3 ./main.py
+else
+  python3 ./main.py $1
+fi
diff --git a/near-rt-ric-simulator/scripts/var_declaration.py b/near-rt-ric-simulator/scripts/var_declaration.py
new file mode 100644 (file)
index 0000000..54621ad
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/env python3
+
+policy_instances = {}
+policy_types = {}
+policy_status = {}
+policy_type_per_instance = {}