diff options
author | Lasse Kaihlavirta <l.kaihlavirt@partner.samsung.com> | 2021-03-24 08:56:10 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@onap.org> | 2021-03-24 08:56:10 +0000 |
commit | 29e0ba3179409458b96089dd37452d4a11cc32d3 (patch) | |
tree | 615dfa2bb37b0cc6a1c5605b15b5253a2404d224 | |
parent | 14433f2704a39d737e79543fe66db53d6d288697 (diff) | |
parent | 5ea372baed3b743dbf3ddeffb8fe1a0acdf376d6 (diff) |
Merge changes Idbdec7b3,I643dadac,I2f54e1d5,I2ead5203
* changes:
Provide initial a1-pe-sim docs
Initial code check-in for A1 Policy Enforcement Simulator
Add basic .gitignore
Add linters setup
107 files changed, 6917 insertions, 0 deletions
diff --git a/.coafile b/.coafile new file mode 100644 index 0000000..aaae2c1 --- /dev/null +++ b/.coafile @@ -0,0 +1,29 @@ +[yaml] +bears = YAMLLintBear +yamllint_config = .yamllint +ignore = + .tox/** + +[json] +bears = JSONFormatBear +json_sort = False +indent_size = 2 +ignore = + .tox/** + +[py] +bears = PyLintBear +pylint_disable = all +pylint_enable = + bad-indentation, trailing-whitespace, unused-wildcard-import, unused-import, + unnecessary-semicolon, unnecessary-semicolon, undefined-variable, + syntax-error, unused-variable, using-constant-test,unused-argument, + len-as-condition, trailing-newlines, missing-final-newline, reimported, + too-many-function-args, singleton-comparison +ignore = + .tox/** + +[md] +bears = MarkdownBear +ignore = + .tox/** diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..21f73a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,107 @@ +### Java template +# Compiled class file +*.class + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +**/.idea + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### Maven template +**/target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar + +### Eclipse template +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# Java annotation processor (APT) +.factorypath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ +.apt_generated_test/ + +# Uncomment this line if you wish to ignore the project description file. +# Typically, this file would be tracked if it contains build/dependency configurations: +#.project +a1-pe-simulator.iml diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..ab5bdc3 --- /dev/null +++ b/.yamllint @@ -0,0 +1,16 @@ +--- + +extends: default + +rules: + line-length: disable + truthy: disable + braces: + max-spaces-inside: 1 + brackets: + max-spaces-inside: 1 + comments-indentation: disable + comments: disable + document-start: disable + indentation: + indent-sequences: whatever 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 @@ -0,0 +1,169 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2021 Samsung Electronics + ~ 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 + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.onap.a1pesimulator</groupId> + <artifactId>a1-pe-simulator</artifactId> + <version>1.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <parent> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-parent</artifactId> + <version>2.3.0.RELEASE</version> + <relativePath/> + </parent> + + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-cache</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-websocket</artifactId> + </dependency> + <dependency> + <groupId>com.github.ben-manes.caffeine</groupId> + <artifactId>caffeine</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.tomcat.embed</groupId> + <artifactId>tomcat-embed-jasper</artifactId> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-devtools</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient</artifactId> + </dependency> + <dependency> + <groupId>io.springfox</groupId> + <artifactId>springfox-swagger2</artifactId> + <version>2.9.2</version> + </dependency> + <dependency> + <groupId>io.springfox</groupId> + <artifactId>springfox-swagger-ui</artifactId> + <version>2.9.2</version> + </dependency> + <dependency> + <groupId>javax.xml.bind</groupId> + <artifactId>jaxb-api</artifactId> + <version>2.3.0</version> + </dependency> + </dependencies> + + <properties> + <java.version>11</java.version> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <version>0.8.4</version> + <executions> + <execution> + <id>prepare-agent</id> + <goals> + <goal>prepare-agent</goal> + </goals> + </execution> + <execution> + <id>report</id> + <phase>prepare-package</phase> + <goals> + <goal>report</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + </plugin> + <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> + +</project> diff --git a/src/main/java/org/onap/a1pesimulator/A1PolicyEnforcementSimulatorApplication.java b/src/main/java/org/onap/a1pesimulator/A1PolicyEnforcementSimulatorApplication.java new file mode 100644 index 0000000..fad5083 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/A1PolicyEnforcementSimulatorApplication.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class A1PolicyEnforcementSimulatorApplication { + + public static void main(String[] args) { + SpringApplication.run(A1PolicyEnforcementSimulatorApplication.class, args); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/configuration/SwaggerConfig.java b/src/main/java/org/onap/a1pesimulator/configuration/SwaggerConfig.java new file mode 100644 index 0000000..f534153 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/configuration/SwaggerConfig.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select() + .apis(RequestHandlerSelectors.basePackage("org.onap.a1pesimulator")).paths(PathSelectors.any()) + .build(); + } + + public ApiInfo apiInfo() { + final ApiInfoBuilder builder = new ApiInfoBuilder(); + builder.title("A1 Policy Enforcement Simulator REST APIs") + .description("A1 Policy Enforcement Simulator REST interfaces (Policy Enforcement PoC)") + .version("1.0.0").license("Copyright (C) 2021 Samsung Electronics"); + return builder.build(); + } +}
\ No newline at end of file diff --git a/src/main/java/org/onap/a1pesimulator/configuration/VesBrokerConfiguration.java b/src/main/java/org/onap/a1pesimulator/configuration/VesBrokerConfiguration.java new file mode 100644 index 0000000..4003515 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/configuration/VesBrokerConfiguration.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.configuration; + +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class VesBrokerConfiguration { + + @Bean + public RestTemplate restTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { + TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() { + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { + } + }}; + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + CloseableHttpClient httpClient = + HttpClients.custom().setSSLContext(sslContext).setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) + .build(); + HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); + + requestFactory.setHttpClient(httpClient); + return new RestTemplate(requestFactory); + } + +} diff --git a/src/main/java/org/onap/a1pesimulator/configuration/VesPmThreadPoolTaskSchedulerConfig.java b/src/main/java/org/onap/a1pesimulator/configuration/VesPmThreadPoolTaskSchedulerConfig.java new file mode 100644 index 0000000..9105544 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/configuration/VesPmThreadPoolTaskSchedulerConfig.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.configuration; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +@Configuration +public class VesPmThreadPoolTaskSchedulerConfig { + + private Integer poolSize; + + public VesPmThreadPoolTaskSchedulerConfig(@Value("${ves.pm.maxPoolSize}") Integer poolSize) { + this.poolSize = poolSize; + } + + @Bean + public ThreadPoolTaskScheduler vesPmThreadPoolTaskScheduler() { + ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); + threadPoolTaskScheduler.setPoolSize(poolSize); + threadPoolTaskScheduler.setThreadNamePrefix("VesPmThreadPoolTaskScheduler"); + return threadPoolTaskScheduler; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/configuration/WebConfig.java b/src/main/java/org/onap/a1pesimulator/configuration/WebConfig.java new file mode 100644 index 0000000..166361e --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/configuration/WebConfig.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**"); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/configuration/WebSocketConfig.java b/src/main/java/org/onap/a1pesimulator/configuration/WebSocketConfig.java new file mode 100644 index 0000000..8cf24bc --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/configuration/WebSocketConfig.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +@Configuration +@EnableWebSocketMessageBroker +public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS(); + } + + @Override + public void configureMessageBroker(MessageBrokerRegistry config) { + config.enableSimpleBroker("/topic", "/queue"); + } +}
\ No newline at end of file diff --git a/src/main/java/org/onap/a1pesimulator/controller/RanA1Controller.java b/src/main/java/org/onap/a1pesimulator/controller/RanA1Controller.java new file mode 100644 index 0000000..4262d27 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/controller/RanA1Controller.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.controller; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Collection; +import org.onap.a1pesimulator.data.PolicyNotification; +import org.onap.a1pesimulator.data.PolicyNotificationActionEnum; +import org.onap.a1pesimulator.service.a1.A1Service; +import org.onap.a1pesimulator.service.a1.OnPolicyAction; +import org.onap.a1pesimulator.service.a1.PolicyInstancesHolder; +import org.springframework.http.ResponseEntity; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * A1 interface facade + * Only operations defined by OSC_2.1.0 should be provided here + */ + +@RestController +@RequestMapping({"${restapi.version}/a1-p"}) +public class RanA1Controller { + + private static final String TOPIC_POLICY = "/topic/policy"; + private final SimpMessagingTemplate messagingTemplate; + private final A1Service a1Service; + private final Collection<OnPolicyAction> onPolicyActions; + private final PolicyInstancesHolder policyHolder; + + public RanA1Controller(A1Service a1Service, SimpMessagingTemplate messagingTemplate, + Collection<OnPolicyAction> onPolicyActions, PolicyInstancesHolder policyHolder) { + this.a1Service = a1Service; + this.messagingTemplate = messagingTemplate; + this.onPolicyActions = onPolicyActions; + this.policyHolder = policyHolder; + } + + @GetMapping(value = "/healthcheck") + public ResponseEntity<String> healthcheck() throws URISyntaxException { + return a1Service.healthCheck(); + } + + @PutMapping(value = "/policytypes/{policyTypeId}") + public ResponseEntity<String> putPolicySchema(@PathVariable Integer policyTypeId, @RequestBody String body) + throws URISyntaxException { + return a1Service.putPolicySchema(policyTypeId, body); + } + + @PutMapping(value = "/policytypes/{policyTypeId}/policies/{policyInstanceId}") + public ResponseEntity<String> putPolicyInstance(@PathVariable Integer policyTypeId, + @PathVariable String policyInstanceId, @RequestBody String body) throws URISyntaxException { + ResponseEntity<String> response = a1Service.putPolicy(policyTypeId, policyInstanceId, body); + if (!response.getStatusCode().is2xxSuccessful()) { + return response; + } + policyHolder.addPolicy(policyInstanceId, body); + onPolicyActions.forEach(action -> handleOnPolicyAction(policyTypeId, policyInstanceId, body, action)); + messagingTemplate.convertAndSend(TOPIC_POLICY, + new PolicyNotification(policyInstanceId, policyTypeId, PolicyNotificationActionEnum.CREATED, body)); + return response; + } + + @DeleteMapping(value = "/policytypes/{policyTypeId}/policies/{policyInstanceId}") + public ResponseEntity<String> deletePolicyInstance(@PathVariable Integer policyTypeId, + @PathVariable String policyInstanceId) throws URISyntaxException { + ResponseEntity<String> response = a1Service.deletePolicy(policyTypeId, policyInstanceId); + if (!response.getStatusCode().is2xxSuccessful()) { + return response; + } + + policyHolder.removePolicy(policyInstanceId); + messagingTemplate.convertAndSend(TOPIC_POLICY, + new PolicyNotification(policyInstanceId, policyTypeId, PolicyNotificationActionEnum.DELETED)); + return response; + } + + @GetMapping(value = "/policytypes") + public ResponseEntity<String> getPolicyTypeIds() throws URISyntaxException { + return a1Service.getPolicyTypeIds(); + } + + @GetMapping(value = "/policytypes/{policyTypeId}") + public ResponseEntity<String> getPolicyType(@PathVariable Integer policyTypeId) throws URISyntaxException { + return a1Service.getPolicyType(policyTypeId); + } + + @GetMapping(value = "/policytypes/{policyTypeId}/policies") + public ResponseEntity<String> getPolicyIdsOfType(@PathVariable Integer policyTypeId) + throws URISyntaxException, IOException { + return a1Service.getPolicyIdsOfType(policyTypeId); + } + + @GetMapping(value = "/policytypes/{policyTypeId}/policies/{policyInstanceId}") + public ResponseEntity<String> getPolicy(@PathVariable Integer policyTypeId, @PathVariable String policyInstanceId) + throws URISyntaxException { + return a1Service.getPolicy(policyTypeId, policyInstanceId); + } + + private void handleOnPolicyAction(Integer policyTypeId, String policyId, String body, OnPolicyAction action) { + if (action.isForMe(policyTypeId, policyId, body)) { + action.onPolicy(policyTypeId, policyId, body); + } + } +} diff --git a/src/main/java/org/onap/a1pesimulator/controller/RanCellController.java b/src/main/java/org/onap/a1pesimulator/controller/RanCellController.java new file mode 100644 index 0000000..d454116 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/controller/RanCellController.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.controller; + +import java.util.Optional; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.RanCell; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.RanPeriodicVesEvent; +import org.onap.a1pesimulator.service.cell.RanCellService; +import org.onap.a1pesimulator.service.cell.RanCellStateService; +import org.onap.a1pesimulator.service.ves.RanVesBrokerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping({"${restapi.version}/ran/cells"}) +public class RanCellController { + + private static final Logger log = LoggerFactory.getLogger(RanCellController.class); + private final RanCellService ranCellService; + private final RanCellStateService ranCellStateService; + private final RanVesBrokerService ranVesBrokerService; + + public RanCellController(RanCellService ranCellService, RanCellStateService ranCellStateService, + RanVesBrokerService ranVesBrokerService) { + this.ranCellService = ranCellService; + this.ranCellStateService = ranCellStateService; + this.ranVesBrokerService = ranVesBrokerService; + } + + @GetMapping + public ResponseEntity<RanCell> getCells() { + return ResponseEntity.ok(ranCellService.getCells()); + } + + @GetMapping(value = "/{identifier}") + public ResponseEntity<CellDetails> getCellById(final @PathVariable String identifier) { + + if (!ranCellService.getCellIds().contains(identifier)) { + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok(ranCellService.getCellById(identifier)); + } + + @PostMapping(value = "/{identifier}/startFailure") + public ResponseEntity<String> startSendingFailureVesEvents(final @PathVariable String identifier) { + + ranCellService.failure(identifier); + ranVesBrokerService.startSendingFailureVesEvents(identifier); + ranCellStateService.failingState(identifier); + + return ResponseEntity.accepted().body("Failure VES Event sending started"); + } + + @PostMapping(value = "/{identifier}/stopFailure") + public ResponseEntity<Void> stopSendingFailureVesEvents(final @PathVariable String identifier) { + + ranCellService.recoverFromFailure(identifier); + + Optional<RanPeriodicVesEvent> vesEvent = ranVesBrokerService.stopSendingVesEvents(identifier); + + if (!vesEvent.isPresent()) { + return ResponseEntity.notFound().build(); + } + + ranCellStateService.stopState(identifier); + return ResponseEntity.accepted().build(); + } + + @PostMapping(value = "/{identifier}/start") + public ResponseEntity<String> startSendingVesEvents(final @RequestBody Optional<Event> vesEventOpt, + final @PathVariable String identifier, final @RequestParam(required = false) Integer interval) { + log.info("Start sending ves events every {} seconds for {} ", getInterval(interval), identifier); + + Event vesEvent = vesEventOpt.orElse(ranVesBrokerService.getGlobalPmVesStructure()); + + ResponseEntity<String> responseEntity = + ranVesBrokerService.startSendingVesEvents(identifier, vesEvent, getInterval(interval)); + if (!responseEntity.getStatusCode().is2xxSuccessful()) { + return responseEntity; + } + + ranCellStateService.activateState(identifier); + return responseEntity; + } + + @PostMapping(value = "/{identifier}/stop") + public ResponseEntity<Void> stopSendingVesEvents(final @PathVariable String identifier) { + log.info("Stop sending custom ves events for {}", identifier); + Optional<RanPeriodicVesEvent> vesEvent = ranVesBrokerService.stopSendingVesEvents(identifier); + if (!vesEvent.isPresent()) { + return ResponseEntity.notFound().build(); + } + + ranCellStateService.stopState(identifier); + return ResponseEntity.accepted().build(); + } + + @GetMapping(value = "/{identifier}/eventStructure") + public ResponseEntity<Event> getVesEventStructure(final @PathVariable String identifier) { + if (!ranVesBrokerService.getEnabledEventElementIdentifiers().contains(identifier)) { + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok(ranVesBrokerService.getEventStructure(identifier)); + } + + private Integer getInterval(Integer requested) { + if (requested == null) { + return ranVesBrokerService.getGlobalVesInterval(); + } + return requested; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/controller/RanController.java b/src/main/java/org/onap/a1pesimulator/controller/RanController.java new file mode 100644 index 0000000..03ea4b8 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/controller/RanController.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.controller; + +import org.onap.a1pesimulator.data.Topology; +import org.onap.a1pesimulator.service.cell.RanCellService; +import org.onap.a1pesimulator.util.ItemsRefresher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping({"${restapi.version}/ran"}) +public class RanController { + + private static final Logger log = LoggerFactory.getLogger(RanController.class); + private final RanCellService ranCellService; + private final ItemsRefresher refresher; + + public RanController(RanCellService ranCellService, final ItemsRefresher refresher) { + this.ranCellService = ranCellService; + this.refresher = refresher; + } + + @GetMapping + public ResponseEntity<Topology> getRan() { + return ResponseEntity.ok(ranCellService.getTopology()); + } + + @GetMapping(value = "/refresh") + public ResponseEntity<Void> refreshRan() { + refresher.refresh(); + log.info("Refreshed the items on request"); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/controller/RanEventConfigureController.java b/src/main/java/org/onap/a1pesimulator/controller/RanEventConfigureController.java new file mode 100644 index 0000000..5efe07a --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/controller/RanEventConfigureController.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.controller; + +import org.onap.a1pesimulator.data.ves.GlobalVesConfiguration; +import org.onap.a1pesimulator.service.ves.RanVesBrokerService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping({"${restapi.version}/ran/eventConfig"}) +public class RanEventConfigureController { + + private final RanVesBrokerService ranVesBrokerService; + + public RanEventConfigureController(RanVesBrokerService ranVesBrokerService) { + this.ranVesBrokerService = ranVesBrokerService; + } + + @GetMapping + public ResponseEntity<GlobalVesConfiguration> getEventConfig() { + GlobalVesConfiguration config = new GlobalVesConfiguration(ranVesBrokerService.getGlobalVesInterval(), + ranVesBrokerService.getGlobalPmVesStructure()); + return ResponseEntity.ok(config); + } + + @PostMapping + public ResponseEntity<Void> setEventConfig(final @RequestBody GlobalVesConfiguration config) { + ranVesBrokerService.setGlobalPmVesStructure(config.getEvent()); + ranVesBrokerService.setGlobalVesInterval(config.getInterval()); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/controller/RanPolicyController.java b/src/main/java/org/onap/a1pesimulator/controller/RanPolicyController.java new file mode 100644 index 0000000..bfe645c --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/controller/RanPolicyController.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.controller; + +import java.io.IOException; +import java.net.URISyntaxException; +import org.onap.a1pesimulator.service.a1.A1Service; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +@Controller +public class RanPolicyController { + + private final A1Service a1Service; + + public RanPolicyController(A1Service a1Service) { + this.a1Service = a1Service; + } + + /** + * Method for reading all policies of given policy type from A1 PE Simulator in one go + * + * @return Policy instance list for wanted policyType + * @throws IOException + * @throws URISyntaxException + */ + @GetMapping(value = "${restapi.version}/ran/policies/{policyTypeId}") + public ResponseEntity<String> getAllPolicies(@PathVariable Integer policyTypeId) + throws IOException, URISyntaxException { + return a1Service.getAllPoliciesForType(policyTypeId); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/controller/RanUeController.java b/src/main/java/org/onap/a1pesimulator/controller/RanUeController.java new file mode 100644 index 0000000..5cbaaba --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/controller/RanUeController.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.controller; + +import java.util.Optional; +import org.onap.a1pesimulator.data.ue.RanUserEquipment; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.service.ue.RanUeService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping({"${restapi.version}/ran/ues"}) +public class RanUeController { + + private final RanUeService ranUeService; + + public RanUeController(RanUeService ranUeService) { + this.ranUeService = ranUeService; + } + + @GetMapping + public ResponseEntity<RanUserEquipment> getUes() { + return ResponseEntity.ok(ranUeService.getUes()); + } + + @GetMapping(value = "/{identifier}") + public ResponseEntity<UserEquipment> getUeById(final @PathVariable String identifier) { + + Optional<UserEquipment> userEquipment = ranUeService.getUserEquipment(identifier); + return userEquipment.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); + + } +} diff --git a/src/main/java/org/onap/a1pesimulator/data/PolicyNotification.java b/src/main/java/org/onap/a1pesimulator/data/PolicyNotification.java new file mode 100644 index 0000000..94f2160 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/PolicyNotification.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class PolicyNotification { + + private String id; + private Integer typeId; + private PolicyNotificationActionEnum action; + private String content; + + public PolicyNotification(String id, Integer typeId, PolicyNotificationActionEnum action) { + this.id = id; + this.typeId = typeId; + this.action = action; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/data/PolicyNotificationActionEnum.java b/src/main/java/org/onap/a1pesimulator/data/PolicyNotificationActionEnum.java new file mode 100644 index 0000000..64531bc --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/PolicyNotificationActionEnum.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data; + +import com.fasterxml.jackson.annotation.JsonFormat; + +@JsonFormat(shape = JsonFormat.Shape.OBJECT) +public enum PolicyNotificationActionEnum { + + CREATED("CREATED"), DELETED("DELETED"); + public final String value; + + PolicyNotificationActionEnum(String stateName) { + this.value = stateName; + } + +} diff --git a/src/main/java/org/onap/a1pesimulator/data/Topology.java b/src/main/java/org/onap/a1pesimulator/data/Topology.java new file mode 100644 index 0000000..0658787 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/Topology.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data; + +import java.util.Collection; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.ue.UserEquipment; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Topology { + + Collection<CellDetails> cells; + Collection<UserEquipment> userEquipments; +} diff --git a/src/main/java/org/onap/a1pesimulator/data/VnfConfig.java b/src/main/java/org/onap/a1pesimulator/data/VnfConfig.java new file mode 100644 index 0000000..bd0e1d0 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/VnfConfig.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + + +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +public class VnfConfig { + + @JsonProperty("vesHost") + private String vesHost; + @JsonProperty("vesPort") + private String vesPort; + @JsonProperty("vesUser") + private String vesUser; + @JsonProperty("vesPassword") + private String vesPassword; + @JsonProperty("vnfId") + private String vnfId; + @JsonProperty("vnfName") + private String vnfName; +} diff --git a/src/main/java/org/onap/a1pesimulator/data/cell/Cell.java b/src/main/java/org/onap/a1pesimulator/data/cell/Cell.java new file mode 100644 index 0000000..8c8c0ce --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/cell/Cell.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.cell; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class Cell { + + private String identifier; +} diff --git a/src/main/java/org/onap/a1pesimulator/data/cell/CellDetails.java b/src/main/java/org/onap/a1pesimulator/data/cell/CellDetails.java new file mode 100644 index 0000000..249e26b --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/cell/CellDetails.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.cell; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Collection; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import org.onap.a1pesimulator.data.cell.state.CellStateEnum; +import org.onap.a1pesimulator.data.cell.state.CellStateMachine; +import org.onap.a1pesimulator.data.cell.state.machine.InactiveState; + +@Getter +@Builder +public class CellDetails { + + private String id; + private Double latitude; + private Double longitude; + + @Setter + @JsonIgnore + @Builder.Default + private CellStateMachine cellStateMachine = new InactiveState(); + + @Setter + private Collection<String> connectedUserEquipments; + + public void previousState() { + cellStateMachine.prev(this); + } + + public void nextState() { + cellStateMachine.next(this); + } + + @JsonProperty("currentState") + public CellStateEnum getCurrentState() { + return cellStateMachine.getState(); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/data/cell/CellList.java b/src/main/java/org/onap/a1pesimulator/data/cell/CellList.java new file mode 100644 index 0000000..627d43b --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/cell/CellList.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.cell; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Collections; +import java.util.List; +import lombok.Getter; + +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +public class CellList { + + private final List<CellData> cellList; + + public CellList() { + cellList = Collections.emptyList(); + } + + @Getter + @JsonIgnoreProperties(ignoreUnknown = true) + public static class CellData { + + @JsonProperty("Cell") + private Cell cell; + } + + @Getter + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Cell { + + private String nodeId; + private Double latitude; + private Double longitude; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/data/cell/CellWithStatus.java b/src/main/java/org/onap/a1pesimulator/data/cell/CellWithStatus.java new file mode 100644 index 0000000..f1419f7 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/cell/CellWithStatus.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.cell; + +import lombok.Builder; +import lombok.Getter; +import org.onap.a1pesimulator.data.cell.state.CellStateEnum; + +@Builder +@Getter +public class CellWithStatus { + + private Cell cell; + private boolean vesEnabled; + private boolean failureMode; + private CellStateEnum state; +} diff --git a/src/main/java/org/onap/a1pesimulator/data/cell/RanCell.java b/src/main/java/org/onap/a1pesimulator/data/cell/RanCell.java new file mode 100644 index 0000000..8ae9243 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/cell/RanCell.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.cell; + +import java.util.Collection; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class RanCell { + + private Collection<CellDetails> cells; + private int itemsLength; +} diff --git a/src/main/java/org/onap/a1pesimulator/data/cell/state/CellStateEnum.java b/src/main/java/org/onap/a1pesimulator/data/cell/state/CellStateEnum.java new file mode 100644 index 0000000..a25156a --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/cell/state/CellStateEnum.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.cell.state; + +import com.fasterxml.jackson.annotation.JsonFormat; + +@JsonFormat(shape = JsonFormat.Shape.OBJECT) +public enum CellStateEnum { + + INACTIVE("INACTIVE"), ACTIVE("ACTIVE"), GOING_TO_SLEEP("GOING_TO_SLEEP"), SLEEPING("SLEEPING"); + + public final String value; + + CellStateEnum(String stateName) { + this.value = stateName; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/data/cell/state/CellStateMachine.java b/src/main/java/org/onap/a1pesimulator/data/cell/state/CellStateMachine.java new file mode 100644 index 0000000..f3c7bf0 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/cell/state/CellStateMachine.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.cell.state; + +import org.onap.a1pesimulator.data.cell.CellDetails; + +public abstract class CellStateMachine { + + private CellStateEnum state; + + protected CellStateMachine(CellStateEnum state) { + this.state = state; + } + + public abstract void next(CellDetails cell); + + public abstract void prev(CellDetails cell); + + public CellStateEnum getState() { + return state; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/data/cell/state/machine/ActiveState.java b/src/main/java/org/onap/a1pesimulator/data/cell/state/machine/ActiveState.java new file mode 100644 index 0000000..c7d600d --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/cell/state/machine/ActiveState.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.cell.state.machine; + +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.state.CellStateEnum; +import org.onap.a1pesimulator.data.cell.state.CellStateMachine; + +public class ActiveState extends CellStateMachine { + + public ActiveState() { + super(CellStateEnum.ACTIVE); + } + + @Override + public void next(CellDetails cell) { + cell.setCellStateMachine(new GoingToSleepingState()); + } + + @Override + public void prev(CellDetails cell) { + cell.setCellStateMachine(new InactiveState()); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/data/cell/state/machine/GoingToSleepingState.java b/src/main/java/org/onap/a1pesimulator/data/cell/state/machine/GoingToSleepingState.java new file mode 100644 index 0000000..d4bdc48 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/cell/state/machine/GoingToSleepingState.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.cell.state.machine; + +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.state.CellStateEnum; +import org.onap.a1pesimulator.data.cell.state.CellStateMachine; + +public class GoingToSleepingState extends CellStateMachine { + + public GoingToSleepingState() { + super(CellStateEnum.GOING_TO_SLEEP); + } + + @Override + public void next(CellDetails cell) { + cell.setCellStateMachine(new SleepingState()); + } + + @Override + public void prev(CellDetails cell) { + cell.setCellStateMachine(new InactiveState()); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/data/cell/state/machine/InactiveState.java b/src/main/java/org/onap/a1pesimulator/data/cell/state/machine/InactiveState.java new file mode 100644 index 0000000..e04d5f7 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/cell/state/machine/InactiveState.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.cell.state.machine; + +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.state.CellStateEnum; +import org.onap.a1pesimulator.data.cell.state.CellStateMachine; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InactiveState extends CellStateMachine { + + private static final Logger log = LoggerFactory.getLogger(InactiveState.class); + + public InactiveState() { + super(CellStateEnum.INACTIVE); + } + + @Override + public void next(CellDetails cell) { + cell.setCellStateMachine(new ActiveState()); + } + + @Override + public void prev(CellDetails cell) { + log.info("YOU ARE IN THE INACTIVE STATE, PREVIOUS STATE ISN'T AVAILABLE"); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/data/cell/state/machine/SleepingState.java b/src/main/java/org/onap/a1pesimulator/data/cell/state/machine/SleepingState.java new file mode 100644 index 0000000..7de3230 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/cell/state/machine/SleepingState.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.cell.state.machine; + +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.state.CellStateEnum; +import org.onap.a1pesimulator.data.cell.state.CellStateMachine; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SleepingState extends CellStateMachine { + + private static final Logger log = LoggerFactory.getLogger(SleepingState.class); + + public SleepingState() { + super(CellStateEnum.SLEEPING); + } + + @Override + public void next(CellDetails cell) { + log.info("YOU ARE IN THE SLEEPING STATE, NEXT STATE ISN'T AVAILABLE"); + } + + @Override + public void prev(CellDetails cell) { + cell.setCellStateMachine(new InactiveState()); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/data/ue/RanUserEquipment.java b/src/main/java/org/onap/a1pesimulator/data/ue/RanUserEquipment.java new file mode 100644 index 0000000..a91a0d1 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/ue/RanUserEquipment.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.ue; + +import java.util.Collection; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class RanUserEquipment { + + private Collection<UserEquipment> ues; + private int itemsLength; +} diff --git a/src/main/java/org/onap/a1pesimulator/data/ue/UserEquipment.java b/src/main/java/org/onap/a1pesimulator/data/ue/UserEquipment.java new file mode 100644 index 0000000..8063527 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/ue/UserEquipment.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.ue; + +import java.util.Collection; +import lombok.Getter; +import lombok.Setter; + +@Getter +public class UserEquipment { + + private String id; + private Double latitude; + private Double longitude; + @Setter + private String cellId; + @Setter + private Collection<String> cellsInRange; + +} diff --git a/src/main/java/org/onap/a1pesimulator/data/ue/UserEquipmentNotification.java b/src/main/java/org/onap/a1pesimulator/data/ue/UserEquipmentNotification.java new file mode 100644 index 0000000..a294c26 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/ue/UserEquipmentNotification.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.ue; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class UserEquipmentNotification { + + private String id; + private String cellId; +} diff --git a/src/main/java/org/onap/a1pesimulator/data/ves/CommonEventHeader.java b/src/main/java/org/onap/a1pesimulator/data/ves/CommonEventHeader.java new file mode 100644 index 0000000..9cef221 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/ves/CommonEventHeader.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.ves; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class CommonEventHeader { + + private String eventType; + + private String version; + + private String vesEventListenerVersion; + + private String sourceId; + + private String reportingEntityName; + + private Long startEpochMicrosec; + + private String eventId; + + private Long lastEpochMicrosec; + + private String priority; + + private Integer sequence; + + private String sourceName; + + private String domain; + + private String eventName; + + private String reportingEntityId; + + private String nfcNamingCode; + + private String nfNamingCode; +} diff --git a/src/main/java/org/onap/a1pesimulator/data/ves/Event.java b/src/main/java/org/onap/a1pesimulator/data/ves/Event.java new file mode 100644 index 0000000..ded1848 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/ves/Event.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.ves; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonTypeName("event") +@JsonTypeInfo(include = As.WRAPPER_OBJECT, use = Id.NAME) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Event { + + private CommonEventHeader commonEventHeader; + private FaultFields faultFields; + + private MeasurementFields measurementFields; +} diff --git a/src/main/java/org/onap/a1pesimulator/data/ves/FaultFields.java b/src/main/java/org/onap/a1pesimulator/data/ves/FaultFields.java new file mode 100644 index 0000000..f00e31d --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/ves/FaultFields.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.ves; + +import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class FaultFields { + + private Double faultFieldsVersion; + private String eventSeverity; + private String eventSourceType; + private String eventCategory; + private String alarmCondition; + private String specificProblem; + private String vfStatus; + private String alarmInterfaceA; + private List<AdditionalInformation> alarmAdditionalInformation; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class AdditionalInformation { + + private String name; + private String value; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/data/ves/GlobalVesConfiguration.java b/src/main/java/org/onap/a1pesimulator/data/ves/GlobalVesConfiguration.java new file mode 100644 index 0000000..7532573 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/ves/GlobalVesConfiguration.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.ves; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class GlobalVesConfiguration { + + private Integer interval; + private Event event; +} diff --git a/src/main/java/org/onap/a1pesimulator/data/ves/MeasurementFields.java b/src/main/java/org/onap/a1pesimulator/data/ves/MeasurementFields.java new file mode 100644 index 0000000..86af5d6 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/ves/MeasurementFields.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.ves; + +import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.List; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class MeasurementFields { + + private Integer measurementInterval; + private String measurementFieldsVersion; + + private List<AdditionalMeasurement> additionalMeasurements; + + @Data + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class AdditionalMeasurement { + + private String name; + private Map<String, String> hashMap; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/data/ves/RanPeriodicVesEvent.java b/src/main/java/org/onap/a1pesimulator/data/ves/RanPeriodicVesEvent.java new file mode 100644 index 0000000..908f66b --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/ves/RanPeriodicVesEvent.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.ves; + +import java.util.concurrent.ScheduledFuture; +import lombok.Builder; +import lombok.Data; +import org.onap.a1pesimulator.service.ves.RanSendVesRunnable; + +@Data +@Builder +public class RanPeriodicVesEvent { + + private Event event; + private Integer interval; + private ScheduledFuture<?> scheduledFuture; + private RanSendVesRunnable sendVesRunnable; +} diff --git a/src/main/java/org/onap/a1pesimulator/data/ves/VesEventStatusNotification.java b/src/main/java/org/onap/a1pesimulator/data/ves/VesEventStatusNotification.java new file mode 100644 index 0000000..c356e4c --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/data/ves/VesEventStatusNotification.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.data.ves; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class VesEventStatusNotification { + + String identifier; + Status status; + + public enum Status { + STARTED, STOPPED; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/exception/LackOfConfigException.java b/src/main/java/org/onap/a1pesimulator/exception/LackOfConfigException.java new file mode 100644 index 0000000..f33384e --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/exception/LackOfConfigException.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.exception; + +public class LackOfConfigException extends Exception { + + public LackOfConfigException(String errorMessage) { + super(errorMessage); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/exception/VesBrokerException.java b/src/main/java/org/onap/a1pesimulator/exception/VesBrokerException.java new file mode 100644 index 0000000..2366c31 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/exception/VesBrokerException.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.exception; + +public class VesBrokerException extends Exception { + + public VesBrokerException(String errorMessage) { + super(errorMessage); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/a1/A1Service.java b/src/main/java/org/onap/a1pesimulator/service/a1/A1Service.java new file mode 100644 index 0000000..aa2c407 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/a1/A1Service.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.a1; + +import java.io.IOException; +import java.net.URISyntaxException; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestClientException; + +public interface A1Service { + + ResponseEntity<String> healthCheck() throws URISyntaxException; + + ResponseEntity<String> putPolicy(Integer policyTypeId, String policyId, String body) throws URISyntaxException; + + ResponseEntity<String> putPolicySchema(Integer policyTypeId, String body) throws URISyntaxException; + + ResponseEntity<String> deletePolicy(Integer policyTypeId, String policyId) throws URISyntaxException; + + ResponseEntity<String> getPolicyTypeIds() throws RestClientException, URISyntaxException; + + ResponseEntity<String> getPolicyType(Integer policyTypeId) throws RestClientException, URISyntaxException; + + ResponseEntity<String> getPolicyIdsOfType(Integer policyTypeId) + throws RestClientException, URISyntaxException, IOException; + + ResponseEntity<String> getPolicy(Integer policyTypeId, String policyInstanceId) + throws RestClientException, URISyntaxException; + + ResponseEntity<String> getAllPoliciesForType(Integer policyTypeId) + throws IOException, RestClientException, URISyntaxException; + +} diff --git a/src/main/java/org/onap/a1pesimulator/service/a1/OnPolicyAction.java b/src/main/java/org/onap/a1pesimulator/service/a1/OnPolicyAction.java new file mode 100644 index 0000000..821e395 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/a1/OnPolicyAction.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.a1; + +public interface OnPolicyAction { + + boolean isForMe(Integer policyTypeId, String policyId, String body); + + void onPolicy(Integer policyTypeId, String policyId, String body); +} diff --git a/src/main/java/org/onap/a1pesimulator/service/a1/PolicyInstancesHolder.java b/src/main/java/org/onap/a1pesimulator/service/a1/PolicyInstancesHolder.java new file mode 100644 index 0000000..a5e5a07 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/a1/PolicyInstancesHolder.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.a1; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.onap.a1pesimulator.util.JsonUtils; +import org.springframework.stereotype.Service; + +@Service +public class PolicyInstancesHolder { + + Map<String, String> cellPolicyMap = new HashMap<>(); + + public void addPolicy(String policyId, String body) { + cellPolicyMap.put(policyId, body); + } + + public void removePolicy(String policyId) { + cellPolicyMap.remove(policyId); + } + + public boolean containsPoliciesForCell(String cell) { + return cellPolicyMap.values().stream().map(this::getCellListFromPolicyInstance).flatMap(List::stream) + .anyMatch(c -> c.equals(cell)); + } + + private List<String> getCellListFromPolicyInstance(String policyInstance) { + RanUeHandoverOnPolicyAction.UeHandoverPolicy policy = + JsonUtils.INSTANCE.deserialize(policyInstance, RanUeHandoverOnPolicyAction.UeHandoverPolicy.class); + return policy.getResources().stream().flatMap(resources -> resources.getCellIdList().stream()) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/a1/RanA1ServiceLocalStoreImpl.java b/src/main/java/org/onap/a1pesimulator/service/a1/RanA1ServiceLocalStoreImpl.java new file mode 100644 index 0000000..feb481d --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/a1/RanA1ServiceLocalStoreImpl.java @@ -0,0 +1,116 @@ +package org.onap.a1pesimulator.service.a1; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClientException; + +/** + * A1 Service implementation which uses in-memory policy data store. + */ +@Service +public class RanA1ServiceLocalStoreImpl implements A1Service { + + private static final Logger log = LoggerFactory.getLogger(RanA1ServiceLocalStoreImpl.class); + + private Map<Integer, Map<String, String>> policyTypesMap = new HashMap<>(); + private Map<Integer, String> policySchemaMap = new HashMap<>(); + private ObjectMapper mapper; + + public RanA1ServiceLocalStoreImpl(ObjectMapper mapper) { + this.mapper = mapper; + } + + @Override + public ResponseEntity<String> healthCheck() throws RestClientException { + return ResponseEntity.ok().build(); + } + + @Override + public ResponseEntity<String> putPolicySchema(Integer policyTypeId, String body) { + policySchemaMap.put(policyTypeId, body); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Override + public ResponseEntity<String> putPolicy(final Integer policyTypeId, final String policyId, final String body) { + log.debug("Create or update policy id {} of policy type id {} with following content {} ", policyId, + policyTypeId, body); + if (policyTypesMap.containsKey(policyTypeId)) { + policyTypesMap.get(policyTypeId).put(policyId, body); + } else { + Map<String, String> policies = new HashMap<>(); + policies.put(policyId, body); + policyTypesMap.put(policyTypeId, policies); + } + return ResponseEntity.accepted().build(); + } + + @Override + public ResponseEntity<String> deletePolicy(final Integer policyTypeId, final String policyId) { + log.debug("Delete policy id {} of policy type id {}", policyId, policyTypeId); + if (policyTypesMap.containsKey(policyTypeId)) { + policyTypesMap.get(policyTypeId).remove(policyId); + return ResponseEntity.accepted().build(); + } else { + return ResponseEntity.notFound().build(); + } + } + + @Override + public ResponseEntity<String> getPolicyTypeIds() throws RestClientException { + return getRestAsString(policySchemaMap.keySet()); + } + + @Override + public ResponseEntity<String> getPolicyType(final Integer policyTypeId) throws RestClientException { + if (policySchemaMap.isEmpty() || !policySchemaMap.containsKey(policyTypeId)) { + return ResponseEntity.notFound().build(); + } else { + return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(policySchemaMap.get(policyTypeId)); + } + } + + @Override + public ResponseEntity<String> getPolicyIdsOfType(final Integer policyTypeId) throws RestClientException { + Set<String> result = new HashSet<>(); + if (policyTypesMap.containsKey(policyTypeId)) { + result = policyTypesMap.get(policyTypeId).keySet(); + } + return getRestAsString(result); + } + + @Override + public ResponseEntity<String> getPolicy(final Integer policyTypeId, final String policyId) + throws RestClientException { + if (policyTypesMap.containsKey(policyTypeId) && policyTypesMap.get(policyTypeId).containsKey(policyId)) { + return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON) + .body(policyTypesMap.get(policyTypeId).get(policyId)); + } else { + return ResponseEntity.notFound().build(); + } + } + + @Override + public ResponseEntity<String> getAllPoliciesForType(final Integer policyTypeId) throws RestClientException { + return getRestAsString(policyTypesMap.get(policyTypeId)); + } + + private ResponseEntity<String> getRestAsString(Object obj) throws RestClientException { + try { + return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(mapper.writeValueAsString(obj)); + } catch (JsonProcessingException e) { + throw new RuntimeException("Cannot serialize object", e); + } + } + +} diff --git a/src/main/java/org/onap/a1pesimulator/service/a1/RanUeHandoverOnPolicyAction.java b/src/main/java/org/onap/a1pesimulator/service/a1/RanUeHandoverOnPolicyAction.java new file mode 100644 index 0000000..fdbbd97 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/a1/RanUeHandoverOnPolicyAction.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.a1; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import lombok.Getter; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.data.ue.UserEquipmentNotification; +import org.onap.a1pesimulator.service.cell.RanCellService; +import org.onap.a1pesimulator.service.ue.RanUeService; +import org.onap.a1pesimulator.util.JsonUtils; +import org.onap.a1pesimulator.util.JsonUtils.JsonUtilsException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Service; + +@Service +public class RanUeHandoverOnPolicyAction implements OnPolicyAction { + + private static final String TOPIC_UE = "/topic/userEquipment"; + private static final String POLICY_EXAMPLE = + "{ \"scope\": { \"ueId\": \"emergency_samsung_s10_01\" }, \"resources\": [ { \"cellIdList\": [ \"Cell1\" ], \"preference\": \"AVOID\" } ] }"; + private static final Logger log = LoggerFactory.getLogger(RanUeHandoverOnPolicyAction.class); + + private final RanUeService ranUeService; + private final SimpMessagingTemplate messagingTemplate; + private final RanCellService ranCellService; + private final PolicyInstancesHolder policyHolder; + + public RanUeHandoverOnPolicyAction(SimpMessagingTemplate messagingTemplate, RanUeService ranUeService, + RanCellService ranCellService, PolicyInstancesHolder policyHolder) { + this.messagingTemplate = messagingTemplate; + this.ranUeService = ranUeService; + this.ranCellService = ranCellService; + this.policyHolder = policyHolder; + } + + @Override + public boolean isForMe(Integer policyTypeId, String policyId, String body) { + try { + JsonUtils.INSTANCE.deserialize(body, UeHandoverPolicy.class); + return true; + } catch (JsonUtilsException ex) { + log.info( + "Policy {} is not for me because policy body doesn't comply with Ue Handover policy. Follow example: {}", + policyId, POLICY_EXAMPLE); + return false; + } + } + + @Override + public void onPolicy(Integer policyTypeId, String policyId, String body) { + UeHandoverPolicy policy = JsonUtils.INSTANCE.deserialize(body, UeHandoverPolicy.class); + String ueId = policy.getScope().getUeId(); + List<String> cellId = policy.getResources().stream().flatMap(resources -> resources.getCellIdList().stream()) + .collect(Collectors.toList()); + + if (ueId == null || cellId.isEmpty()) { + log.warn("Cannot handover because {} is not provided in preload! Follow example: {}", + ueId == null ? "ueId" : "cellId", POLICY_EXAMPLE); + return; + } + + Optional<String> activeCellId = getActiveCellForUE(ueId); + + if (!activeCellId.isPresent()) { + log.warn("Cannot handover ue {} because there is no active cell in range", ueId); + return; + } + + ranUeService.handover(ueId, activeCellId.get()); + messagingTemplate.convertAndSend(TOPIC_UE, new UserEquipmentNotification(ueId, activeCellId.get())); + } + + private Optional<String> getActiveCellForUE(String ue) { + Optional<UserEquipment> equipment = ranUeService.getUserEquipment(ue); + if (!equipment.isPresent()) { + log.warn("Cannot handover because is not ue with id: {}", ue); + return Optional.empty(); + } + + return ranCellService.getAllCellsWithStatus().stream().filter(c -> !c.isFailureMode()) + .map(cellWithStatus -> cellWithStatus.getCell().getIdentifier()) + .filter(cell -> ranUeService.canHandover(ue, cell)) + .filter(cell -> !policyHolder.containsPoliciesForCell(cell)).findFirst(); + } + + @Getter + public static class UeHandoverPolicy { + + private Scope scope; + private List<Resources> resources; + } + + @Getter + public static class Scope { + + private String ueId; + } + + @Getter + public static class Resources { + + private List<String> cellIdList; + private Preference preference; + } + + public enum Preference { + SHALL("SHALL"), PREFER("PREFER"), AVOID("AVOID"), FORBID("FORBID"); + public final String value; + + Preference(String stateName) { + this.value = stateName; + } + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/a1/SetLowRangeValuesOnPolicyAction.java b/src/main/java/org/onap/a1pesimulator/service/a1/SetLowRangeValuesOnPolicyAction.java new file mode 100644 index 0000000..e608aa5 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/a1/SetLowRangeValuesOnPolicyAction.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.a1; + +import java.util.List; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.MeasurementFields.AdditionalMeasurement; +import org.onap.a1pesimulator.data.ves.RanPeriodicVesEvent; +import org.onap.a1pesimulator.service.ves.RanVesBrokerService; +import org.onap.a1pesimulator.util.JsonUtils; +import org.onap.a1pesimulator.util.RanVesUtils; +import org.springframework.stereotype.Service; + +@Service +public class SetLowRangeValuesOnPolicyAction implements OnPolicyAction { + + private final RanVesBrokerService vesBrokerService; + + public SetLowRangeValuesOnPolicyAction(RanVesBrokerService vesBrokerService) { + this.vesBrokerService = vesBrokerService; + } + + @Override + public boolean isForMe(Integer policyTypeId, String policyId, String body) { + // disabling for now + return false; + } + + @Override + public void onPolicy(Integer policyTypeId, String policyId, String body) { + vesBrokerService.getPeriodicEventsCache().values().forEach(this::updateEvent); + } + + private void updateEvent(RanPeriodicVesEvent periodicEvent) { + List<AdditionalMeasurement> lowRangeValues = RanVesUtils.setLowRangeValues( + periodicEvent.getEvent().getMeasurementFields().getAdditionalMeasurements()); + Event clonedEvent = JsonUtils.INSTANCE.clone(periodicEvent.getEvent()); + clonedEvent.getMeasurementFields().setAdditionalMeasurements(lowRangeValues); + periodicEvent.getSendVesRunnable().updateEvent(clonedEvent); + } + +} diff --git a/src/main/java/org/onap/a1pesimulator/service/cell/RanCellService.java b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellService.java new file mode 100644 index 0000000..a12ed2c --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellService.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.cell; + +import java.util.Collection; +import java.util.Set; +import org.onap.a1pesimulator.data.Topology; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.CellWithStatus; +import org.onap.a1pesimulator.data.cell.RanCell; + +public interface RanCellService { + + Topology getTopology(); + + Set<String> getCellIds(); + + CellDetails getCellById(String id); + + RanCell getCells(); + + void failure(String id); + + void recoverFromFailure(String id); + + Collection<CellWithStatus> getAllCellsWithStatus(); +}
\ No newline at end of file diff --git a/src/main/java/org/onap/a1pesimulator/service/cell/RanCellServiceImpl.java b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellServiceImpl.java new file mode 100644 index 0000000..0c0ab00 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellServiceImpl.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.cell; + +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; +import org.onap.a1pesimulator.data.Topology; +import org.onap.a1pesimulator.data.cell.Cell; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.CellWithStatus; +import org.onap.a1pesimulator.data.cell.RanCell; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.service.ue.RanUeHolder; +import org.onap.a1pesimulator.service.ves.RanVesHolder; +import org.springframework.stereotype.Service; + +@Service +public class RanCellServiceImpl implements RanCellService { + + private final RanCellsHolder ranCellsHolder; + private final RanUeHolder ueHolder; + private final RanVesHolder vesHolder; + + public RanCellServiceImpl(RanCellsHolder ranCellsHolder, RanUeHolder ueHolder, RanVesHolder vesHolder) { + this.ranCellsHolder = ranCellsHolder; + this.ueHolder = ueHolder; + this.vesHolder = vesHolder; + } + + @Override + public Set<String> getCellIds() { + return ranCellsHolder.getCellIds(); + } + + @Override + public CellDetails getCellById(String id) { + CellDetails cellDetails = ranCellsHolder.getCellById(id); + cellDetails.setConnectedUserEquipments(getConnectedUserEquipments(cellDetails.getId())); + return cellDetails; + } + + @Override + public RanCell getCells() { + Collection<CellDetails> cellDetails = ranCellsHolder.getAllCells(); + cellDetails.forEach(cell -> cell.setConnectedUserEquipments(getConnectedUserEquipments(cell.getId()))); + return new RanCell(cellDetails, cellDetails.size()); + } + + @Override + public Topology getTopology() { + Collection<CellDetails> cellList = ranCellsHolder.getCellDetailsList(); + cellList.forEach(cell -> cell.setConnectedUserEquipments(getConnectedUserEquipments(cell.getId()))); + return Topology.builder().cells(cellList).userEquipments(ueHolder.getUserEquipments()).build(); + } + + private Set<String> getConnectedUserEquipments(String cellId) { + Collection<UserEquipment> cellUes = ueHolder.getUserEquipmentsConnectedToCell(cellId); + return cellUes.stream().map(UserEquipment::getId).collect(Collectors.toSet()); + } + + @Override + public void failure(String id) { + ranCellsHolder.markCellInFailure(id); + } + + @Override + public void recoverFromFailure(String id) { + ranCellsHolder.unmarkCellInFailure(id); + } + + @Override + public Collection<CellWithStatus> getAllCellsWithStatus() { + return ranCellsHolder.getAllCells().stream().map(this::wrapCellWithStatus).collect(Collectors.toList()); + } + + private CellWithStatus wrapCellWithStatus(CellDetails cell) { + Cell c = Cell.builder().identifier(cell.getId()).build(); + return CellWithStatus.builder().cell(c).failureMode(ranCellsHolder.isInFailureMode(cell.getId())) + .vesEnabled(vesHolder.isEventEnabled(cell.getId())).state(cell.getCurrentState()).build(); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/cell/RanCellStateService.java b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellStateService.java new file mode 100644 index 0000000..d69629e --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellStateService.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.cell; + +import java.util.Optional; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.state.CellStateEnum; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Service; + +@Service +public class RanCellStateService { + + private static final Logger log = LoggerFactory.getLogger(RanCellStateService.class); + + private final RanCellsHolder cellsHolder; + private final SimpMessagingTemplate messagingTemplate; + + public static final String TOPIC_CELL = "/topic/cellStatus"; + + public RanCellStateService(RanCellsHolder cellsHolder, SimpMessagingTemplate messagingTemplate) { + this.cellsHolder = cellsHolder; + this.messagingTemplate = messagingTemplate; + } + + public void activateState(String identifier) { + Optional<CellDetails> cellDetails = getCell(identifier); + if (cellExist(cellDetails, identifier, "Activate")) { + boolean changed = nextStateIfPossible(cellDetails.get(), CellStateEnum.INACTIVE); + if (changed) { + sendCellNotification(cellDetails.get()); + } + } + } + + public void failingState(String identifier) { + Optional<CellDetails> cellDetails = getCell(identifier); + if (cellExist(cellDetails, identifier, "Failing")) { + boolean changed = nextStateIfPossible(cellDetails.get(), CellStateEnum.ACTIVE); + if (changed) { + sendCellNotification(cellDetails.get()); + } + } + } + + public void stopState(String identifier) { + Optional<CellDetails> cellDetails = getCell(identifier); + if (cellExist(cellDetails, identifier, "Stop")) { + boolean changed = previousStateIfPossible(cellDetails.get()); + if (changed) { + sendCellNotification(cellDetails.get()); + } + } + } + + private boolean cellExist(Optional<CellDetails> cellDetails, String identifier, String actionName) { + if (cellDetails.isEmpty()) { + log.info("Cell not found for {} identifier! '{}' action won't be executed!", identifier, actionName); + return false; + } + + return true; + } + + private boolean previousStateIfPossible(CellDetails cell) { + + CellStateEnum state = cell.getCellStateMachine().getState(); + if (state == CellStateEnum.SLEEPING || state == CellStateEnum.GOING_TO_SLEEP || state == CellStateEnum.ACTIVE) { + cell.previousState(); + } else { + log.info("Cell {} is in {} state! The changing of the state isn't allowed." + + "Supported states are: GOING_TO_SLEEP, SLEEPING, ACTIVE.", cell.getId(), + cell.getCellStateMachine().getState().value); + return false; + } + + return true; + } + + private boolean nextStateIfPossible(CellDetails cellDetails, CellStateEnum shouldBe) { + + if (cellDetails.getCellStateMachine().getState() == shouldBe) { + cellDetails.nextState(); + } else { + log.info( + "Cell {} is in {} state. The changing of the state isn't allowed. " + "The supported state is: {}!", + cellDetails.getId(), cellDetails.getCellStateMachine().getState().value, shouldBe.value); + return false; + } + + return true; + } + + private Optional<CellDetails> getCell(String identifier) { + CellDetails cell = null; + try { + cell = cellsHolder.getCellById(identifier); + } catch (RuntimeException e) { + log.info("Exception was thrown: ", e); + } + + if (cell == null) { + return Optional.empty(); + } + + return Optional.of(cell); + } + + private void sendCellNotification(CellDetails cellDetails) { + messagingTemplate.convertAndSend(TOPIC_CELL, cellDetails); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/cell/RanCellsHolder.java b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellsHolder.java new file mode 100644 index 0000000..86fa32c --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellsHolder.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.cell; + +import java.text.MessageFormat; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.function.BinaryOperator; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.CellList.Cell; +import org.onap.a1pesimulator.data.cell.CellList.CellData; +import org.onap.a1pesimulator.util.TopologyReader; +import org.springframework.stereotype.Service; + +@Service +public class RanCellsHolder { + + private Map<String, CellDetails> cellDetailsById; + private final Collection<CellInFailureMode> cellsInFailureMode = new HashSet<>(); + + private final TopologyReader topologyReader; + + public RanCellsHolder(TopologyReader topologyReader) { + this.topologyReader = topologyReader; + refresh(); + } + + public Set<String> getCellIds() { + return cellDetailsById.keySet(); + } + + public CellDetails getCellById(String id) { + if (!cellDetailsById.containsKey(id)) { + throw new RuntimeException(MessageFormat.format("Cell not found: {0}", id)); + } + return cellDetailsById.get(id); + } + + public Collection<CellDetails> getCellDetailsList() { + return cellDetailsById.values(); + } + + public Collection<CellDetails> getAllCells() { + return cellDetailsById.values(); + } + + public void markCellInFailure(String id) { + cellsInFailureMode.add(CellInFailureMode.builder().id(id).build()); + } + + public boolean isInFailureMode(String id) { + return cellsInFailureMode.stream().anyMatch(byIdPredicate(id)); + } + + public void unmarkCellInFailure(String id) { + cellsInFailureMode.removeIf(byIdPredicate(id)); + } + + public Optional<CellInFailureMode> getCellsInFailureMode(String id) { + return cellsInFailureMode.stream().filter(byIdPredicate(id)).findFirst(); + } + + @Getter + @Builder + public static class CellInFailureMode { + + private final String id; + @Setter + private Long sleepingModeDetectedTime; + } + + public void refresh() { + List<CellData> cellDatas = topologyReader.loadCellTopology().getCellList(); + cellDetailsById = cellDatas.stream().collect(Collectors.toMap(cellData -> cellData.getCell().getNodeId(), + this::toCellDetails, throwingMerger(), TreeMap::new)); + } + + public boolean hasChanged() { + return topologyReader.topologyCellHasChanged(); + } + + private <T> BinaryOperator<T> throwingMerger() { + return (u, v) -> { + throw new IllegalStateException(String.format("Duplicate key %s", u)); + }; + } + + private CellDetails toCellDetails(CellData data) { + Cell cell = data.getCell(); + return CellDetails.builder().id(cell.getNodeId()).latitude(cell.getLatitude()).longitude(cell.getLongitude()) + .build(); + } + + public static Predicate<CellInFailureMode> byIdPredicate(String id) { + return cell -> cell.getId().equals(id); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/distance/DistanceService.java b/src/main/java/org/onap/a1pesimulator/service/distance/DistanceService.java new file mode 100644 index 0000000..18462e5 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/distance/DistanceService.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.distance; + +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.util.DistanceCalculator; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +public class DistanceService { + + private Double cellRange; + + public DistanceService(@Value("${topology.cell.range}") Double cellRange) { + this.cellRange = cellRange; + } + + public boolean isInRange(CellDetails cell, UserEquipment ue) { + return DistanceCalculator + .isInRange(cell.getLatitude(), cell.getLongitude(), ue.getLatitude(), ue.getLongitude(), + cellRange); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ue/RanUeHolder.java b/src/main/java/org/onap/a1pesimulator/service/ue/RanUeHolder.java new file mode 100644 index 0000000..d31135d --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ue/RanUeHolder.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ue; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.service.cell.RanCellsHolder; +import org.onap.a1pesimulator.service.distance.DistanceService; +import org.onap.a1pesimulator.util.TopologyReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +@Service +public class RanUeHolder { + + private static final Logger log = LoggerFactory.getLogger(RanUeHolder.class); + + private Map<String, UserEquipment> userEquipmentsById; + + private final TopologyReader topologyReader; + private final DistanceService distanceService; + + private final RanCellsHolder ranCellsHolder; + + public RanUeHolder(TopologyReader topologyReader, DistanceService distanceService, RanCellsHolder ranCellsHolder) { + this.topologyReader = topologyReader; + this.distanceService = distanceService; + this.ranCellsHolder = ranCellsHolder; + refresh(); + } + + public Collection<UserEquipment> getUserEquipments() { + return userEquipmentsById.values(); + } + + public Collection<UserEquipment> getUserEquipmentsConnectedToCell(String cellId) { + return userEquipmentsById.values().stream().filter(ue -> cellId.equalsIgnoreCase(ue.getCellId())) + .collect(Collectors.toList()); + } + + public Optional<UserEquipment> getUserEquipment(String id) { + return userEquipmentsById.values().stream().filter(ue -> id.equalsIgnoreCase(ue.getId())).findAny(); + } + + public void refresh() { + Collection<UserEquipment> ues = topologyReader.loadUeTopology(); + userEquipmentsById = ues.stream().filter(this::validate) + .collect(Collectors.toMap(UserEquipment::getId, Function.identity())); + } + + public boolean hasChanged() { + return topologyReader.topologyUeHasChanged(); + } + + private boolean validate(UserEquipment ue) { + CellDetails cell = ranCellsHolder.getCellById(ue.getCellId()); + boolean inRange = distanceService.isInRange(cell, ue); + if (!inRange) { + log.warn("UE {} is not in range of preferred cell {}", ue.getId(), cell.getId()); + } + return true; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ue/RanUeService.java b/src/main/java/org/onap/a1pesimulator/service/ue/RanUeService.java new file mode 100644 index 0000000..556ed23 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ue/RanUeService.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ue; + +import java.util.Collection; +import java.util.Optional; +import org.onap.a1pesimulator.data.ue.RanUserEquipment; +import org.onap.a1pesimulator.data.ue.UserEquipment; + +public interface RanUeService { + + Collection<UserEquipment> getUserEquipments(); + + Collection<UserEquipment> getUserEquipmentsConnectedToCell(String cellId); + + Optional<UserEquipment> getUserEquipment(String id); + + RanUserEquipment getUes(); + + void handover(String ueId, String cellId); + + boolean canHandover(String ueId, String cellId); +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ue/RanUeServiceImpl.java b/src/main/java/org/onap/a1pesimulator/service/ue/RanUeServiceImpl.java new file mode 100644 index 0000000..da6f5bc --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ue/RanUeServiceImpl.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ue; + +import java.util.Collection; +import java.util.Optional; +import java.util.stream.Collectors; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.ue.RanUserEquipment; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.service.cell.RanCellsHolder; +import org.onap.a1pesimulator.service.distance.DistanceService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +@Service +public class RanUeServiceImpl implements RanUeService { + + private static final Logger log = LoggerFactory.getLogger(RanUeServiceImpl.class); + + private final RanUeHolder ueHolder; + private final RanCellsHolder ranCellsHolder; + private final DistanceService distanceService; + + public RanUeServiceImpl(RanUeHolder ueHolder, RanCellsHolder ranCellsHolder, DistanceService distanceService) { + this.ueHolder = ueHolder; + this.ranCellsHolder = ranCellsHolder; + this.distanceService = distanceService; + } + + @Override + public Collection<UserEquipment> getUserEquipments() { + return ueHolder.getUserEquipments(); + } + + @Override + public RanUserEquipment getUes() { + Collection<UserEquipment> uesCollection = ueHolder.getUserEquipments(); + return new RanUserEquipment(uesCollection, uesCollection.size()); + } + + @Override + public Collection<UserEquipment> getUserEquipmentsConnectedToCell(String cellId) { + return ueHolder.getUserEquipmentsConnectedToCell(cellId); + } + + @Override + public Optional<UserEquipment> getUserEquipment(String id) { + Optional<UserEquipment> userEquipment = ueHolder.getUserEquipment(id); + userEquipment.ifPresent(ue -> ue.setCellsInRange(getCellsIdsInRange(ue))); + return userEquipment; + } + + @Override + public void handover(String ueId, String cellId) { + Optional<UserEquipment> userEquipment = getUserEquipment(ueId); + if (!userEquipment.isPresent()) { + log.warn("Cannot handover ue {} to cell {}, because ue does not exist!", ueId, cellId); + return; + } + userEquipment.get().setCellId(cellId); + } + + @Override + public boolean canHandover(String ueId, String cellId) { + Optional<UserEquipment> userEquipment = getUserEquipment(ueId); + return userEquipment.map(equipment -> equipment.getCellsInRange().stream().anyMatch(cellId::equalsIgnoreCase)) + .orElse(false); + } + + private Collection<String> getCellsIdsInRange(UserEquipment ue) { + return ranCellsHolder.getAllCells().stream().filter(cell -> distanceService.isInRange(cell, ue)) + .map(CellDetails::getId).collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/OnEventAction.java b/src/main/java/org/onap/a1pesimulator/service/ves/OnEventAction.java new file mode 100644 index 0000000..b34594a --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/OnEventAction.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import org.onap.a1pesimulator.data.ves.Event; + +@FunctionalInterface +public interface OnEventAction { + + void onEvent(Event event); +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanCellEventCustomizer.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanCellEventCustomizer.java new file mode 100644 index 0000000..9922329 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanCellEventCustomizer.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.util.List; +import java.util.Optional; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.MeasurementFields.AdditionalMeasurement; +import org.onap.a1pesimulator.service.ue.RanUeHolder; +import org.onap.a1pesimulator.service.ves.RanSendVesRunnable.EventCustomizer; +import org.onap.a1pesimulator.util.Constants; +import org.onap.a1pesimulator.util.JsonUtils; +import org.onap.a1pesimulator.util.RanVesUtils; +import org.springframework.stereotype.Service; + +@Service +public class RanCellEventCustomizer implements EventCustomizer { + + private static final String UE_PARAM_TRAFFIC_MODEL_RANGE = "[[20-50]]"; + private final RanUeHolder ranUeHolder; + + public RanCellEventCustomizer(RanUeHolder ueHolder) { + this.ranUeHolder = ueHolder; + } + + @Override + public Event apply(Event t) { + Event event = JsonUtils.INSTANCE.clone(t); + return customizeEvent(event); + } + + private Event customizeEvent(Event event) { + RanVesUtils.updateHeader(event); + enrichWithUeData(event); + randomizeEvent(event); + return event; + } + + private void randomizeEvent(Event event) { + List<AdditionalMeasurement> additionalMeasurementsToRandomize = + event.getMeasurementFields().getAdditionalMeasurements(); + event.getMeasurementFields().setAdditionalMeasurements( + RanVesUtils.randomizeAdditionalMeasurements(additionalMeasurementsToRandomize)); + } + + private void enrichWithUeData(Event event) { + + Optional<AdditionalMeasurement> identity = event.getMeasurementFields().getAdditionalMeasurements().stream() + .filter(msrmnt -> Constants.MEASUREMENT_FIELD_IDENTIFIER + .equalsIgnoreCase( + msrmnt.getName())) + .findAny(); + identity.ifPresent(m -> addTrafficModelMeasurement(event, m)); + } + + private void addTrafficModelMeasurement(Event event, AdditionalMeasurement identity) { + AdditionalMeasurement trafficModelMeasurement = + RanVesUtils.buildTrafficModelMeasurement(identity, ranUeHolder, UE_PARAM_TRAFFIC_MODEL_RANGE); + event.getMeasurementFields().getAdditionalMeasurements().add(trafficModelMeasurement); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanCellFailureEventCustomizer.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanCellFailureEventCustomizer.java new file mode 100644 index 0000000..ac2c4fc --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanCellFailureEventCustomizer.java @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.text.MessageFormat; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.MeasurementFields.AdditionalMeasurement; +import org.onap.a1pesimulator.service.ue.RanUeHolder; +import org.onap.a1pesimulator.service.ves.RanSendVesRunnable.EventCustomizer; +import org.onap.a1pesimulator.util.Constants; +import org.onap.a1pesimulator.util.JsonUtils; +import org.onap.a1pesimulator.util.RanVesUtils; + +public class RanCellFailureEventCustomizer implements EventCustomizer { + + private static final String UE_PARAM_TRAFFIC_MODEL_RANGE = "[[50->10]]"; + private final RanUeHolder ranUeHolder; + private final Event event; + + private final Map<Key, Value> additionalMeasurementsValues = new HashMap<>(); + private final ValueFactory valueFactory; + + public RanCellFailureEventCustomizer(Event event, RanUeHolder ranUeHolder) { + this.ranUeHolder = ranUeHolder; + this.event = event; + valueFactory = new ValueFactory(); + collectAdditionalMeasurementValues(event); + } + + @Override + public Event apply(Event t) { + return customizeEvent(JsonUtils.INSTANCE.clone(this.event)); + } + + private void collectAdditionalMeasurementValues(Event event) { + Collection<AdditionalMeasurement> additionalMeasurementsToResolve = + event.getMeasurementFields().getAdditionalMeasurements(); + additionalMeasurementsToResolve.forEach(this::collectAdditionalMeasurementValue); + } + + private void collectAdditionalMeasurementValue(AdditionalMeasurement m) { + for (Entry<String, String> entry : m.getHashMap().entrySet()) { + if (!RanVesUtils.isRange(entry.getValue())) { + continue; + } + additionalMeasurementsValues + .putIfAbsent(new Key(m.getName(), entry.getKey()), valueFactory.getInstance(entry.getValue())); + } + } + + private Event customizeEvent(Event event) { + RanVesUtils.updateHeader(event); + enrichWithUeData(event); + resolveRanges(event); + return event; + } + + private void resolveRanges(Event event) { + List<AdditionalMeasurement> additionalMeasurementsToResolve = + event.getMeasurementFields().getAdditionalMeasurements(); + + additionalMeasurementsToResolve.forEach(this::resolveRanges); + event.getMeasurementFields().setAdditionalMeasurements(additionalMeasurementsToResolve); + } + + private void resolveRanges(AdditionalMeasurement m) { + for (Entry<String, String> entry : m.getHashMap().entrySet()) { + Key key = new Key(m.getName(), entry.getKey()); + if (!additionalMeasurementsValues.containsKey(key)) { + continue; + } + Value value = additionalMeasurementsValues.get(key); + value.current = value.calculateCurrentValue(); + entry.setValue(value.current.toString()); + } + } + + private void enrichWithUeData(Event event) { + + Optional<AdditionalMeasurement> identity = event.getMeasurementFields().getAdditionalMeasurements().stream() + .filter(msrmnt -> Constants.MEASUREMENT_FIELD_IDENTIFIER + .equalsIgnoreCase( + msrmnt.getName())) + .findAny(); + identity.ifPresent(m -> addTrafficModelMeasurement(event, m)); + } + + private void addTrafficModelMeasurement(Event event, AdditionalMeasurement identity) { + AdditionalMeasurement trafficModelMeasurement = + RanVesUtils.buildTrafficModelMeasurement(identity, ranUeHolder, UE_PARAM_TRAFFIC_MODEL_RANGE); + event.getMeasurementFields().getAdditionalMeasurements().add(trafficModelMeasurement); + + collectAdditionalMeasurementValue(trafficModelMeasurement); + } + + // -----------helper classes + + private static class ValueFactory { + + public Value getInstance(String value) { + String[] split; + if (RanVesUtils.isRandomRange(value)) { + split = RanVesUtils.splitRandomRange(value); + return new RandomValue(Integer.valueOf(split[0]), Integer.valueOf(split[1])); + } + if (RanVesUtils.isTrandingRange(value)) { + split = RanVesUtils.splitTrendingRange(value); + Integer start = Integer.valueOf(split[0]); + Integer end = Integer.valueOf(split[1]); + if (start < end) { + return new RaisingValue(start, end); + } else if (start > end) { + return new DecreasingValue(start, end); + } + } + throw new RuntimeException(MessageFormat.format("Cannot instantiate Value from string: {0}", value)); + } + } + + private abstract static class Value { + + protected Integer start; + protected Integer end; + protected Integer current; + + public Value(Integer start, Integer end) { + this.start = start; + this.end = end; + } + + public abstract Integer calculateCurrentValue(); + } + + private static class RaisingValue extends Value { + + private int increment; + + public RaisingValue(Integer start, Integer end) { + super(start, end); + } + + @Override + public Integer calculateCurrentValue() { + if (current == null) { + return start; + } + if (increment == 0) { + increment = 1; + } else { + increment = increment * 2; + } + Integer result = start + increment; + if (result > end) { + increment = 1; + return end; + } + return result; + } + } + + private static class DecreasingValue extends Value { + + private int decrement; + + public DecreasingValue(Integer start, Integer end) { + super(start, end); + } + + @Override + public Integer calculateCurrentValue() { + if (current == null) { + return start; + } + if (decrement == 0) { + decrement = 1; + } else { + decrement = decrement * 2; + } + Integer result = start - decrement; + if (result < end) { + return end; + } + return result; + } + } + + private static class RandomValue extends Value { + + public RandomValue(Integer start, Integer end) { + super(start, end); + } + + @Override + public Integer calculateCurrentValue() { + return RanVesUtils.getRandomNumber(start, end); + } + } + + @AllArgsConstructor + @EqualsAndHashCode + private static class Key { + + private String paramName; + private String mapKey; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanCheckCellIsDeadOnEvent.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanCheckCellIsDeadOnEvent.java new file mode 100644 index 0000000..1330e04 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanCheckCellIsDeadOnEvent.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import static org.onap.a1pesimulator.service.cell.RanCellStateService.TOPIC_CELL; + +import java.util.Optional; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.state.CellStateEnum; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.MeasurementFields; +import org.onap.a1pesimulator.service.cell.RanCellsHolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Service; + +@Service +public class RanCheckCellIsDeadOnEvent implements OnEventAction { + + private static final Logger log = LoggerFactory.getLogger(RanCheckCellIsDeadOnEvent.class); + + private final RanCellsHolder ranCellsHolder; + private final SimpMessagingTemplate messagingTemplate; + + private final Integer failingModeThroughputValue; + private final Integer failingModeLatencyValue; + private final Integer failingCheckoutDelayTimeInSec; + + private static final int TO_MICRO_SEC = 1_000_000; + + public RanCheckCellIsDeadOnEvent(RanCellsHolder ranCellsHolder, SimpMessagingTemplate messagingTemplate, + @Value("${ves.failing.throughput}") Integer failingModeThroughputValue, + @Value("${ves.failing.latency}") Integer failingModeLatencyValue, + @Value("${ves.failing.checkout.delay}") Integer failingCheckoutDelayTimeInSec) { + this.ranCellsHolder = ranCellsHolder; + this.messagingTemplate = messagingTemplate; + + this.failingModeThroughputValue = failingModeThroughputValue; + this.failingModeLatencyValue = failingModeLatencyValue; + this.failingCheckoutDelayTimeInSec = failingCheckoutDelayTimeInSec; + } + + @Override + public void onEvent(Event event) { + Optional<String> cellId = getCellIdentifier(event); + Optional<String> throughput = getCellThroughput(event); + Optional<String> latency = getCellLatency(event); + + if (cellId.isPresent() && throughput.isPresent() && latency.isPresent()) { + checkCell(cellId.get(), Integer.parseInt(throughput.get()), Integer.parseInt(latency.get()), + event.getCommonEventHeader().getLastEpochMicrosec()); + } + } + + private void checkCell(String cellId, Integer throughput, Integer latency, Long lastEpochMicrosec) { + if (throughput <= failingModeThroughputValue && latency >= failingModeLatencyValue) { + log.info("Failure mode detected for cell {}", cellId); + processSleepingMode(cellId, lastEpochMicrosec); + } + } + + private void processSleepingMode(String cellId, Long lastEpochMicrosec) { + CellDetails cell = ranCellsHolder.getCellById(cellId); + if (cell.getCellStateMachine().getState() == CellStateEnum.GOING_TO_SLEEP) { + Optional<RanCellsHolder.CellInFailureMode> cellInFailureModeOpt = + ranCellsHolder.getCellsInFailureMode(cellId); + if (cellInFailureModeOpt.isPresent()) { + RanCellsHolder.CellInFailureMode cellInFailureMode = cellInFailureModeOpt.get(); + if (cellInFailureMode.getSleepingModeDetectedTime() == null) { + cellInFailureMode.setSleepingModeDetectedTime(lastEpochMicrosec); + } else { + long waitingEpochMicrosec = addDelayTime(cellInFailureMode.getSleepingModeDetectedTime()); + if (lastEpochMicrosec >= waitingEpochMicrosec) { + log.info("Cell {} is sleeping!", cellId); + cell.nextState(); + messagingTemplate.convertAndSend(TOPIC_CELL, cell); + } + } + } + } + } + + private Optional<String> getCellIdentifier(Event event) { + return getValueFromAdditionalMeasurement(event, "identifier"); + } + + private Optional<String> getCellThroughput(Event event) { + return getValueFromAdditionalMeasurement(event, "throughput"); + } + + private Optional<String> getCellLatency(Event event) { + return getValueFromAdditionalMeasurement(event, "latency"); + } + + private Optional<String> getValueFromAdditionalMeasurement(Event event, String key) { + Optional<MeasurementFields.AdditionalMeasurement> measurement = getAdditionalMeasurement(event, key); + return measurement.map(this::getValueFromAdditionalMeasurement); + } + + private String getValueFromAdditionalMeasurement(MeasurementFields.AdditionalMeasurement measurement) { + return measurement.getHashMap().get("value"); + } + + private Optional<MeasurementFields.AdditionalMeasurement> getAdditionalMeasurement(Event event, + String additionalMeasurement) { + return event.getMeasurementFields().getAdditionalMeasurements().stream() + .filter(e -> e.getName().equals(additionalMeasurement)).findFirst(); + } + + private long addDelayTime(long epoch) { + return epoch + failingCheckoutDelayTimeInSec * TO_MICRO_SEC; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanEventCustomizerFactory.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanEventCustomizerFactory.java new file mode 100644 index 0000000..3fbeda9 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanEventCustomizerFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.text.MessageFormat; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.service.ue.RanUeHolder; +import org.onap.a1pesimulator.service.ves.RanSendVesRunnable.EventCustomizer; +import org.springframework.stereotype.Component; + +@Component +public class RanEventCustomizerFactory { + + private final EventCustomizer regularEventCustomizer; + private final RanUeHolder ranUeHolder; + + public RanEventCustomizerFactory(EventCustomizer regularEventCustomizer, RanUeHolder ranUeHolder) { + this.ranUeHolder = ranUeHolder; + this.regularEventCustomizer = regularEventCustomizer; + } + + public EventCustomizer getEventCustomizer(Event event, Mode mode) { + switch (mode) { + case REGULAR: + return regularEventCustomizer; + case FAILURE: + return new RanCellFailureEventCustomizer(event, ranUeHolder); + default: + throw new RuntimeException( + MessageFormat.format("Cannot construct event customizer for mode: {0}", mode)); + } + } + + public enum Mode { + REGULAR, FAILURE + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanSendVesRunnable.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanSendVesRunnable.java new file mode 100644 index 0000000..7378bc0 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanSendVesRunnable.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.util.Collection; +import java.util.function.Function; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.exception.VesBrokerException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RanSendVesRunnable implements Runnable { + + private static final Logger log = LoggerFactory.getLogger(RanSendVesRunnable.class); + + private final RanVesSender vesSender; + private Event event; + private final EventCustomizer eventCustomizer; + private final Collection<OnEventAction> onEventAction; + + public RanSendVesRunnable(RanVesSender vesSender, Event event, EventCustomizer eventCustomizer, + Collection<OnEventAction> onEventActions) { + this.vesSender = vesSender; + this.event = event; + this.eventCustomizer = eventCustomizer; + this.onEventAction = onEventActions; + } + + @Override + public void run() { + try { + Event customizedEvent = eventCustomizer.apply(event); + onEventAction.forEach(action -> action.onEvent(customizedEvent)); + vesSender.send(customizedEvent); + } catch (VesBrokerException e) { + log.error("Sending scheduled event failed: {}", e.getMessage()); + } + } + + public void updateEvent(Event event) { + this.event = event; + } + + @FunctionalInterface + public interface EventCustomizer extends Function<Event, Event> { } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanVesBrokerService.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesBrokerService.java new file mode 100644 index 0000000..8a90d46 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesBrokerService.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.RanPeriodicVesEvent; +import org.springframework.http.ResponseEntity; + +public interface RanVesBrokerService { + + ResponseEntity<String> startSendingVesEvents(String identifier, Event vesEvent, Integer interval); + + Optional<RanPeriodicVesEvent> stopSendingVesEvents(String identifier); + + Map<String, RanPeriodicVesEvent> getPeriodicEventsCache(); + + Collection<String> getEnabledEventElementIdentifiers(); + + Event getEventStructure(String identifier); + + Event startSendingFailureVesEvents(String identifier); + + Event getGlobalPmVesStructure(); + + void setGlobalPmVesStructure(Event event); + + Integer getGlobalVesInterval(); + + void setGlobalVesInterval(Integer interval); +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanVesBrokerServiceImpl.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesBrokerServiceImpl.java new file mode 100644 index 0000000..861bd36 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesBrokerServiceImpl.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.MeasurementFields.AdditionalMeasurement; +import org.onap.a1pesimulator.data.ves.RanPeriodicVesEvent; +import org.onap.a1pesimulator.util.Constants; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +@Service +public class RanVesBrokerServiceImpl implements RanVesBrokerService { + + private final RanVesDataProvider vesDataProvider; + + private final RanVesHolder vesHolder; + + public RanVesBrokerServiceImpl(RanVesDataProvider vesDataProvider, RanVesHolder vesHolder) { + this.vesDataProvider = vesDataProvider; + this.vesHolder = vesHolder; + } + + @Override + public Map<String, RanPeriodicVesEvent> getPeriodicEventsCache() { + return vesHolder.getPeriodicEventsCache(); + } + + @Override + public ResponseEntity<String> startSendingVesEvents(String identifier, Event vesEvent, Integer interval) { + + enrichWithIdentifier(identifier, vesEvent); + vesHolder.startSendingVesEvents(identifier, vesEvent, interval); + + return ResponseEntity.accepted().body("VES Event sending started"); + } + + @Override + public Event startSendingFailureVesEvents(String identifier) { + + Event vesEvent = vesDataProvider.getFailurePmVesEvent(); + + enrichWithIdentifier(identifier, vesEvent); + vesHolder.startSendingFailureVesEvents(identifier, vesEvent); + return vesEvent; + } + + @Override + public Optional<RanPeriodicVesEvent> stopSendingVesEvents(String identifier) { + return vesHolder.stopSendingVesEvents(identifier); + } + + @Override + public Collection<String> getEnabledEventElementIdentifiers() { + return vesHolder.getEnabledEventElementIdentifiers(); + } + + @Override + public Event getEventStructure(String identifier) { + return vesHolder.getEventStructure(identifier); + } + + @Override + public Event getGlobalPmVesStructure() { + return vesDataProvider.getPmVesEvent(); + } + + @Override + public void setGlobalPmVesStructure(Event event) { + vesDataProvider.setPmVesEvent(event); + } + + @Override + public Integer getGlobalVesInterval() { + return vesDataProvider.getRegularVesInterval(); + } + + @Override + public void setGlobalVesInterval(Integer interval) { + vesDataProvider.setInterval(interval); + } + + private void enrichWithIdentifier(String identifier, Event event) { + if (event.getMeasurementFields() == null || event.getMeasurementFields().getAdditionalMeasurements() == null) { + return; + } + Collection<AdditionalMeasurement> additionalMeasurements = + event.getMeasurementFields().getAdditionalMeasurements(); + Optional<AdditionalMeasurement> identityOpt = additionalMeasurements.stream() + .filter(m -> Constants.MEASUREMENT_FIELD_IDENTIFIER + .equalsIgnoreCase(m.getName())) + .findAny(); + if (identityOpt.isPresent()) { + identityOpt.get().getHashMap().put(Constants.MEASUREMENT_FIELD_IDENTIFIER, identifier); + } else { + AdditionalMeasurement measurement = new AdditionalMeasurement(); + measurement.setName(Constants.MEASUREMENT_FIELD_IDENTIFIER); + measurement.setHashMap(Collections.singletonMap(Constants.MEASUREMENT_FIELD_VALUE, identifier)); + additionalMeasurements.add(measurement); + } + } + +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanVesDataProvider.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesDataProvider.java new file mode 100644 index 0000000..95743f3 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesDataProvider.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.io.IOException; +import java.net.URL; +import lombok.Setter; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.util.JsonUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Service; + +@Service +public class RanVesDataProvider { + + private static final String PM_VES_LOCATION = "classpath:pmVes.json"; + private static final String PM_FAILURE_VES_LOCATION = "classpath:failurePmVes.json"; + + @Setter + private Event pmVesEvent; + @Setter + private Event failurePmVesEvent; + @Setter + private Integer interval; + + private final Integer defaultInterval; + private final ResourceLoader resourceLoader; + + public RanVesDataProvider(@Value("${ves.defaultInterval}") Integer defaultInterval, ResourceLoader resourceLoader) { + this.defaultInterval = defaultInterval; + this.resourceLoader = resourceLoader; + } + + @Cacheable("pmVes") + public Event loadPmVesEvent() { + URL resourceUrl = getResourceURL(resourceLoader.getResource(PM_VES_LOCATION)); + return JsonUtils.INSTANCE.deserializeFromFileUrl(resourceUrl, Event.class); + } + + @Cacheable("failurePmVes") + public Event loadFailurePmVesEvent() { + URL resourceUrl = getResourceURL(resourceLoader.getResource(PM_FAILURE_VES_LOCATION)); + return JsonUtils.INSTANCE.deserializeFromFileUrl(resourceUrl, Event.class); + } + + public Integer getRegularVesInterval() { + if (interval == null) { + return defaultInterval; + } + return interval; + } + + public Integer getFailureVesInterval() { + return defaultInterval; + } + + public Event getPmVesEvent() { + if (pmVesEvent == null) { + return loadPmVesEvent(); + } + return pmVesEvent; + } + + public Event getFailurePmVesEvent() { + if (failurePmVesEvent == null) { + return loadFailurePmVesEvent(); + } + return failurePmVesEvent; + } + + private URL getResourceURL(Resource resource) { + try { + return resource.getURL(); + } catch (IOException e) { + throw new RuntimeException("Cannot get resource URL for: " + resource.getFilename()); + } + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanVesHolder.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesHolder.java new file mode 100644 index 0000000..d53d8dd --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesHolder.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.text.MessageFormat; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.function.BiFunction; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.RanPeriodicVesEvent; +import org.onap.a1pesimulator.service.ves.RanEventCustomizerFactory.Mode; +import org.onap.a1pesimulator.service.ves.RanSendVesRunnable.EventCustomizer; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.stereotype.Service; + +@Service +public class RanVesHolder { + + private final Map<String, RanPeriodicVesEvent> periodicEventsCache = new ConcurrentHashMap<>(); + + private final RanVesDataProvider vesDataProvider; + private final RanEventCustomizerFactory eventCustomizerFactory; + private final ThreadPoolTaskScheduler vesPmThreadPoolTaskScheduler; + private final Collection<OnEventAction> onEventActions; + private final RanVesSender vesSender; + + public RanVesHolder(ThreadPoolTaskScheduler vesPmThreadPoolTaskScheduler, RanVesSender vesSender, + RanEventCustomizerFactory eventCustomizerFactory, RanVesDataProvider vesDataProvider, + Collection<OnEventAction> onEventActions) { + this.vesPmThreadPoolTaskScheduler = vesPmThreadPoolTaskScheduler; + this.vesSender = vesSender; + this.eventCustomizerFactory = eventCustomizerFactory; + this.vesDataProvider = vesDataProvider; + this.onEventActions = onEventActions; + } + + Map<String, RanPeriodicVesEvent> getPeriodicEventsCache() { + return periodicEventsCache; + } + + ResponseEntity<String> startSendingVesEvents(String identifier, Event vesEvent, Integer interval) { + + periodicEventsCache.compute(identifier, + new ThreadCacheUpdateFunction(vesPmThreadPoolTaskScheduler, vesEvent, interval, + eventCustomizerFactory.getEventCustomizer(vesEvent, Mode.REGULAR), onEventActions, vesSender)); + return ResponseEntity.accepted().body("VES Event sending started"); + } + + ResponseEntity<String> startSendingFailureVesEvents(String identifier, Event vesEvent) { + + periodicEventsCache.compute(identifier, new ThreadCacheUpdateFunction(vesPmThreadPoolTaskScheduler, vesEvent, + vesDataProvider.getFailureVesInterval(), + eventCustomizerFactory.getEventCustomizer(vesEvent, Mode.FAILURE), onEventActions, vesSender)); + return ResponseEntity.accepted().body("Failure VES Event sending started"); + } + + Optional<RanPeriodicVesEvent> stopSendingVesEvents(String identifier) { + RanPeriodicVesEvent periodicEvent = periodicEventsCache.remove(identifier); + if (periodicEvent == null) { + return Optional.empty(); + } + periodicEvent.getScheduledFuture().cancel(false); + return Optional.of(periodicEvent); + } + + Collection<String> getEnabledEventElementIdentifiers() { + return periodicEventsCache.keySet(); + } + + public boolean isEventEnabled(String identifier) { + return periodicEventsCache.containsKey(identifier); + } + + Event getEventStructure(String identifier) { + if (!periodicEventsCache.containsKey(identifier)) { + throw new IllegalArgumentException( + MessageFormat.format("Cannot find event for given source {0}", identifier)); + } + return periodicEventsCache.get(identifier).getEvent(); + } + + private static class ThreadCacheUpdateFunction + implements BiFunction<String, RanPeriodicVesEvent, RanPeriodicVesEvent> { + + private final Integer interval; + private final ThreadPoolTaskScheduler vesPmThreadPoolTaskScheduler; + private final Event vesEvent; + private final EventCustomizer eventCustomizer; + private final Collection<OnEventAction> onEventActions; + private final RanVesSender vesSender; + + public ThreadCacheUpdateFunction(ThreadPoolTaskScheduler vesPmThreadPoolTaskScheduler, Event vesEvent, + Integer interval, EventCustomizer eventCustomizer, Collection<OnEventAction> onEventActions, + RanVesSender vesSender) { + this.vesPmThreadPoolTaskScheduler = vesPmThreadPoolTaskScheduler; + this.vesEvent = vesEvent; + this.interval = interval; + this.eventCustomizer = eventCustomizer; + this.onEventActions = onEventActions; + this.vesSender = vesSender; + } + + @Override + public RanPeriodicVesEvent apply(String key, RanPeriodicVesEvent value) { + if (value != null) { + // if thread is registered then cancel it and schedule a new one + value.getScheduledFuture().cancel(false); + } + RanSendVesRunnable sendVesRunnable = + new RanSendVesRunnable(vesSender, vesEvent, eventCustomizer, onEventActions); + ScheduledFuture<?> scheduledFuture = + vesPmThreadPoolTaskScheduler.scheduleAtFixedRate(sendVesRunnable, interval * 1000L); + return RanPeriodicVesEvent.builder().event(vesEvent).interval(interval).scheduledFuture(scheduledFuture) + .sendVesRunnable(sendVesRunnable).build(); + } + + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanVesSender.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesSender.java new file mode 100644 index 0000000..9c50197 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesSender.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import org.onap.a1pesimulator.data.VnfConfig; +import org.onap.a1pesimulator.data.ves.CommonEventHeader; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.exception.VesBrokerException; +import org.onap.a1pesimulator.util.JsonUtils; +import org.onap.a1pesimulator.util.VnfConfigReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service +public class RanVesSender { + + private static final Logger log = LoggerFactory.getLogger(RanVesSender.class); + + private RestTemplate restTemplate; + + private String vesCollectorProtocol; + + private String vesCollectorPath; + + private VnfConfigReader vnfConfigReader; + + public RanVesSender(RestTemplate restTemplate, VnfConfigReader vnfConfigReader, + @Value("${ves.collector.protocol}") String vesCollectorProtocol, + @Value("${ves.collector.endpoint}") String vesCollectorPath) { + this.restTemplate = restTemplate; + this.vnfConfigReader = vnfConfigReader; + this.vesCollectorProtocol = vesCollectorProtocol; + this.vesCollectorPath = vesCollectorPath; + } + + public ResponseEntity<String> send(Event vesEvent) throws VesBrokerException { + VnfConfig vnfConfig = vnfConfigReader.getVnfConfig(); + String url = getVesCollectorUrl(vnfConfig); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setBasicAuth(vnfConfig.getVesUser(), vnfConfig.getVesPassword()); + + setVnfInfo(vesEvent, vnfConfig); + String event = JsonUtils.INSTANCE.objectToPrettyString(vesEvent); + + log.info("Sending following VES event: {}", event); + + HttpEntity<String> entity = new HttpEntity<>(event, headers); + ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class); + + log.debug("Response received: {}", response); + + if (response.getStatusCode() == HttpStatus.OK || response.getStatusCode() == HttpStatus.ACCEPTED) { + return response; + } else { + String errorMsg = + "Failed to send VES event to the collector with response status code:" + response.getStatusCode(); + throw new VesBrokerException(errorMsg); + } + } + + private String getVesCollectorUrl(VnfConfig vnfConfig) { + return vesCollectorProtocol + "://" + vnfConfig.getVesHost() + ":" + vnfConfig.getVesPort() + vesCollectorPath; + } + + private void setVnfInfo(Event vesEvent, VnfConfig vnfConfig) { + CommonEventHeader header = vesEvent.getCommonEventHeader(); + header.setSourceId(vnfConfig.getVnfId()); + header.setSourceName(vnfConfig.getVnfName()); + vesEvent.setCommonEventHeader(header); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/util/Constants.java b/src/main/java/org/onap/a1pesimulator/util/Constants.java new file mode 100644 index 0000000..1964efd --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/util/Constants.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.util; + +public class Constants { + + private Constants() { + } + + public static final String MEASUREMENT_FIELD_IDENTIFIER = "identifier"; + public static final String MEASUREMENT_FIELD_VALUE = "value"; +} diff --git a/src/main/java/org/onap/a1pesimulator/util/DistanceCalculator.java b/src/main/java/org/onap/a1pesimulator/util/DistanceCalculator.java new file mode 100644 index 0000000..e487ed3 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/util/DistanceCalculator.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.util; + +public class DistanceCalculator { + + private static final int EARTH_RADIUS = 6371; // Earth radius in KM + + private DistanceCalculator() { + } + + /** + * Calculate distance in KM using Haversine formula + */ + public static double calculate(double startLat, double startLong, double endLat, double endLong) { + return haversine(startLat, startLong, endLat, endLong); + } + + public static boolean isInRange(double startLat, double startLong, double endLat, double endLong, double range) { + double distance = calculate(startLat, startLong, endLat, endLong); + return distance < range; + } + + private static double haversine(double startLat, double startLong, double endLat, double endLong) { + double dLat = Math.toRadians(endLat - startLat); + double dLon = Math.toRadians(endLong - startLong); + double startLatInRadians = Math.toRadians(startLat); + double endLatInRadians = Math.toRadians(endLat); + + double a = Math.pow(Math.sin(dLat / 2), 2) + Math.pow(Math.sin(dLon / 2), 2) * Math.cos(startLatInRadians) + * Math.cos(endLatInRadians); + double c = 2 * Math.asin(Math.sqrt(a)); + return EARTH_RADIUS * c; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/util/ItemsRefresher.java b/src/main/java/org/onap/a1pesimulator/util/ItemsRefresher.java new file mode 100644 index 0000000..6991da0 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/util/ItemsRefresher.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.util; + +import org.onap.a1pesimulator.service.cell.RanCellsHolder; +import org.onap.a1pesimulator.service.ue.RanUeHolder; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +@Service +@EnableScheduling +public class ItemsRefresher { + + private final RanCellsHolder cellsHolder; + private final RanUeHolder ueHolder; + + public ItemsRefresher(final RanCellsHolder cellsHolder, final RanUeHolder ueHolder) { + this.cellsHolder = cellsHolder; + this.ueHolder = ueHolder; + } + + @Scheduled(fixedRateString = "${refresher.fixed.rate.ms}") + public void refresh() { + if (cellsHolder.hasChanged()) { + cellsHolder.refresh(); + } + + if (ueHolder.hasChanged()) { + ueHolder.refresh(); + } + } +} diff --git a/src/main/java/org/onap/a1pesimulator/util/JsonUtils.java b/src/main/java/org/onap/a1pesimulator/util/JsonUtils.java new file mode 100644 index 0000000..9263bc1 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/util/JsonUtils.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.FileReader; +import java.io.IOException; +import java.net.URL; +import java.text.MessageFormat; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public enum JsonUtils { + + INSTANCE; + + private static final Logger log = LoggerFactory.getLogger(JsonUtils.class); + + private ObjectMapper mapper; + + private JsonUtils() { + this.mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + public String objectToPrettyString(Object object) { + try { + return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(object); + } catch (JsonProcessingException e) { + throw new JsonUtilsException("Cannot serialize object", e); + } + } + + @SuppressWarnings("unchecked") + public <T> T clone(T object) { + String stringValue; + try { + stringValue = mapper.writeValueAsString(object); + return (T) mapper.readValue(stringValue, object.getClass()); + } catch (JsonProcessingException e) { + throw new JsonUtilsException("Cannot clone object", e); + } + } + + public <T> T deserializeFromFile(String fileName, Class<T> clazz) { + try { + return mapper.readValue(new FileReader(fileName), clazz); + } catch (IOException e) { + String errorMsg = MessageFormat.format("Could not deserialize from file: {0} into {1}", fileName, + clazz.getSimpleName()); + log.error(errorMsg, e); + throw new JsonUtilsException(errorMsg, e); + } + } + + public <T> T deserializeFromFileUrl(URL url, Class<T> clazz) { + try { + return mapper.readValue(url, clazz); + } catch (IOException e) { + String errorMsg = MessageFormat.format("Could not deserialize from file URL: {0} into {1}", url, + clazz.getSimpleName()); + log.error(errorMsg, e); + throw new JsonUtilsException(errorMsg, e); + } + } + + public <T> T deserialize(String string, Class<T> clazz) throws JsonUtilsException { + try { + return mapper.readValue(string, clazz); + } catch (IOException e) { + String errorMsg = MessageFormat + .format("Could not deserialize into {0} from object: {1}", clazz.getSimpleName(), + string); + log.error(errorMsg, e); + throw new JsonUtilsException(errorMsg, e); + } + } + + public static class JsonUtilsException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public JsonUtilsException(String message, Throwable throwable) { + super(message, throwable); + } + } +} diff --git a/src/main/java/org/onap/a1pesimulator/util/RanVesUtils.java b/src/main/java/org/onap/a1pesimulator/util/RanVesUtils.java new file mode 100644 index 0000000..d5f3e67 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/util/RanVesUtils.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.util; + +import java.text.MessageFormat; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.data.ves.CommonEventHeader; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.MeasurementFields.AdditionalMeasurement; +import org.onap.a1pesimulator.service.ue.RanUeHolder; + +public class RanVesUtils { + + private static final String PATTERN_DIGIT = "\\d+"; + private static final String PATTERN_SPLIT_RANDOM = "-"; + private static final String PATTERN_SPLIT_TRENDING = "->"; + private static final String MARKER_START = "[["; + private static final String MARKER_END = "]]"; + private static final String PATTERN_MARKER_START = "\\[\\["; + private static final String PATTERN_MARKER_END = "\\]\\]"; + + private static final String UE_PARAM_TRAFFIC_MODEL = "trafficModel"; + private static final int TEN_MINUTES_MICROSECONDS = 10 * 60 * 1000_000; + + private static final Random random = new Random(); + + private RanVesUtils() { + } + + public static void updateHeader(Event event) { + CommonEventHeader commonEventHeader = event.getCommonEventHeader(); + commonEventHeader.setLastEpochMicrosec(ChronoUnit.MICROS.between(Instant.EPOCH, Instant.now())); + commonEventHeader.setStartEpochMicrosec(getStartEpochMicroseconds()); + } + + public static AdditionalMeasurement buildTrafficModelMeasurement(AdditionalMeasurement identityMeasurement, + RanUeHolder ranUeHolder, String valuePattern) { + String cellId = identityMeasurement.getHashMap().get(Constants.MEASUREMENT_FIELD_VALUE); + AdditionalMeasurement trafficModel = new AdditionalMeasurement(); + Map<String, String> hashMap = new HashMap<>(); + trafficModel.setName(UE_PARAM_TRAFFIC_MODEL); + trafficModel.setHashMap(hashMap); + Collection<UserEquipment> cellUes = ranUeHolder.getUserEquipmentsConnectedToCell(cellId); + cellUes.stream().map(UserEquipment::getId).forEach(ueId -> hashMap.put(ueId, valuePattern)); + + return trafficModel; + } + + public static List<AdditionalMeasurement> randomizeAdditionalMeasurements( + Collection<AdditionalMeasurement> toRandomize) { + return toRandomize.stream().map(measurement -> transformAdditionalMeasurementValues(measurement, + RanVesUtils::randomizeValue)).collect(Collectors.toList()); + } + + public static List<AdditionalMeasurement> setLowRangeValues(List<AdditionalMeasurement> toUpdateMeasurements) { + return toUpdateMeasurements.stream().map(measurement -> transformAdditionalMeasurementValues(measurement, + RanVesUtils::getLowRangeValue)).collect(Collectors.toList()); + } + + private static AdditionalMeasurement transformAdditionalMeasurementValues(AdditionalMeasurement measurement, + UnaryOperator<String> transformAction) { + AdditionalMeasurement randomizedMeasurement = new AdditionalMeasurement(); + randomizedMeasurement.setName(measurement.getName()); + randomizedMeasurement.setHashMap(transformValues(measurement.getHashMap(), transformAction)); + return randomizedMeasurement; + } + + private static Map<String, String> transformValues(Map<String, String> values, + UnaryOperator<String> transformAction) { + Map<String, String> randomizedMap = new HashMap<>(values.size()); + values.forEach((key, value) -> randomizedMap.put(key, transformAction.apply(value))); + return randomizedMap; + } + + private static String randomizeValue(String value) { + if (!isRange(value)) { + return value; + } + String toRandomize = value.substring(MARKER_START.length(), value.length() - MARKER_END.length()); + String[] ranges = toRandomize.split(PATTERN_SPLIT_RANDOM); + int randomNumber = getRandomNumber(parseInt(ranges[0]), parseInt(ranges[1])); + return String.valueOf(randomNumber); + } + + private static String getLowRangeValue(String value) { + if (!isRange(value)) { + return value; + } + String toRandomize = value.substring(MARKER_START.length(), value.length() - MARKER_END.length()); + String[] ranges = toRandomize.split(PATTERN_SPLIT_RANDOM); + return String.valueOf(ranges[0]); + } + + private static Long getStartEpochMicroseconds() { + long epochMicrosecondsNow = ChronoUnit.MICROS.between(Instant.EPOCH, Instant.now()); + long lowest10minInterval = epochMicrosecondsNow - epochMicrosecondsNow % TEN_MINUTES_MICROSECONDS; + long highest10minInterval = lowest10minInterval + TEN_MINUTES_MICROSECONDS; + + if ((epochMicrosecondsNow - lowest10minInterval) < (highest10minInterval - epochMicrosecondsNow)) { + return lowest10minInterval; + } else { + return highest10minInterval; + } + } + + public static int getRandomNumber(int min, int max) { + return random.nextInt(max - min) + min; + } + + private static int parseInt(String strNum) { + try { + return Integer.parseInt(strNum); + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException(MessageFormat.format("Cannot parse int for value: {0}", strNum), nfe); + } + } + + public static boolean isRange(String value) { + return value.startsWith(MARKER_START) && value.endsWith(MARKER_END); + } + + private static boolean isRange(String value, String splitPattern) { + String pattern = PATTERN_MARKER_START + PATTERN_DIGIT + splitPattern + PATTERN_DIGIT + PATTERN_MARKER_END; + return value.matches(pattern); + } + + public static boolean isRandomRange(String value) { + return isRange(value, PATTERN_SPLIT_RANDOM); + } + + public static boolean isTrandingRange(String value) { + return isRange(value, PATTERN_SPLIT_TRENDING); + } + + public static String[] splitRandomRange(String value) { + return splitRange(value, PATTERN_SPLIT_RANDOM); + } + + public static String[] splitTrendingRange(String value) { + return splitRange(value, PATTERN_SPLIT_TRENDING); + } + + private static String[] splitRange(String value, String splitPattern) { + String truncated = value.substring(MARKER_START.length(), value.length() - MARKER_END.length()); + return truncated.split(splitPattern); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/util/TopologyReader.java b/src/main/java/org/onap/a1pesimulator/util/TopologyReader.java new file mode 100644 index 0000000..55e47cf --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/util/TopologyReader.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.util; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import org.onap.a1pesimulator.data.cell.CellList; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +public class TopologyReader { + + private final String topologyCellConfigFile; + private final String topologyUeConfigFile; + + private long topologyCellLastModified = 0L; + private long topologyUeLastModified = 0L; + + private TopologyReader(@Value("${topology.cell.config.file}") final String topologyCellConfigFile, + @Value("${topology.ue.config.file}") final String topologyUeConfigFile) { + this.topologyCellConfigFile = topologyCellConfigFile; + this.topologyUeConfigFile = topologyUeConfigFile; + } + + public CellList loadCellTopology() { + final File file = new File(topologyCellConfigFile); + topologyCellLastModified = file.lastModified(); + + if (!file.exists()) { + return new CellList(); + } + + return JsonUtils.INSTANCE.deserializeFromFile(topologyCellConfigFile, CellList.class); + } + + public Collection<UserEquipment> loadUeTopology() { + final File file = new File(topologyUeConfigFile); + topologyUeLastModified = file.lastModified(); + + if (!file.exists()) { + return Collections.emptyList(); + } + + final UserEquipment[] userEquipments = + JsonUtils.INSTANCE.deserializeFromFile(topologyUeConfigFile, UserEquipment[].class); + return Arrays.asList(userEquipments); + } + + public boolean topologyCellHasChanged() { + return topologyCellLastModified != new File(topologyCellConfigFile).lastModified(); + } + + public boolean topologyUeHasChanged() { + return topologyUeLastModified != new File(topologyUeConfigFile).lastModified(); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/util/VnfConfigReader.java b/src/main/java/org/onap/a1pesimulator/util/VnfConfigReader.java new file mode 100644 index 0000000..318b3d6 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/util/VnfConfigReader.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.Properties; +import org.onap.a1pesimulator.data.VnfConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +public class VnfConfigReader { + + private static final Logger log = LoggerFactory.getLogger(VnfConfigReader.class); + + private String vnfConfigFile; + + private VnfConfigReader(@Value("${vnf.config.file}") String vnfConfigFile) { + this.vnfConfigFile = vnfConfigFile; + } + + public VnfConfig getVnfConfig() { + + Properties props = new Properties(); + VnfConfig vnfConfig; + + try { + props.load(new FileReader(vnfConfigFile)); + ObjectMapper mapper = new ObjectMapper(); + vnfConfig = mapper.convertValue(props, VnfConfig.class); + } catch (FileNotFoundException e) { + String errorMsg = "Could not find vnfConfigFile under: " + vnfConfigFile; + log.error(errorMsg); + throw new VnfConfigReaderException(errorMsg, e); + } catch (Exception e) { + String errorMsg = "Failed to get VnfConfig: " + e.getMessage(); + log.error(errorMsg); + throw new VnfConfigReaderException(errorMsg, e); + } + + return vnfConfig; + } + + private static class VnfConfigReaderException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public VnfConfigReaderException(String message, Throwable t) { + super(message, t); + } + } +} diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties new file mode 100644 index 0000000..d8e142d --- /dev/null +++ b/src/main/resources/application-dev.properties @@ -0,0 +1,17 @@ +# +# Copyright (C) 2021 Samsung Electronics +# 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 +# + +vnf.config.file=src/test/resources/vnf.config + +topology.cell.config.file=src/test/resources/cells.json +topology.ue.config.file=src/test/resources/ue.json
\ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..b1f3a97 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,41 @@ +# +# Copyright (C) 2021 Samsung Electronics +# 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 +# + +server.port=9998 +vnf.config.file=/var/a1pesim/vnf.config +ves.collector.protocol=https +ves.collector.endpoint=/eventListener/v7 +ves.pm.maxPoolSize=10 +ves.defaultInterval=10 +ves.defaultFailureDuration=120 + +ves.failing.throughput=1 +ves.failing.latency=500 +# in sec +ves.failing.checkout.delay=15 + +topology.cell.range=5 +topology.cell.config.file=/var/a1pesim/cells.json +topology.ue.config.file=/var/a1pesim/ue.json + +spring.cache.cache-names=vnfConfig,pmVes,failurePmVes +spring.cache.caffeine.spec=maximumSize=10,expireAfterAccess=900s + +spring.mvc.view.prefix: / +spring.mvc.view.suffix: .jsp + +logging.config=classpath:logback-spring.xml + +refresher.fixed.rate.ms=60000 + +restapi.version=v1
\ No newline at end of file diff --git a/src/main/resources/failurePmVes.json b/src/main/resources/failurePmVes.json new file mode 100644 index 0000000..1fdcdd3 --- /dev/null +++ b/src/main/resources/failurePmVes.json @@ -0,0 +1,41 @@ +{ + "event": { + "commonEventHeader": { + "version": "4.0.1", + "vesEventListenerVersion": "7.0.1", + "domain": "measurement", + "eventName": "Measurement_vIsbcMmc", + "eventId": "measurement0000259", + "sequence": 3, + "priority": "Normal", + "reportingEntityId": "cc305d54-75b4-431b-adb2-eb6b9e541234", + "reportingEntityName": "ibcx0001vm002oam001", + "sourceId": "de305d54-75b4-431b-adb2-eb6b9e546014", + "sourceName": "ibcx0001vm002ssc001", + "nfVendorName": "Samsung", + "nfNamingCode": "ibcx", + "nfcNamingCode": "ssc", + "startEpochMicrosec": 1413378172000000, + "lastEpochMicrosec": 1413378172000000, + "timeZoneOffset": "UTC-05:30" + }, + "measurementFields": { + "additionalMeasurements": [ + { + "name": "latency", + "hashMap": { + "value": "[[200->500]]" + } + }, + { + "name": "throughput", + "hashMap": { + "value": "[[10->1]]" + } + } + ], + "measurementInterval": 5, + "measurementFieldsVersion": "4.0" + } + } +} diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..f03b9bd --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2021 Samsung Electronics + ~ 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 + --> + +<configuration> + + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</Pattern> + </layout> + </appender> + + <springProfile name="!dev"> + + <property name="LOG_DIR" value="${user.home}/log/${proc.name}"/> + + <appender name="A1-PE-SIMULATOR-METRICS" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <filter class="ch.qos.logback.classic.filter.LevelFilter"> + <level>INFO</level> + <onMatch>ACCEPT</onMatch> + <onMismatch>DENY</onMismatch> + </filter> + <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> + <fileNamePattern>${LOG_DIR}/application/metrics-%d{yyyy-MM-dd}.%i.log.gz + </fileNamePattern> + <maxFileSize>30MB</maxFileSize> + <maxHistory>30</maxHistory> + <totalSizeCap>1GB</totalSizeCap> + </rollingPolicy> + <layout class="ch.qos.logback.classic.PatternLayout"> + <pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSS+00:00}|NULL|%-5level:%class{36}:%M:%L: %m%n + </pattern> + </layout> + </appender> + + <appender name="A1-PE-SIMULATOR-DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <filter class="ch.qos.logback.classic.filter.LevelFilter"> + <level>DEBUG</level> + <onMatch>ACCEPT</onMatch> + <onMismatch>DENY</onMismatch> + </filter> + <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> + <fileNamePattern>${LOG_DIR}/application/debug-%d{yyyy-MM-dd}.%i.log.gz + </fileNamePattern> + <maxFileSize>30MB</maxFileSize> + <maxHistory>30</maxHistory> + <totalSizeCap>1GB</totalSizeCap> + </rollingPolicy> + <layout class="ch.qos.logback.classic.PatternLayout"> + <pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSS+00:00}|NULL|%-5level:%class{36}:%M:%L: %m%n + </pattern> + </layout> + </appender> + + <appender name="A1-PE-SIMULATOR-ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <filter class="ch.qos.logback.classic.filter.LevelFilter"> + <level>ERROR</level> + <onMatch>ACCEPT</onMatch> + <onMismatch>DENY</onMismatch> + </filter> + <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> + <fileNamePattern>${LOG_DIR}/application/error-%d{yyyy-MM-dd}.%i.log.gz + </fileNamePattern> + <maxFileSize>30MB</maxFileSize> + <maxHistory>30</maxHistory> + <totalSizeCap>1GB</totalSizeCap> + </rollingPolicy> + <layout class="ch.qos.logback.classic.PatternLayout"> + <pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSS+00:00}|NULL|%-5level:%class{36}:%M:%L: %m%n + </pattern> + </layout> + </appender> + + <appender name="debugAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> + <fileNamePattern>${LOG_DIR}/debug-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern> + <maxFileSize>30MB</maxFileSize> + <maxHistory>30</maxHistory> + <totalSizeCap>1GB</totalSizeCap> + </rollingPolicy> + <layout class="ch.qos.logback.classic.PatternLayout"> + <pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSS+00:00}|NULL|%-5level:%class{36}:%M:%L: %m%n + </pattern> + </layout> + </appender> + + <logger name="org.onap.a1pesimulator" level="debug" additivity="false"> + <appender-ref ref="A1-PE-SIMULATOR-DEBUG"/> + <appender-ref ref="A1-PE-SIMULATOR-METRICS"/> + <appender-ref ref="A1-PE-SIMULATOR-ERROR"/> + </logger> + + <root level="INFO"> + <appender-ref ref="debugAppender"/> + </root> + + </springProfile> + + <springProfile name="dev"> + <root level="INFO"> + <appender-ref ref="STDOUT"/> + </root> + </springProfile> + +</configuration> diff --git a/src/main/resources/pmVes.json b/src/main/resources/pmVes.json new file mode 100644 index 0000000..9c4f717 --- /dev/null +++ b/src/main/resources/pmVes.json @@ -0,0 +1,41 @@ +{ + "event": { + "commonEventHeader": { + "version": "4.0.1", + "vesEventListenerVersion": "7.0.1", + "domain": "measurement", + "eventName": "Measurement_vIsbcMmc", + "eventId": "measurement0000259", + "sequence": 3, + "priority": "Normal", + "reportingEntityId": "cc305d54-75b4-431b-adb2-eb6b9e541234", + "reportingEntityName": "ibcx0001vm002oam001", + "sourceId": "de305d54-75b4-431b-adb2-eb6b9e546014", + "sourceName": "ibcx0001vm002ssc001", + "nfVendorName": "Samsung", + "nfNamingCode": "ibcx", + "nfcNamingCode": "ssc", + "startEpochMicrosec": 1413378172000000, + "lastEpochMicrosec": 1413378172000000, + "timeZoneOffset": "UTC-05:30" + }, + "measurementFields": { + "additionalMeasurements": [ + { + "name": "latency", + "hashMap": { + "value": "[[10-150]]" + } + }, + { + "name": "throughput", + "hashMap": { + "value": "[[10-100]]" + } + } + ], + "measurementInterval": 5, + "measurementFieldsVersion": "4.0" + } + } +} diff --git a/src/test/java/org/onap/a1pesimulator/TestHelpers.java b/src/test/java/org/onap/a1pesimulator/TestHelpers.java new file mode 100644 index 0000000..449318d --- /dev/null +++ b/src/test/java/org/onap/a1pesimulator/TestHelpers.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.CellWithStatus; +import org.onap.a1pesimulator.data.ue.UserEquipment; + +public class TestHelpers { + + public static final int CELL_ARRAY_SIZE = 5; + public static final int UE_ARRAY_SIZE = 3; + + public static final String CELL_PREFIX = "Chn00"; + + public static final String FIRST_CELL_ID = "Chn0000"; + public static final double FIRST_CELL_LATITUDE = 50.11; + public static final double FIRST_CELL_LONGITUDE = 19.98; + public static final int FIRST_CELL_CONNECTED_UE_SIZE = 1; + public static final String FIRST_CELL_CONNECTED_UE_ID = "mobile_samsung_s10"; + + public static final String FIRST_UE_ID = "emergency_police_111"; + public static final double FIRST_UE_LATITUDE = 50.035; + public static final double FIRST_UE_LONGITUDE = 19.97; + public static final String FIRST_UE_CELL_ID = "Chn0002"; + + public static final String FIRST_UE_HANDOVER_CELL = "Chn0004"; + + public static void checkFirstCell(CellDetails cellDetails) { + assertNotNull(cellDetails); + + assertEquals(FIRST_CELL_ID, cellDetails.getId()); + assertEquals(FIRST_CELL_LATITUDE, cellDetails.getLatitude()); + assertEquals(FIRST_CELL_LONGITUDE, cellDetails.getLongitude()); + assertEquals(FIRST_CELL_CONNECTED_UE_SIZE, cellDetails.getConnectedUserEquipments().size()); + assertEquals(FIRST_CELL_CONNECTED_UE_ID, cellDetails.getConnectedUserEquipments().iterator().next()); + } + + public static void checkFirstUserEquipment(UserEquipment userEquipment) { + assertNotNull(userEquipment); + + assertEquals(FIRST_UE_ID, userEquipment.getId()); + assertEquals(FIRST_UE_LATITUDE, userEquipment.getLatitude()); + assertEquals(FIRST_UE_LONGITUDE, userEquipment.getLongitude()); + assertEquals(FIRST_UE_CELL_ID, userEquipment.getCellId()); + } + + public static void checkFirstCellWithStatus(CellWithStatus cellWithStatus) { + assertNotNull(cellWithStatus); + + assertEquals(FIRST_CELL_ID, cellWithStatus.getCell().getIdentifier()); + assertFalse(cellWithStatus.isFailureMode()); + assertFalse(cellWithStatus.isVesEnabled()); + } + + private TestHelpers() { + } +} diff --git a/src/test/java/org/onap/a1pesimulator/controller/RanA1ControllerTest.java b/src/test/java/org/onap/a1pesimulator/controller/RanA1ControllerTest.java new file mode 100644 index 0000000..5b846c4 --- /dev/null +++ b/src/test/java/org/onap/a1pesimulator/controller/RanA1ControllerTest.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.controller; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.onap.a1pesimulator.TestHelpers.FIRST_UE_CELL_ID; +import static org.onap.a1pesimulator.TestHelpers.FIRST_UE_HANDOVER_CELL; +import static org.onap.a1pesimulator.TestHelpers.FIRST_UE_ID; +import static org.onap.a1pesimulator.controller.URLHelper.getHealthCheckEndpoint; +import static org.onap.a1pesimulator.controller.URLHelper.getPolicyPath; +import static org.onap.a1pesimulator.controller.URLHelper.getPolicyTypePath; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.common.collect.Lists; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.service.ue.RanUeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +@SpringBootTest +@AutoConfigureMockMvc +@RunWith(SpringRunner.class) +public class RanA1ControllerTest { + + private static final String FIRST_POLICY_TYPE_ID = "1000"; + private static final Policy.Preference FIRST_POLICY_PREFERENCE = Policy.Preference.AVOID; + private static final String FIRST_POLICY_ORIGINAL_ID = "ue1"; + private static final String TEST_POLICY_SCHEMA = + "{\n" + " \"name\": \"samsung_policy_type\",\n" + " \"description\": \"samsung policy type;\",\n" + + " \"policy_type_id\": 1000,\n" + " \"create_schema\": {\n" + + " \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n" + + " \"title\": \"Samsung_demo\",\n" + " \"description\": \"Samsung demo policy type\",\n" + + " \"type\": \"object\",\n" + " \"additionalProperties\": false,\n" + " \"required\": [\n" + + " \"scope\",\n" + " \"resources\"\n" + " ]\n" + " }\n" + "}"; + + @Autowired + private MockMvc mvc; + + @Autowired + private ObjectMapper mapper; + + @Autowired + RanUeService ranUeService; + + @Test + public void testHealthcheck() throws Exception { + this.mvc.perform(get(getHealthCheckEndpoint())); + } + + @Test + public void testGetPolicyTypes() throws Exception { + String firstPolicyURL = URLHelper.getPolicyTypePath("1000"); + mvc.perform(put(firstPolicyURL).content(TEST_POLICY_SCHEMA)).andExpect(status().isCreated()); + + String[] policyTypes = mapper.readValue(getFromController(getPolicyTypePath()), String[].class); + + assertEquals(FIRST_POLICY_TYPE_ID, policyTypes[0]); + } + + @Test + public void testGetPolicy() throws Exception { + // Remove escaping + Policy policy = mapper.readValue(getFromController(getPolicyPath("3", "1")), Policy.class); + + assertEquals(FIRST_POLICY_ORIGINAL_ID, policy.getScope().getUeId()); + assertEquals(FIRST_POLICY_PREFERENCE, policy.getResources().get(0).getPreference()); + } + + @Test + public void testPutPolicyAndTriggerHandover() throws Exception { + String policyURL = getPolicyPath("3", "1"); + + mvc.perform(put(policyURL).content(getHandoverPolicy())).andExpect(status().isAccepted()); + + // Remove escaping + Policy policy = mapper.readValue(getFromController(policyURL), Policy.class); + + assertEquals(FIRST_UE_ID, policy.getScope().getUeId()); + + assertEquals(FIRST_POLICY_PREFERENCE, policy.getResources().get(0).getPreference()); + assertEquals(FIRST_UE_CELL_ID, policy.getResources().get(0).getCellIdList().get(0)); + + UserEquipment userEquipment = ranUeService.getUserEquipment(FIRST_UE_ID).orElse(null); + + assertNotNull(userEquipment); + assertEquals(FIRST_UE_HANDOVER_CELL, userEquipment.getCellId()); + + mvc.perform(put(policyURL).content(getOriginalPolicy())).andExpect(status().isAccepted()); + } + + @Test + public void testPutPolicyAndCantHandover() throws Exception { + String policyURL = getPolicyPath("3", "1"); + + // trigger cellId==null in the onPolicy + mvc.perform(put(policyURL).content(getHandoverPolicyWithoutCellId())).andExpect(status().isAccepted()); + + // trigger canHandover==false in the onPolicy + mvc.perform(put(policyURL).content(getOriginalPolicy())).andExpect(status().isAccepted()); + } + + @Test + public void shouldDeletePolicyInstanceAndReturn200() throws Exception { + String url = getPolicyPath("3", "1"); + mvc.perform(delete(url)).andExpect(status().isAccepted()); + } + + @Test + public void shouldReturn404WhenPolicyInstanceNotExists() throws Exception { + String url = getPolicyPath("10", "321123"); + mvc.perform(delete(url)).andExpect(status().isNotFound()); + } + + private String getFromController(String url) throws Exception { + return this.mvc.perform(get(url)).andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + } + + private String getOriginalPolicy() throws JsonProcessingException { + return mapper.writeValueAsString( + Policy.builder().scope(Policy.Scope.builder().ueId(FIRST_POLICY_ORIGINAL_ID).build()).resources( + Lists.newArrayList(Policy.Resources.builder().cellIdList(Lists.newArrayList(FIRST_UE_CELL_ID)) + .preference(Policy.Preference.AVOID).build())).build()); + } + + private String getHandoverPolicy() throws JsonProcessingException { + return mapper.writeValueAsString(Policy.builder().scope(Policy.Scope.builder().ueId(FIRST_UE_ID).build()) + .resources(Lists.newArrayList(Policy.Resources.builder().cellIdList( + Lists.newArrayList(FIRST_UE_CELL_ID)).preference( + Policy.Preference.AVOID).build())).build()); + } + + private String getHandoverPolicyWithoutCellId() throws JsonProcessingException { + return mapper.writeValueAsString(Policy.builder().scope(Policy.Scope.builder().ueId(FIRST_UE_ID).build()) + .resources(Lists.newArrayList( + Policy.Resources.builder().cellIdList(Lists.newArrayList()) + .preference(Policy.Preference.AVOID).build())) + .build()); + } + + @Data + @Builder + @JsonSerialize + @NoArgsConstructor + @AllArgsConstructor + private static class Policy { + + private Scope scope; + private List<Resources> resources; + + @Data + @Builder + @JsonSerialize + @NoArgsConstructor + @AllArgsConstructor + private static class Scope { + + private String ueId; + } + + @Data + @Builder + @JsonSerialize + @NoArgsConstructor + @AllArgsConstructor + private static class Resources { + + private List<String> cellIdList; + private Preference preference; + } + + public enum Preference { + SHALL("SHALL"), PREFER("PREFER"), AVOID("AVOID"), FORBID("FORBID"); + public final String value; + + Preference(String stateName) { + this.value = stateName; + } + } + + } +} diff --git a/src/test/java/org/onap/a1pesimulator/controller/RanCellControllerTest.java b/src/test/java/org/onap/a1pesimulator/controller/RanCellControllerTest.java new file mode 100644 index 0000000..ebc93fb --- /dev/null +++ b/src/test/java/org/onap/a1pesimulator/controller/RanCellControllerTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.controller; + +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.onap.a1pesimulator.controller.URLHelper.getRanCellControllerEndpoint; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.RanCell; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; + +@SpringBootTest +@AutoConfigureMockMvc +@RunWith(SpringRunner.class) +@ActiveProfiles(value = {"localStore"}) +public class RanCellControllerTest { + + @Autowired + private MockMvc mvc; + + @Autowired + private ObjectMapper mapper; + + protected String mapToJson(Object obj) throws JsonProcessingException { + mapper = new ObjectMapper(); + return mapper.writeValueAsString(obj); + } + + protected <T> T mapFromJson(String json, Class<T> clazz) + throws JsonParseException, JsonMappingException, IOException { + mapper = new ObjectMapper(); + return mapper.readValue(json, clazz); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetCells() throws Exception { + MvcResult mvcResult = this.mvc.perform( + MockMvcRequestBuilders.get(getRanCellControllerEndpoint()).accept(MediaType.APPLICATION_JSON_VALUE)) + .andDo(MockMvcResultHandlers.print()).andReturn(); + + int status = mvcResult.getResponse().getStatus(); + assertEquals(200, status); + String content = mvcResult.getResponse().getContentAsString(); + + RanCell[] ranCell = this.mapFromJson(content, RanCell[].class); + + assertTrue(ranCell.length > 0); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetCellById() throws Exception { + MvcResult mvcResult = this.mvc.perform( + MockMvcRequestBuilders.get(getRanCellControllerEndpoint() + "/{identifier}", 1) + .accept(MediaType.APPLICATION_JSON_VALUE)).andReturn(); + + int status = mvcResult.getResponse().getStatus(); + assertEquals(200, status); + String content = mvcResult.getResponse().getContentAsString(); + + CellDetails cell = this.mapFromJson(content, CellDetails.class); + assertEquals(cell.getId(), "1"); + } + + @Test(expected = IllegalArgumentException.class) + public void testStartSendingFailureVesEvents() throws Exception { + this.mvc.perform(MockMvcRequestBuilders.get(getRanCellControllerEndpoint() + "/1/startFailure") + .accept(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()); + } + + @Test(expected = IllegalArgumentException.class) + public void testStopSendingFailureVesEvents() throws Exception { + this.mvc.perform(MockMvcRequestBuilders.get(getRanCellControllerEndpoint() + "/1/stopFailure") + .accept(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()); + } + + @Test(expected = IllegalArgumentException.class) + public void testStartSendingVesEvents() throws Exception { + this.mvc.perform(MockMvcRequestBuilders.get(getRanCellControllerEndpoint() + "/1/start") + .accept(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()); + } + + @Test(expected = IllegalArgumentException.class) + public void testStopSendingVesEvents() throws Exception { + this.mvc.perform(MockMvcRequestBuilders.get(getRanCellControllerEndpoint() + "/1/stop") + .accept(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetVesEventStructure() throws Exception { + this.mvc.perform(MockMvcRequestBuilders.get(getRanCellControllerEndpoint() + "/1/structure") + .accept(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()); + } +} diff --git a/src/test/java/org/onap/a1pesimulator/controller/RanControllerTest.java b/src/test/java/org/onap/a1pesimulator/controller/RanControllerTest.java new file mode 100644 index 0000000..3c2602f --- /dev/null +++ b/src/test/java/org/onap/a1pesimulator/controller/RanControllerTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.controller; + +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.onap.a1pesimulator.controller.URLHelper.getRanCellControllerEndpoint; +import static org.onap.a1pesimulator.controller.URLHelper.getRanControllerEndpoint; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.onap.a1pesimulator.data.Topology; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; + +@SpringBootTest +@AutoConfigureMockMvc +@RunWith(SpringRunner.class) +@ActiveProfiles(value = {"localStore"}) +public class RanControllerTest { + + @Autowired + private MockMvc mvc; + + @Autowired + private ObjectMapper mapper; + + protected <T> T mapFromJson(String json, Class<T> clazz) throws IOException { + mapper = new ObjectMapper(); + return mapper.readValue(json, clazz); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetRan() throws Exception { + MvcResult mvcResult = this.mvc.perform( + MockMvcRequestBuilders.get(getRanControllerEndpoint()).accept(MediaType.APPLICATION_JSON_VALUE)) + .andDo(MockMvcResultHandlers.print()).andReturn(); + + int status = mvcResult.getResponse().getStatus(); + assertEquals(200, status); + String content = mvcResult.getResponse().getContentAsString(); + + Topology topology = this.mapFromJson(content, Topology.class); + + assertTrue(topology.getCells().size() > 0); + assertTrue(topology.getUserEquipments().size() > 0); + } + + @Test(expected = IllegalArgumentException.class) + public void testRefreshRan() throws Exception { + this.mvc.perform(MockMvcRequestBuilders.get(getRanCellControllerEndpoint() + "/refresh") + .accept(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()); + } +} diff --git a/src/test/java/org/onap/a1pesimulator/controller/RanUeControllerTest.java b/src/test/java/org/onap/a1pesimulator/controller/RanUeControllerTest.java new file mode 100644 index 0000000..f474102 --- /dev/null +++ b/src/test/java/org/onap/a1pesimulator/controller/RanUeControllerTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.controller; + +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.onap.a1pesimulator.controller.URLHelper.getRanUeControllerEndpoint; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; + +@SpringBootTest +@AutoConfigureMockMvc +@RunWith(SpringRunner.class) +@ActiveProfiles(value = {"localStore"}) +public class RanUeControllerTest { + + @Autowired + private MockMvc mvc; + + @Autowired + private ObjectMapper mapper; + + protected String mapToJson(Object obj) throws JsonProcessingException { + mapper = new ObjectMapper(); + return mapper.writeValueAsString(obj); + } + + protected <T> T mapFromJson(String json, Class<T> clazz) + throws JsonParseException, JsonMappingException, IOException { + mapper = new ObjectMapper(); + return mapper.readValue(json, clazz); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetUes() throws Exception { + MvcResult mvcResult = this.mvc.perform( + MockMvcRequestBuilders.get(getRanUeControllerEndpoint()).accept(MediaType.APPLICATION_JSON_VALUE)) + .andDo(MockMvcResultHandlers.print()).andReturn(); + + int status = mvcResult.getResponse().getStatus(); + assertEquals(200, status); + String content = mvcResult.getResponse().getContentAsString(); + + UserEquipment[] ues = this.mapFromJson(content, UserEquipment[].class); + + assertTrue(ues.length > 0); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetUeById() throws Exception { + MvcResult mvcResult = this.mvc.perform( + MockMvcRequestBuilders.get(getRanUeControllerEndpoint() + "/{identifier}", 1) + .accept(MediaType.APPLICATION_JSON_VALUE)).andReturn(); + + int status = mvcResult.getResponse().getStatus(); + assertEquals(200, status); + String content = mvcResult.getResponse().getContentAsString(); + + UserEquipment ue = this.mapFromJson(content, UserEquipment.class); + assertEquals(ue.getId(), "1"); + } +} diff --git a/src/test/java/org/onap/a1pesimulator/controller/URLHelper.java b/src/test/java/org/onap/a1pesimulator/controller/URLHelper.java new file mode 100644 index 0000000..41b55dc --- /dev/null +++ b/src/test/java/org/onap/a1pesimulator/controller/URLHelper.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.controller; + +import org.springframework.stereotype.Component; + +@Component +public class URLHelper { + + private static final String A1_CONTROLLER_PREFIX = "/v1/a1-p"; + + private static final String POLICY_FORMAT = "%s/%s/policies/%s"; + + private static final String RAN_CELL_CONTROLLER_PREFIX = "${restapi.version}/ran/cells"; + + private static final String RAN_CONTROLLER_PREFIX = "${restapi.version}/ran"; + + private static final String RAN_UE_CONTROLLER_PREFIX = "${restapi.version}/ran/ues"; + + public static String getHealthCheckEndpoint() { + return A1_CONTROLLER_PREFIX + "/healthcheck"; + } + + public static String getPolicyTypePath() { + return A1_CONTROLLER_PREFIX + "/policytypes"; + } + + public static String getPolicyTypePath(String policyType) { + return getPolicyTypePath() + "/" + policyType; + } + + public static String getPolicyPath(String policyType, String policy) { + return String.format(POLICY_FORMAT, getPolicyTypePath(), policyType, policy); + } + + public static String getRanCellControllerEndpoint() { + return RAN_CELL_CONTROLLER_PREFIX; + } + + public static String getRanControllerEndpoint() { + return RAN_CONTROLLER_PREFIX; + } + + public static String getRanUeControllerEndpoint() { + return RAN_UE_CONTROLLER_PREFIX; + } +} diff --git a/src/test/java/org/onap/a1pesimulator/service/CellServiceTest.java b/src/test/java/org/onap/a1pesimulator/service/CellServiceTest.java new file mode 100644 index 0000000..5e9dc05 --- /dev/null +++ b/src/test/java/org/onap/a1pesimulator/service/CellServiceTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.onap.a1pesimulator.TestHelpers.CELL_ARRAY_SIZE; +import static org.onap.a1pesimulator.TestHelpers.CELL_PREFIX; +import static org.onap.a1pesimulator.TestHelpers.FIRST_CELL_ID; +import static org.onap.a1pesimulator.TestHelpers.UE_ARRAY_SIZE; +import static org.onap.a1pesimulator.TestHelpers.checkFirstCell; +import static org.onap.a1pesimulator.TestHelpers.checkFirstCellWithStatus; +import static org.onap.a1pesimulator.TestHelpers.checkFirstUserEquipment; + +import java.util.Collection; +import java.util.Set; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.onap.a1pesimulator.data.Topology; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.CellWithStatus; +import org.onap.a1pesimulator.service.cell.RanCellService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@SpringBootTest +@RunWith(SpringRunner.class) +public class CellServiceTest { + + @Autowired + private RanCellService cellService; + + @Test + public void testGetTopology() { + Topology topology = cellService.getTopology(); + + assertNotNull(topology); + + assertEquals(CELL_ARRAY_SIZE, topology.getCells().size()); + assertEquals(UE_ARRAY_SIZE, topology.getUserEquipments().size()); + + checkFirstCell(topology.getCells().iterator().next()); + checkFirstUserEquipment(topology.getUserEquipments().iterator().next()); + } + + @Test + public void testGetCellById() { + CellDetails cellDetails = cellService.getCellById(FIRST_CELL_ID); + checkFirstCell(cellDetails); + } + + @Test + public void testGetCellIds() { + Set<String> ids = cellService.getCellIds(); + assertEquals(CELL_ARRAY_SIZE, ids.size()); + ids.forEach(id -> assertTrue(id.startsWith(CELL_PREFIX))); + } + + @Test + public void testGetCellWithStatus() { + Collection<CellWithStatus> cells = cellService.getAllCellsWithStatus(); + + assertEquals(CELL_ARRAY_SIZE, cells.size()); + checkFirstCellWithStatus(cells.iterator().next()); + } +} diff --git a/src/test/java/org/onap/a1pesimulator/service/RanUeServiceImplTest.java b/src/test/java/org/onap/a1pesimulator/service/RanUeServiceImplTest.java new file mode 100644 index 0000000..6409700 --- /dev/null +++ b/src/test/java/org/onap/a1pesimulator/service/RanUeServiceImplTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service; + +import static org.onap.a1pesimulator.TestHelpers.FIRST_CELL_CONNECTED_UE_ID; +import static org.onap.a1pesimulator.TestHelpers.FIRST_CELL_ID; +import static org.onap.a1pesimulator.TestHelpers.FIRST_UE_HANDOVER_CELL; + +import java.util.Optional; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.service.ue.RanUeServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class RanUeServiceImplTest { + + @Autowired + private RanUeServiceImpl ranUeService; + + @Test + public void testHandover() { + // when + ranUeService.handover(FIRST_CELL_CONNECTED_UE_ID, FIRST_UE_HANDOVER_CELL); + + // then + Optional<UserEquipment> optUserEquipment = ranUeService.getUserEquipment(FIRST_CELL_CONNECTED_UE_ID); + + Assert.assertTrue(optUserEquipment.isPresent()); + UserEquipment userEquipment = optUserEquipment.get(); + Assert.assertEquals(FIRST_UE_HANDOVER_CELL, userEquipment.getCellId()); + + // cleanup + userEquipment.setCellId(FIRST_CELL_ID); + } +} diff --git a/src/test/java/org/onap/a1pesimulator/service/UeServiceTest.java b/src/test/java/org/onap/a1pesimulator/service/UeServiceTest.java new file mode 100644 index 0000000..53c63ce --- /dev/null +++ b/src/test/java/org/onap/a1pesimulator/service/UeServiceTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.onap.a1pesimulator.TestHelpers.FIRST_CELL_CONNECTED_UE_ID; +import static org.onap.a1pesimulator.TestHelpers.FIRST_CELL_ID; +import static org.onap.a1pesimulator.TestHelpers.FIRST_UE_CELL_ID; +import static org.onap.a1pesimulator.TestHelpers.FIRST_UE_HANDOVER_CELL; +import static org.onap.a1pesimulator.TestHelpers.FIRST_UE_ID; +import static org.onap.a1pesimulator.TestHelpers.UE_ARRAY_SIZE; +import static org.onap.a1pesimulator.TestHelpers.checkFirstUserEquipment; + +import java.util.Collection; +import java.util.Optional; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.service.ue.RanUeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@SpringBootTest +@RunWith(SpringRunner.class) +public class UeServiceTest { + + @Autowired + private RanUeService ueService; + + @Test + public void testGetUes() { + Collection<UserEquipment> userEquipments = ueService.getUserEquipments(); + + assertNotNull(userEquipments); + assertEquals(UE_ARRAY_SIZE, userEquipments.size()); + + UserEquipment userEquipment = userEquipments.iterator().next(); + checkFirstUserEquipment(userEquipment); + } + + @Test + public void testGetUeConnectedToCell() { + Collection<UserEquipment> userEquipments = ueService.getUserEquipmentsConnectedToCell(FIRST_CELL_ID); + + assertNotNull(userEquipments); + assertEquals(1, userEquipments.size()); + + UserEquipment userEquipment = userEquipments.iterator().next(); + assertEquals(FIRST_CELL_CONNECTED_UE_ID, userEquipment.getId()); + } + + @Test + public void testGetUeByID() { + Optional<UserEquipment> userEquipmentOpt = ueService.getUserEquipment(FIRST_UE_ID); + + assertNotNull(userEquipmentOpt); + assertTrue(userEquipmentOpt.isPresent()); + + UserEquipment userEquipment = userEquipmentOpt.get(); + checkFirstUserEquipment(userEquipment); + } + + @Test + public void testGetUeByNotCorrectID() { + Optional<UserEquipment> userEquipmentOpt = ueService.getUserEquipment("BAD_ID"); + + assertFalse(userEquipmentOpt.isPresent()); + assertNull(userEquipmentOpt.orElse(null)); + } + + @Test + public void testHandoverFlow() { + boolean canHandover = ueService.canHandover(FIRST_UE_ID, FIRST_UE_HANDOVER_CELL); + assertTrue(canHandover); + + ueService.handover(FIRST_UE_ID, FIRST_UE_HANDOVER_CELL); + + UserEquipment userEquipment = ueService.getUserEquipment(FIRST_UE_ID).orElse(null); + assertNotNull(userEquipment); + assertEquals(FIRST_UE_HANDOVER_CELL, userEquipment.getCellId()); + + ueService.handover(FIRST_UE_ID, FIRST_UE_CELL_ID); + } + + @Test + public void testCantHandoverFlow() { + boolean canHandover = ueService.canHandover(FIRST_UE_ID, "BAD_CELL_ID"); + assertFalse(canHandover); + } +} diff --git a/src/test/java/org/onap/a1pesimulator/service/VesBrokerServiceImplTest.java b/src/test/java/org/onap/a1pesimulator/service/VesBrokerServiceImplTest.java new file mode 100644 index 0000000..5b27529 --- /dev/null +++ b/src/test/java/org/onap/a1pesimulator/service/VesBrokerServiceImplTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service; + +import static org.mockito.Mockito.when; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; +import org.mockito.Mock; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.service.ves.RanVesBrokerService; +import org.onap.a1pesimulator.service.ves.RanVesSender; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.client.RestTemplate; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class VesBrokerServiceImplTest { + + private static final String VES_COLLECTOR_URL = "someProtocol://someVesCollectorIP:someVesCollectorPort/somePath"; + + @Autowired + private RanVesBrokerService vesBrokerService; + @Autowired + private RanVesSender vesSender; + @Autowired + private ObjectMapper mapper; + @Mock + private RestTemplate restTemplate; + + @Before + public void before() { + ReflectionTestUtils.setField(vesSender, "restTemplate", restTemplate); + } + + @Test + public void testStartSendingVes() throws Exception { + ResponseEntity<String> responseEntity = new ResponseEntity<>(HttpStatus.OK); + + when(restTemplate.exchange(ArgumentMatchers.eq(VES_COLLECTOR_URL), ArgumentMatchers.eq(HttpMethod.POST), + ArgumentMatchers.any(HttpEntity.class), ArgumentMatchers.eq(String.class))).thenReturn(responseEntity); + + ResponseEntity<String> response = vesBrokerService.startSendingVesEvents("CustomIdentifier", + loadEventFromFile("VesBrokerControllerTest_pm_ves.json"), 10); + + Assert.assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); + } + + private Event loadEventFromFile(String fileName) throws Exception { + return mapper.readValue(loadFileContent(fileName), Event.class); + } + + private String loadFileContent(String fileName) throws IOException, URISyntaxException { + Path path = Paths.get(VesBrokerServiceImplTest.class.getResource(fileName).toURI()); + return new String(Files.readAllBytes(path)); + } +} diff --git a/src/test/java/org/onap/a1pesimulator/util/ItemsRefresherTest.java b/src/test/java/org/onap/a1pesimulator/util/ItemsRefresherTest.java new file mode 100644 index 0000000..25bdacf --- /dev/null +++ b/src/test/java/org/onap/a1pesimulator/util/ItemsRefresherTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.util; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.onap.a1pesimulator.service.cell.RanCellsHolder; +import org.onap.a1pesimulator.service.ue.RanUeHolder; + +public class ItemsRefresherTest { + + private final RanCellsHolder cellsHolder = mock(RanCellsHolder.class); + private final RanUeHolder ueHolder = mock(RanUeHolder.class); + private final ItemsRefresher refresher = new ItemsRefresher(cellsHolder, ueHolder); + + @Test + public void testHaveNotChanged() { + // given + when(cellsHolder.hasChanged()).thenReturn(false); + when(ueHolder.hasChanged()).thenReturn(false); + + // when + refresher.refresh(); + + // then + verify(cellsHolder).hasChanged(); + verify(cellsHolder, never()).refresh(); + verify(ueHolder).hasChanged(); + verify(ueHolder, never()).refresh(); + } + + @Test + public void testOneHasChanged() { + // given + when(cellsHolder.hasChanged()).thenReturn(false); + when(ueHolder.hasChanged()).thenReturn(true); + + // when + refresher.refresh(); + + // then + verify(cellsHolder).hasChanged(); + verify(cellsHolder, never()).refresh(); + verify(ueHolder).hasChanged(); + verify(ueHolder).refresh(); + } + + @Test + public void testBothHaveChanged() { + // given + when(cellsHolder.hasChanged()).thenReturn(true); + when(ueHolder.hasChanged()).thenReturn(true); + + // when + refresher.refresh(); + + // then + verify(cellsHolder).hasChanged(); + verify(cellsHolder).refresh(); + verify(ueHolder).hasChanged(); + verify(ueHolder).refresh(); + } +} diff --git a/src/test/java/org/onap/a1pesimulator/util/VnfConfigReaderTest.java b/src/test/java/org/onap/a1pesimulator/util/VnfConfigReaderTest.java new file mode 100644 index 0000000..b921755 --- /dev/null +++ b/src/test/java/org/onap/a1pesimulator/util/VnfConfigReaderTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.util; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.onap.a1pesimulator.data.VnfConfig; +import org.onap.a1pesimulator.exception.LackOfConfigException; +import org.onap.a1pesimulator.exception.VesBrokerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class VnfConfigReaderTest { + + @Autowired + private VnfConfigReader vnfConfigReader; + + @Test + public void testGettingVnfConfig() throws LackOfConfigException, VesBrokerException { + VnfConfig vnfConfig = vnfConfigReader.getVnfConfig(); + Assert.assertEquals("someVesCollectorIP", vnfConfig.getVesHost()); + Assert.assertEquals("someVesCollectorPort", vnfConfig.getVesPort()); + Assert.assertEquals("someVesUser", vnfConfig.getVesUser()); + Assert.assertEquals("someVesPassword", vnfConfig.getVesPassword()); + Assert.assertEquals("someVnfId", vnfConfig.getVnfId()); + Assert.assertEquals("someVnfName", vnfConfig.getVnfName()); + } +} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..97c855b --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,32 @@ +# +# Copyright (C) 2021 Samsung Electronics +# 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 +# + +vnf.config.file=src/test/resources/vnf.config +ves.collector.protocol=someProtocol +ves.collector.endpoint=/somePath +ves.pm.maxPoolSize=10 +ves.defaultInterval=10 +ves.defaultFailureDuration=120 + +ves.failing.throughput=1 +ves.failing.latency=500 +# in sec +ves.failing.checkout.delay=15 + +topology.cell.config.file=src/test/resources/cells.json +topology.cell.range=5 +topology.ue.config.file=src/test/resources/ue.json + +refresher.fixed.rate.ms=60000 + +restapi.version=v1
\ No newline at end of file diff --git a/src/test/resources/cells.json b/src/test/resources/cells.json new file mode 100644 index 0000000..bd44bb7 --- /dev/null +++ b/src/test/resources/cells.json @@ -0,0 +1,125 @@ +{ + "cellList": [ + { + "Cell": { + "networkId": "RAN001", + "nodeId": "Chn0000", + "physicalCellId": 0, + "pnfName": "ncserver1", + "sectorNumber": 0, + "latitude": "50.11", + "longitude": "19.98" + }, + "neighbor": [ + { + "nodeId": "Chn0002", + "blacklisted": "false" + }, + { + "nodeId": "Chn0003", + "blacklisted": "false" + }, + { + "nodeId": "Chn0001", + "blacklisted": "false" + } + ] + }, + { + "Cell": { + "networkId": "RAN001", + "nodeId": "Chn0001", + "physicalCellId": 1, + "pnfName": "ncserver1", + "sectorNumber": 0, + "latitude": "50.06", + "longitude": "20.03" + }, + "neighbor": [ + { + "nodeId": "Chn0004", + "blacklisted": "false" + }, + { + "nodeId": "Chn0000", + "blacklisted": "false" + }, + { + "nodeId": "Chn0002", + "blacklisted": "false" + } + ] + }, + { + "Cell": { + "networkId": "RAN001", + "nodeId": "Chn0002", + "physicalCellId": 3, + "pnfName": "ncserver1", + "sectorNumber": 0, + "latitude": "50.06", + "longitude": "19.94" + }, + "neighbor": [ + { + "nodeId": "Chn0004", + "blacklisted": "false" + }, + { + "nodeId": "Chn0000", + "blacklisted": "false" + }, + { + "nodeId": "Chn0003", + "blacklisted": "false" + }, + { + "nodeId": "Chn0001", + "blacklisted": "false" + } + ] + }, + { + "Cell": { + "networkId": "RAN001", + "nodeId": "Chn0003", + "physicalCellId": 4, + "pnfName": "ncserver1", + "sectorNumber": 0, + "latitude": "50.11", + "longitude": "19.88" + }, + "neighbor": [ + { + "nodeId": "Chn0002", + "blacklisted": "false" + }, + { + "nodeId": "Chn0000", + "blacklisted": "false" + } + ] + }, + { + "Cell": { + "networkId": "RAN001", + "nodeId": "Chn0004", + "physicalCellId": 6, + "pnfName": "ncserver1", + "sectorNumber": 0, + "latitude": "50.01", + "longitude": "19.99" + }, + "neighbor": [ + { + "nodeId": "Chn0002", + "blacklisted": "false" + }, + { + "nodeId": "Chn0001", + "blacklisted": "false" + } + ] + } + ] +} diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml new file mode 100644 index 0000000..1a162e3 --- /dev/null +++ b/src/test/resources/logback.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2021 Samsung Electronics + ~ 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 + --> + +<configuration> + + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</Pattern> + </layout> + </appender> + + <root level="INFO"> + <appender-ref ref="STDOUT"/> + </root> + +</configuration> diff --git a/src/test/resources/org/onap/a1pesimulator/service/VesBrokerControllerTest_pm_ves.json b/src/test/resources/org/onap/a1pesimulator/service/VesBrokerControllerTest_pm_ves.json new file mode 100644 index 0000000..8c59897 --- /dev/null +++ b/src/test/resources/org/onap/a1pesimulator/service/VesBrokerControllerTest_pm_ves.json @@ -0,0 +1,35 @@ +{ + "event": { + "commonEventHeader": { + "version": "4.0.1", + "vesEventListenerVersion": "7.0.1", + "domain": "measurement", + "eventName": "Measurement_vIsbcMmc", + "eventId": "measurement0000259", + "sequence": 3, + "priority": "Normal", + "reportingEntityId": "cc305d54-75b4-431b-adb2-eb6b9e541234", + "reportingEntityName": "ibcx0001vm002oam001", + "sourceId": "de305d54-75b4-431b-adb2-eb6b9e546014", + "sourceName": "ibcx0001vm002ssc001", + "nfVendorName": "Samsung", + "nfNamingCode": "ibcx", + "nfcNamingCode": "ssc", + "startEpochMicrosec": 1413378172000000, + "lastEpochMicrosec": 1413378172000000, + "timeZoneOffset": "UTC-05:30" + }, + "measurementFields": { + "additionalMeasurements": [ + { + "name": "latency", + "hashMap": { + "value": "[[100-150]]" + } + } + ], + "measurementInterval": 5, + "measurementFieldsVersion": "4.0" + } + } +} diff --git a/src/test/resources/ue.json b/src/test/resources/ue.json new file mode 100644 index 0000000..437df08 --- /dev/null +++ b/src/test/resources/ue.json @@ -0,0 +1,20 @@ +[ + { + "id": "mobile_samsung_s10", + "latitude": "50.09", + "longitude": "19.94", + "cellId": "Chn0000" + }, + { + "id": "mobile_samsung_s20", + "latitude": "50.05", + "longitude": "19.95", + "cellId": "Chn0002" + }, + { + "id": "emergency_police_111", + "latitude": "50.035", + "longitude": "19.97", + "cellId": "Chn0002" + } +] diff --git a/src/test/resources/vnf.config b/src/test/resources/vnf.config new file mode 100644 index 0000000..09a2553 --- /dev/null +++ b/src/test/resources/vnf.config @@ -0,0 +1,7 @@ +vesHost=someVesCollectorIP +vesPort=someVesCollectorPort +vesUser=someVesUser +vesPassword=someVesPassword +vnfId=someVnfId +vnfName=someVnfName +unknownProperty=doNotFail @@ -0,0 +1,44 @@ +[tox] +minversion = 3.2.0 +envlist = json,yaml,py,md +skipsdist = true +requires = pip >= 8 + +[testenv] +basepython = python3 +whitelist_externals = + git + bash +deps = + coala-bears + nodeenv + +[testenv:json] +commands_pre = + /bin/sh -c "git --no-pager diff HEAD HEAD^ --name-only '*.json' > /tmp/.coalist_json" +commands = +# '\ ' at the end of command is needed for a case where above command returns empty list (it adds empty file +# parameter to '--files' opt + /bin/bash -c "coala --non-interactive --disable-caching --no-autoapply-warn json --files $(</tmp/.coalist_json) \ " + +[testenv:yaml] +commands_pre = + /bin/sh -c "git --no-pager diff HEAD HEAD^ --name-only '*.yaml' '*.yml' > /tmp/.coalist_yaml" +commands = +# '\ ' at the end of command is needed for a case where above command returns empty list (it adds empty file +# parameter to '--files' opt + /bin/bash -c "coala --non-interactive --disable-caching --no-autoapply-warn yaml --files $(</tmp/.coalist_yaml) \ " + +[testenv:py] +commands_pre = + /bin/sh -c "git --no-pager diff HEAD HEAD^ --name-only '*.py' > /tmp/.coalist_py" +commands = + /bin/bash -c "coala --non-interactive --disable-caching --no-autoapply-warn py --files $(</tmp/.coalist_py) \ " + +[testenv:md] +commands_pre = + nodeenv -p --verbose + npm install --global remark-cli + /bin/sh -c "git --no-pager diff HEAD HEAD^ --name-only '*.md' > /tmp/.coalist_md" +commands = + /bin/bash -c "coala --non-interactive --disable-caching --no-autoapply-warn md --files $(</tmp/.coalist_md) \ " |