diff options
-rw-r--r-- | README.md | 381 | ||||
-rw-r--r-- | doc/resources/cells.json | 125 | ||||
-rw-r--r-- | doc/resources/ue.json | 20 | ||||
-rw-r--r-- | doc/resources/vnf.config | 7 | ||||
-rw-r--r-- | doc/swagger/templates/markdown.hbs | 108 | ||||
-rw-r--r-- | doc/swagger/templates/operation.hbs | 73 | ||||
-rw-r--r-- | doc/swagger/templates/security.hbs | 88 | ||||
-rw-r--r-- | doc/swagger/templates/strapdown.html.hbs | 10 | ||||
-rw-r--r-- | pom.xml | 38 |
9 files changed, 850 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..8b5ebcf --- /dev/null +++ b/README.md @@ -0,0 +1,381 @@ +# A1 Policy Enforcement Simulator (A1 PE Simulator) + +Simulator that supports the A1-P OSC\_2.1.0 interface also provides internal API to managed the RAN elements (Cells, Ues) and allows to customize and send VES Events. + +## How to use the A1 PE Simulator? + +### RAN + +A1 PE Simulator needs two main files to define the topology(cells) and user equipments that should be managed (and those cells/ues are used in A1 Policy Enforcement loop). + +- doc/resource/cell.json + +```json +{ + "cellList": [ + { + "Cell": { + "networkId": "RAN001", + "nodeId": "Cell1", + "physicalCellId": 0, + "pnfName": "ncserver1", + "sectorNumber": 0, + "latitude": "50.11", + "longitude": "19.98" + }, + "neighbor": [ + { + "nodeId": "Cell3", + "blacklisted": "false" + }, + { + "nodeId": "Cell4", + "blacklisted": "false" + }, + { + "nodeId": "Cell2", + "blacklisted": "false" + } + ] + } +} +``` + +- doc/resource/ue.json + +```json +[ + { + "id": "emergency_samsung_s10_01", + "latitude": "50.09", + "longitude": "19.94", + "cellId": "Cell1" + }, + { + "id": "emergency_police_01", + "latitude": "50.035", + "longitude": "19.97", + "cellId": "Cell3" + } +] +``` + +Those files location is defined in the *src/main/resources/application.properties*. + +Important: The vnf.config, cells.json, ue.json files should be in the */var/a1pesim/* (default folder location, can be changed). +So copy the content of **doc/resources/** to this location on the host, where simulator will be running. + +How to change those default location, see: + +- **Run A1 PE Simulator with a new configuration** + +How to refresh the content of those files in runtime, see: + +- **Refresh the configuration files in runtime** + +### VES + +A1 PE Simulator provides REST endpoints that can be used to trigger sending VES events to e.q DMaaP topic via VES-COLLECTOR (DCAE MS). + +The file **vnf.config** provides the connectivity configuration like also the sourceId, sourceName values that will be added to the commonEventHeader: + +``` +vesHost=vesconsumer +vesPort=30417 +vesUser=sample1 +vesPassword=sample1 +vnfId=de305d54-75b4-431b-adb2-eb6b9e546014 +vnfName=ibcx0001vm002ssc001 +``` + +- vesHost defines the hostname of the VES consumer +- vesPort defines the port on which consumer expects events +- vesUser and vesPassword are used to create the BasicAuth header in the request +- vnfId, vnfName map to the VES event -> commonEventHeader content: + +```json +{ + "event":{ + "commonEventHeader": { + "sourceId": "de305d54-75b4-431b-adb2-eb6b9e546014", + "sourceName": "ibcx0001vm002ssc001", + ... + }, + ... +} +``` + +Cells can seed two types of VES events **normal** and **failure**. +In both cases the VES events values are mostly the same. +Is only one distinction, the *measurementFields.additionalMeasurements.latency/throughput* values are generated by using different algorithms. + +- for normal VES Events + +```json +{ + ... + "measurementFields" : { + "additionalMeasurements" : [ + { + "name": "latency", + "hashMap": { + "value": "[[10-150]]" + } + }, + { + "name": "throughput", + "hashMap": { + "value": "[[10-100]]" + } + } + ], + ... + } +} +``` + +**10-150** means that the generated values will oscillate between 10 and 150 + +- for failure VES Events + +```json +{ +... +"measurementFields" : { + "additionalMeasurements" : [ + { + "name": "latency", + "hashMap": { + "value": "[[200->500]]" + } + }, + { + "name": "throughput", + "hashMap": { + "value": "[[10->1]]" + } + } + ], + ... + } +} +``` + +**200->500** means that the value will be generated from 200 to 500 (by using the exponential function) + +### A1-P Mediator API + +The A1 Mediator listens on the northbound interface of the RIC for policy guidance. +The caller (e.g., non RT RIC, SMO, etc.) creates policy types and policy instances through A1, and subsequently A1 exchanges messages with xApps via RMR. + +#### Policy Type + +Example schema (use in Policy Enforcement PoC): + +```json +{ + "name": "samsung_policy_type", + "description": "samsung policy type; standard model of a policy with unconstrained scope id combinations", + "policy_type_id": 1000, + "create_schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Samsung_demo", + "description": "Samsung demo policy type", + "type": "object", + "properties": { + "scope": { + "type": "object", + "properties": { + "ueId": { + "type": "string" + }, + "groupId": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "ueId" + ] + }, + "resources": { + "type": "array", + "items": { + "type": "object", + "properties": { + "cellIdList": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string" + } + }, + "preference": { + "type": "string", + "enum": [ + "SHALL", + "PREFER", + "AVOID", + "FORBID" + ] + } + }, + "additionalProperties": false, + "required": [ + "cellIdList", + "preference" + ] + } + } + }, + "additionalProperties": false, + "required": [ + "scope", + "resources" + ] + } +} +``` + +To create the policy type, the proper request, can be sent: + +``` +curl -X PUT -v -H "accept: application/json" -H "Content-Type: application/json" --data-binary @/tmp/policy_type.json localhost:9998/v1/a1-p/policytypes/1000 +``` + +where: + +- @/tmp/policy_type.json file with policy schema +- localhost:9998/v1/a1-p/policytypes/${policy_type_id} - policy type ID use to create the policy instance + +#### Create/Delete Policy Instance + +Create new/Update example policy instance request: + +``` +curl --location --request PUT 'http://localhost:9998/a1-p/policytypes/1000/policies/1' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "scope" : { + "ueId" : "emergency_samsung_s10_01" + }, + "resources" : [ + { + "cellIdList" : [ "Cell1" ], + "preference" : "AVOID" + } + ] +}' +``` + +where: + +- localhost:9998/a1-p/policytypes/${policy_type_id}/policies/${policy_instance_id} - id of the policy instance to create + +Delete the policy instance request: + +``` +curl --location --request DELETE 'http://localhost:9998/a1-p/policytypes/1000/policies/1' +``` + +### Run A1 PE Simulator with a new configuration + +A1 PE Simulator uses the properties to define the: + +- File location for vnf.config, cells.json, ue.json +- VES consumer supported protocol and endpoint (e.q for VES-Collector) +- Cell range +- VES sending default interval +- Version of the A1 PE Simulator API + +To see the default see (src/main/resources/application.properties) + +To override those values one of the options can be used: + +#### 1. By defining the OS env variables + +- VNF_CONFIG_FILE= +- TOPOLOGY_CELL_CONFIG_FILE= +- TOPOLOGY_UE_CONFIG_FILE= +- VES_COLLECTOR_PROTOCOL= +- VES_COLLECTOR_ENDPOINT= +- VES_DEFAULTINTERVAL= +- RESTAPI_VERSION= + +#### 2. By adding the process arguments + +Add the -D flag the running command: + +- "-Dvnf.config.file=" +- "-Dtopology.cell.config.file=" +- "-Dtopology.ue_config.file=" +- "-Dves.collector.protocol=" +- "-Dves.collector.endpoint=" +- "-Dves.defaultinterval=" +- "-Drestapi.version=" + +When running with -Dspring.profiles.active=dev default values for **vnf.config.file**, **topology.cell.config.file** and **topology.ue.config.file** are setup to use the example files from *src/test/resources/* + +### Refresh the configuration files in runtime + +When the content of cells.json, use.json will be changed, the user should send a request to notify the server about this change and A1 PE Simulator will reload those files: + +``` +curl --location --request GET 'http://localhost:9998/v1/ran/refresh' +``` + +Also, A1 PE Simulator automatically refreshes the topology/ues information from those file in defined time interval: + +**refresher.fixed.rate.ms=60000** + +# API + +The API is documented by the Swagger tool. + +## Swagger + +The generated swagger html file can be found in *doc/swagger/html* directory. +JSON file that can be e.q import to Swagger GUI can be found in *doc/swagger*. +Those files are regenerate each maven build. So to generate this file please see **Build the A1 PE Simulator** chapter. + +# Developer Guide + +## Build the A1 PE Simulator + +Following mvn command (in the project directory) will build A1 Policy Enforcement Simulator: + +```bash +mvn clean install +``` + +## Run the A1 PE Simulator + +Following command will run the A1 Policy Enforcement Simulator: + +```bash +java -jar a1-pe-simulator-1.0-SNAPSHOT.jar org.onap.a1pesimulator.A1PolicyEnforcementSimulatorApplication +``` + +The application should start on 9998 port. + +## Logging + +The logs file will be created in the ${user.home}/log path. +To define the **user.home** value, use the process arguments e.g "-Duser.home=/path_to_dir". + +After the A1 PE Simulator starts successfully the */path_to_dir.log* should start to contain the logs: + +``` +. +└── a1-pe-simulator + ├── application + │ ├── debug-2021-03-15.0.log + │ ├── error-2021-03-15.0.log + │ └── metrics-2021-03-15.0.log + └── debug-2021-03-15.0.log +``` + +# Dockerized the A1 PE Simulator + +... diff --git a/doc/resources/cells.json b/doc/resources/cells.json new file mode 100644 index 0000000..42e94c2 --- /dev/null +++ b/doc/resources/cells.json @@ -0,0 +1,125 @@ +{ + "cellList": [ + { + "Cell": { + "networkId": "RAN001", + "nodeId": "Cell1", + "physicalCellId": 0, + "pnfName": "ncserver1", + "sectorNumber": 0, + "latitude": "50.11", + "longitude": "19.98" + }, + "neighbor": [ + { + "nodeId": "Cell3", + "blacklisted": "false" + }, + { + "nodeId": "Cell4", + "blacklisted": "false" + }, + { + "nodeId": "Cell2", + "blacklisted": "false" + } + ] + }, + { + "Cell": { + "networkId": "RAN001", + "nodeId": "Cell2", + "physicalCellId": 1, + "pnfName": "ncserver1", + "sectorNumber": 0, + "latitude": "50.06", + "longitude": "20.03" + }, + "neighbor": [ + { + "nodeId": "Cell5", + "blacklisted": "false" + }, + { + "nodeId": "Cell1", + "blacklisted": "false" + }, + { + "nodeId": "Cell3", + "blacklisted": "false" + } + ] + }, + { + "Cell": { + "networkId": "RAN001", + "nodeId": "Cell3", + "physicalCellId": 3, + "pnfName": "ncserver1", + "sectorNumber": 0, + "latitude": "50.06", + "longitude": "19.94" + }, + "neighbor": [ + { + "nodeId": "Cell5", + "blacklisted": "false" + }, + { + "nodeId": "Cell1", + "blacklisted": "false" + }, + { + "nodeId": "Cell4", + "blacklisted": "false" + }, + { + "nodeId": "Cell2", + "blacklisted": "false" + } + ] + }, + { + "Cell": { + "networkId": "RAN001", + "nodeId": "Cell4", + "physicalCellId": 4, + "pnfName": "ncserver1", + "sectorNumber": 0, + "latitude": "50.11", + "longitude": "19.88" + }, + "neighbor": [ + { + "nodeId": "Cell3", + "blacklisted": "false" + }, + { + "nodeId": "Cell1", + "blacklisted": "false" + } + ] + }, + { + "Cell": { + "networkId": "RAN001", + "nodeId": "Cell5", + "physicalCellId": 6, + "pnfName": "ncserver1", + "sectorNumber": 0, + "latitude": "50.01", + "longitude": "19.99" + }, + "neighbor": [ + { + "nodeId": "Cell3", + "blacklisted": "false" + }, + { + "nodeId": "Cell2", + "blacklisted": "false" + } + ] + } + ] +} diff --git a/doc/resources/ue.json b/doc/resources/ue.json new file mode 100644 index 0000000..2342723 --- /dev/null +++ b/doc/resources/ue.json @@ -0,0 +1,20 @@ +[ + { + "id": "emergency_samsung_s10_01", + "latitude": "50.09", + "longitude": "19.94", + "cellId": "Cell1" + }, + { + "id": "mobile_samsung_s20_02", + "latitude": "50.05", + "longitude": "19.95", + "cellId": "Cell3" + }, + { + "id": "emergency_police_01", + "latitude": "50.035", + "longitude": "19.97", + "cellId": "Cell3" + } +] diff --git a/doc/resources/vnf.config b/doc/resources/vnf.config new file mode 100644 index 0000000..695a222 --- /dev/null +++ b/doc/resources/vnf.config @@ -0,0 +1,7 @@ +vesHost=vesconsumer +vesPort=30417 +vesUser=sample1 +vesPassword=sample1 +vnfId=de305d54-75b4-431b-adb2-eb6b9e546014 +vnfName=ibcx0001vm002ssc001 +vnfType=oran_sim_type
\ No newline at end of file diff --git a/doc/swagger/templates/markdown.hbs b/doc/swagger/templates/markdown.hbs new file mode 100644 index 0000000..546f673 --- /dev/null +++ b/doc/swagger/templates/markdown.hbs @@ -0,0 +1,108 @@ +#{{#info}}{{title}} + + +## {{join schemes " | "}}://{{host}}{{basePath}} + + +{{description}} + +{{#contact}} +[**Contact the developer**](mailto:{{email}}) +{{/contact}} + +**Version** {{version}} + +[**Terms of Service**]({{termsOfService}}) + +{{#license}}[**{{name}}**]({{url}}){{/license}} + +{{/info}} + +{{#if consumes}}**Consumes:** {{join consumes ", "}}{{/if}} + +{{#if produces}}**Produces:** {{join produces ", "}}{{/if}} + +{{#if securityDefinitions}} +# Security Definitions +{{/if}} +{{> security}} + +# APIs + +{{#each paths}} +## {{@key}} +{{#this}} +{{#get}} +### GET +{{> operation}} +{{/get}} + +{{#put}} +### PUT +{{> operation}} +{{/put}} + +{{#post}} +### POST + +{{> operation}} + +{{/post}} + +{{#delete}} +### DELETE +{{> operation}} +{{/delete}} + +{{#option}} +### OPTION +{{> operation}} +{{/option}} + +{{#patch}} +### PATCH +{{> operation}} +{{/patch}} + +{{#head}} +### HEAD +{{> operation}} +{{/head}} + +{{/this}} +{{/each}} + +# Definitions +{{#each definitions}} +## <a name="/definitions/{{key}}">{{@key}}</a> + +<table border="1"> + <tr> + <th>name</th> + <th>type</th> + <th>required</th> + <th>description</th> + <th>example</th> + </tr> + {{#each this.properties}} + <tr> + <td>{{@key}}</td> + <td> + {{#ifeq type "array"}} + {{#items.$ref}} + {{type}}[<a href="{{items.$ref}}">{{basename items.$ref}}</a>] + {{/items.$ref}} + {{^items.$ref}}{{type}}[{{items.type}}]{{/items.$ref}} + {{else}} + {{#$ref}}<a href="{{$ref}}">{{basename $ref}}</a>{{/$ref}} + {{^$ref}}{{type}}{{#format}} ({{format}}){{/format}}{{/$ref}} + {{/ifeq}} + </td> + <td>{{#required}}required{{/required}}{{^required}}optional{{/required}}</td> + <td>{{#description}}{{{description}}}{{/description}}{{^description}}-{{/description}}</td> + <td>{{example}}</td> + </tr> + {{/each}} +</table> +{{/each}} + diff --git a/doc/swagger/templates/operation.hbs b/doc/swagger/templates/operation.hbs new file mode 100644 index 0000000..a581961 --- /dev/null +++ b/doc/swagger/templates/operation.hbs @@ -0,0 +1,73 @@ +{{#deprecated}}-deprecated-{{/deprecated}} +<a id="{{operationId}}">{{summary}}</a> + +{{description}} + +{{#if externalDocs.url}}{{externalDocs.description}}. [See external documents for more details]({{externalDocs.url}}) +{{/if}} + +{{#if security}} +#### Security +{{/if}} + +{{#security}} +{{#each this}} +* {{@key}} +{{#this}} * {{this}} +{{/this}} +{{/each}} +{{/security}} + +#### Request + +{{#if consumes}} +**Content-Type: ** {{join consumes ", "}}{{/if}} + +##### Parameters +{{#if parameters}} +<table border="1"> + <tr> + <th>Name</th> + <th>Located in</th> + <th>Required</th> + <th>Description</th> + <th>Default</th> + <th>Schema</th> + </tr> +{{/if}} + +{{#parameters}} +<tr> + <th>{{name}}</th> + <td>{{in}}</td> + <td>{{#if required}}yes{{else}}no{{/if}}</td> + <td>{{description}}{{#if pattern}} (**Pattern**: `{{pattern}}`){{/if}}</td> + <td> - </td> +{{#ifeq in "body"}} + <td> + {{#ifeq schema.type "array"}}Array[<a href="{{schema.items.$ref}}">{{basename schema.items.$ref}}</a>]{{/ifeq}} + {{#schema.$ref}}<a href="{{schema.$ref}}">{{basename schema.$ref}}</a> {{/schema.$ref}} + </td> +{{else}} + {{#ifeq type "array"}} + <td>Array[{{items.type}}] ({{collectionFormat}})</td> + {{else}} + <td>{{type}} {{#format}}({{format}}){{/format}}</td> + {{/ifeq}} +{{/ifeq}} +</tr> +{{/parameters}} +{{#if parameters}} +</table> +{{/if}} + + +#### Response + +{{#if produces}}**Content-Type: ** {{join produces ", "}}{{/if}} + + +| Status Code | Reason | Response Model | +|-------------|-------------|----------------| +{{#each responses}}| {{@key}} | {{description}} | {{#schema.$ref}}<a href="{{schema.$ref}}">{{basename schema.$ref}}</a>{{/schema.$ref}}{{#ifeq schema.type "array"}}Array[<a href="{{schema.items.$ref}}">{{basename schema.items.$ref}}</a>]{{/ifeq}}{{^schema}} - {{/schema}}| +{{/each}} diff --git a/doc/swagger/templates/security.hbs b/doc/swagger/templates/security.hbs new file mode 100644 index 0000000..04f86e8 --- /dev/null +++ b/doc/swagger/templates/security.hbs @@ -0,0 +1,88 @@ +{{#each securityDefinitions}} +### {{@key}} +{{#this}} +{{#ifeq type "oauth2"}} +<table> + <tr> + <th>type</th> + <th colspan="2">{{type}}</th> + </tr> +{{#if description}} + <tr> + <th>description</th> + <th colspan="2">{{description}}</th> + </tr> +{{/if}} +{{#if authorizationUrl}} + <tr> + <th>authorizationUrl</th> + <th colspan="2">{{authorizationUrl}}</th> + </tr> +{{/if}} +{{#if flow}} + <tr> + <th>flow</th> + <th colspan="2">{{flow}}</th> + </tr> +{{/if}} +{{#if tokenUrl}} + <tr> + <th>tokenUrl</th> + <th colspan="2">{{tokenUrl}}</th> + </tr> +{{/if}} +{{#if scopes}} + <tr> + <td rowspan="3">scopes</td> +{{#each scopes}} + <td>{{@key}}</td> + <td>{{this}}</td> + </tr> + <tr> +{{/each}} + </tr> +{{/if}} +</table> +{{/ifeq}} +{{#ifeq type "apiKey"}} +<table> + <tr> + <th>type</th> + <th colspan="2">{{type}}</th> + </tr> +{{#if description}} + <tr> + <th>description</th> + <th colspan="2">{{description}}</th> + </tr> +{{/if}} +{{#if name}} + <tr> + <th>name</th> + <th colspan="2">{{name}}</th> + </tr> +{{/if}} +{{#if in}} + <tr> + <th>in</th> + <th colspan="2">{{in}}</th> + </tr> +{{/if}} +</table> +{{/ifeq}} +{{#ifeq type "basic"}} +<table> + <tr> + <th>type</th> + <th colspan="2">{{type}}</th> + </tr> +{{#if description}} + <tr> + <th>description</th> + <th colspan="2">{{description}}</th> + </tr> +{{/if}} +</table> +{{/ifeq}} +{{/this}} +{{/each}}
\ No newline at end of file diff --git a/doc/swagger/templates/strapdown.html.hbs b/doc/swagger/templates/strapdown.html.hbs new file mode 100644 index 0000000..ec02669 --- /dev/null +++ b/doc/swagger/templates/strapdown.html.hbs @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<title>API Document</title> + +<xmp theme="united" style="display:none;"> +{{>markdown}} +</xmp> + +<script src="http://strapdownjs.com/v/0.2/strapdown.js"></script> +</html>
\ No newline at end of file @@ -125,6 +125,44 @@ <plugin> <artifactId>maven-resources-plugin</artifactId> </plugin> + <plugin> + <groupId>com.github.kongchen</groupId> + <artifactId>swagger-maven-plugin</artifactId> + <version>3.1.7</version> + <configuration> + <apiSources> + <apiSource> + <springmvc>true</springmvc> + <locations>org.onap.a1pesimulator</locations> + <schemes>http</schemes> + <host>localhost:9998</host> + <basePath>/</basePath> + <info> + <title> A1 Policy Enforcement Simulator REST APIs (Policy Enforcement PoC) </title> + <version>${project.version}</version> + <description> + This interface supports the A1-P OSC_2.1.0 API also provides internal API to managed the RAN elements (Cells, Ues) and allows to customized the send VES Events + </description> + <license> + <name>Copyright (C) 2021 Samsung Electronics</name> + </license> + </info> + <swaggerDirectory>${basedir}/doc/swagger/</swaggerDirectory> + <swaggerFileName>a1-pe-simulator-spec</swaggerFileName> + <templatePath>${basedir}/doc/swagger/templates/strapdown.html.hbs</templatePath> + <outputPath>${basedir}/doc/swagger/html/a1-pe-simulator-api.html</outputPath> + </apiSource> + </apiSources> + </configuration> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>generate</goal> + </goals> + </execution> + </executions> + </plugin> </plugins> </build> |