Environment variables:
* **FLASK_SERVER_NAME**: Address that the xapp_onboarder is listening on. Default http://0.0.0.0:8888
* **CHART_REPO_URL**: helm chart repo URL. Default http://0.0.0.0:8080
-* **DBAAS_SERVICE_HOST**: DBAAS service host URL that will be injected into the xApp config
-* **DBAAS_MASTER_NAME**: DBAAS_HA sentinel master URL that will be injected into the xApp config
-* **DBAAS_SERVICE_SENTINEL_PORT**: DBAAS_HA sentinel port that will be injected into the xApp config
-* **DBAAS_SERVICE_PORT**: DBAAS service port that will be injected into the xApp config
## Configurations
Environment variables:
## Run the API server
```bash
-python3 -m xapp_onboarder.server.server
-```
-Or we recommend you can set up the symbolic link in your PATH
-```bash
-ln -s $(pip show xapp_onboarder | grep Location | awk '{printf $2 "/xapp_onboarder/xapp_onboarder"}') /usr/local/bin/xapp_onboarder
-```
-Then you can run the server
-```bash
xapp_onboarder
```
## Run the CLI tool
```bash
-python3 -m xapp_onboarder.server.cli
-```
-Or we recommend you can set up the symbolic link in your PATH
-```bash
-ln -s $(pip show xapp_onboarder | grep Location | awk '{printf $2 "/xapp_onboarder/cli"}') /usr/local/bin/cli
-```
-Then you can run the server
-```bash
cli
```
# limitations under the License. #
################################################################################
-schema_file = {
+controls_schema_file = {
"$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "http://example.com/root.json",
+ "$id": "#/controls",
"type": "object",
- "title": "The Root Schema",
+ "title": "Controls Section Schema",
"required": [
- "local",
- "logger",
- "rmr",
- "db",
- "controls",
- "metrics"
+ "test"
],
"properties": {
- "local": {
- "$id": "#/properties/local",
- "type": "object",
- "title": "The Local Schema",
- "required": [
- "host"
- ],
- "properties": {
- "host": {
- "$id": "#/properties/local/properties/host",
- "type": "string",
- "title": "The Host Schema",
- "default": "",
- "examples": [
- ":8080"
- ],
- "pattern": "^(.*)$"
- }
- }
- },
- "logger": {
- "$id": "#/properties/logger",
- "type": "object",
- "title": "The Logger Schema",
- "required": [
- "level"
- ],
- "properties": {
- "level": {
- "$id": "#/properties/logger/properties/level",
- "type": "integer",
- "title": "The Level Schema",
- "default": 0,
- "examples": [
- 3
- ]
- }
- }
- },
- "rmr": {
- "$id": "#/properties/rmr",
- "type": "object",
- "title": "The Rmr Schema",
- "required": [
- "protPort",
- "maxSize",
- "numWorkers",
- "rxMessages",
- "txMessages"
- ],
- "properties": {
- "protPort": {
- "$id": "#/properties/rmr/properties/protPort",
- "type": "string",
- "title": "The Protport Schema",
- "default": "",
- "examples": [
- "tcp:4560"
- ],
- "pattern": "^(.*)$"
- },
- "maxSize": {
- "$id": "#/properties/rmr/properties/maxSize",
- "type": "integer",
- "title": "The Maxsize Schema",
- "default": 0,
- "examples": [
- 2072
- ]
- },
- "numWorkers": {
- "$id": "#/properties/rmr/properties/numWorkers",
- "type": "integer",
- "title": "The Numworkers Schema",
- "default": 0,
- "examples": [
- 1
- ]
- },
- "rxMessages": {
- "$id": "#/properties/rmr/properties/rxMessages",
- "type": "array",
- "title": "The Rxmessages Schema",
- "items": {
- "$id": "#/properties/rmr/properties/rxMessages/items",
- "type": "string",
- "title": "The Items Schema",
- "default": "",
- "examples": [
- "RIC_SUB_RESP",
- "RIC_SUB_FAILURE",
- "RIC_SUB_DEL_RESP",
- "RIC_SUB_DEL_FAILURE",
- "RIC_INDICATION"
- ],
- "pattern": "^(.*)$"
- }
- },
- "txMessages": {
- "$id": "#/properties/rmr/properties/txMessages",
- "type": "array",
- "title": "The Txmessages Schema",
- "items": {
- "$id": "#/properties/rmr/properties/txMessages/items",
- "type": "string",
- "title": "The Items Schema",
- "default": "",
- "examples": [
- "RIC_SUB_REQ",
- "RIC_SUB_DEL_REQ",
- "RIC_SGNB_ADDITION_REQ",
- "RIC_SGNB_ADDITION_ACK",
- "RIC_SGNB_ADDITION_REJECT",
- "RIC_SGNB_MOD_REQUEST",
- "RIC_SGNB_MOD_REQUEST_ACK",
- "RIC_SGNB_MOD_REQUEST_REJECT",
- "RIC_SGNB_MOD_REQUIRED",
- "RIC_SGNB_MOD_CONFIRM",
- "RIC_SGNB_MOD_REFUSE",
- "RIC_SGNB_RECONF_COMPLETE",
- "RIC_SGNB_RELEASE_REQUEST",
- "RIC_SGNB_RELEASE_CONFIRM",
- "RIC_SGNB_RELEASE_REQUIRED",
- "RIC_SGNB_RELEASE_REQUEST_ACK",
- "RIC_SECONDARY_RAT_DATA_USAGE_REPORT",
- "RIC_SN_STATUS_TRANSFER",
- "RIC_RRC_TRANSFER",
- "RIC_UE_CONTEXT_RELEASE"
- ],
- "pattern": "^(.*)$"
- }
- }
- }
- },
- "db": {
- "$id": "#/properties/db",
- "type": "object",
- "title": "The Db Schema",
- "required": [
- "host",
- "port",
- "namespaces"
- ],
- "properties": {
- "host": {
- "$id": "#/properties/db/properties/host",
- "type": "string",
- "title": "The Host Schema",
- "default": "",
- "examples": [
- "localhost"
- ],
- "pattern": "^(.*)$"
- },
- "port": {
- "$id": "#/properties/db/properties/port",
- "type": "integer",
- "title": "The Port Schema",
- "default": 0,
- "examples": [
- 6379
- ]
- },
- "namespaces": {
- "$id": "#/properties/db/properties/namespaces",
- "type": "array",
- "title": "The Namespaces Schema",
- "items": {
- "$id": "#/properties/db/properties/namespaces/items",
- "type": "string",
- "title": "The Items Schema",
- "default": "",
- "examples": [
- "sdl",
- "rnib"
- ],
- "pattern": "^(.*)$"
- }
- }
- }
- },
- "controls": {
- "$id": "#/properties/controls",
- "type": "object",
- "title": "The Controls Schema",
- "required": [
- "active",
- "requestorId",
- "ranFunctionId",
- "ricActionId",
- "interfaceId"
- ],
- "properties": {
- "active": {
- "$id": "#/properties/controls/properties/active",
- "type": "boolean",
- "title": "The Active Schema",
- "default": False,
- "examples": [
- True
- ]
- },
- "requestorId": {
- "$id": "#/properties/controls/properties/requestorId",
- "type": "integer",
- "title": "The Requestorid Schema",
- "default": 0,
- "examples": [
- 66
- ]
- },
- "ranFunctionId": {
- "$id": "#/properties/controls/properties/ranFunctionId",
- "type": "integer",
- "title": "The Ranfunctionid Schema",
- "default": 0,
- "examples": [
- 1
- ]
- },
- "ricActionId": {
- "$id": "#/properties/controls/properties/ricActionId",
- "type": "integer",
- "title": "The Ricactionid Schema",
- "default": 0,
- "examples": [
- 0
- ]
- },
- "interfaceId": {
- "$id": "#/properties/controls/properties/interfaceId",
- "type": "object",
- "title": "The Interfaceid Schema",
- "required": [
- "globalENBId"
- ],
- "properties": {
- "globalENBId": {
- "$id": "#/properties/controls/properties/interfaceId/properties/globalENBId",
- "type": "object",
- "title": "The Globalenbid Schema",
- "required": [
- "plmnId",
- "eNBId"
- ],
- "properties": {
- "plmnId": {
- "$id": "#/properties/controls/properties/interfaceId/properties/globalENBId/properties/plmnId",
- "type": "string",
- "title": "The Plmnid Schema",
- "default": "",
- "examples": [
- "310150"
- ],
- "pattern": "^(.*)$"
- },
- "eNBId": {
- "$id": "#/properties/controls/properties/interfaceId/properties/globalENBId/properties/eNBId",
- "type": "integer",
- "title": "The Enbid Schema",
- "default": 0,
- "examples": [
- 202251
- ]
- }
- }
- }
- }
- }
- }
- },
- "metrics": {
- "$id": "#/properties/metrics",
- "type": "array",
- "title": "The Metrics Schema",
- "items": {
- "$id": "#/properties/metrics/items",
- "type": "object",
- "title": "The Items Schema",
- "required": [
- "objectName",
- "objectInstance",
- "name",
- "type",
- "description"
- ],
- "properties": {
- "objectName": {
- "$id": "#/properties/metrics/items/properties/objectName",
- "type": "string",
- "title": "The Objectname Schema",
- "default": "",
- "examples": [
- "UEEventStreamingCounters"
- ],
- "pattern": "^(.*)$"
- },
- "objectInstance": {
- "$id": "#/properties/metrics/items/properties/objectInstance",
- "type": "string",
- "title": "The Objectinstance Schema",
- "default": "",
- "examples": [
- "SgNBAdditionRequest"
- ],
- "pattern": "^(.*)$"
- },
- "name": {
- "$id": "#/properties/metrics/items/properties/name",
- "type": "string",
- "title": "The Name Schema",
- "default": "",
- "examples": [
- "SgNBAdditionRequest"
- ],
- "pattern": "^(.*)$"
- },
- "type": {
- "$id": "#/properties/metrics/items/properties/type",
- "type": "string",
- "title": "The Type Schema",
- "default": "",
- "examples": [
- "counter"
- ],
- "pattern": "^(.*)$"
- },
- "description": {
- "$id": "#/properties/metrics/items/properties/description",
- "type": "string",
- "title": "The Description Schema",
- "default": "",
- "examples": [
- "The total number of SG addition request events processed"
- ],
- "pattern": "^(.*)$"
- }
- }
- }
+ "test": {
+ "$id": "#/controls/test",
+ "type": "string",
+ "title": "test",
+ "default": "test",
+ "examples": [
+ "test"
+ ]
}
}
}
config_file = {
"xapp_name": "test_xapp",
"version": "1.0.0",
- "containers": [{
- "name": "test1",
- "image": {
- "registry": "test_repo",
- "name": "test_name",
- "tag": "test_tag"
- },
- "command": "test command"
- },
+ "containers": [
{
- "name": "test2",
+ "name": "mcxapp",
"image": {
- "registry": "test2_repo",
- "name": "test2_name",
- "tag": "test2:_tag"
+ "registry": "nexus3.o-ran-sc.org:10002",
+ "name": "o-ran-sc/ric-app-mc",
+ "tag": "1.0.2"
},
- "command": "test2 command"
- }],
- "local": {
- "host": ":8080"
- },
- "logger": {
- "level": 3
+ "command": "/playpen/bin/container_start.sh"
+ }
+ ],
+ "livenessProbe": {
+ "exec": {
+ "command": ["/usr/local/bin/health_ck"]
+ },
+ "initialDelaySeconds": 5,
+ "periodSeconds": 15
},
- "db": {
- "host": "localhost",
- "port": 6379,
- "namespaces": ["sdl", "rnib"]
+ "readinessProbe": {
+ "httpGet": {
+ "path": "ric/v1/health/alive",
+ "port": 8080
+ },
+ "initialDelaySeconds": 5,
+ "periodSeconds": 15
},
- "controls": {
- "active": True,
- "requestorId": 66,
- "ranFunctionId": 1,
- "ricActionId": 0,
- "interfaceId": {
- "globalENBId": {
- "plmnId": "310150",
- "eNBId": 202251
+ "messaging": {
+ "ports": [
+ {
+ "name": "http",
+ "container": "mcxapp",
+ "port": 8080,
+ "description": "http service"
+ },
+ {
+ "name": "rmr_data",
+ "container": "mcxapp",
+ "port": 4560,
+ "description": "rmr data port for mcxapp"
+ },
+ {
+ "name": "rmr_route",
+ "container": "mcxapp",
+ "port": 4561,
+ "description": "rmr route port for mcxapp"
}
- }
- },
- "rmr": {
- "protPort": "tcp:4560",
- "maxSize": 10000,
+ ],
+ "maxSize": 2072,
"numWorkers": 1,
+ "txMessages": [
+ "RIC_SUB_REQ",
+ "RIC_SUB_DEL_REQ"
+ ],
"rxMessages": [
"RIC_SUB_RESP",
"RIC_SUB_FAILURE",
"RIC_SUB_DEL_RESP",
- "RIC_SUB_DEL_FAILURE",
"RIC_INDICATION"
],
- "txMessages": [
- "RIC_SUB_REQ",
- "RIC_SUB_DEL_REQ",
- "RIC_SGNB_ADDITION_REQ",
- "RIC_SGNB_ADDITION_ACK",
- "RIC_SGNB_ADDITION_REJECT",
- "RIC_SGNB_MOD_REQUEST",
- "RIC_SGNB_MOD_REQUEST_ACK",
- "RIC_SGNB_MOD_REQUEST_REJECT",
- "RIC_SGNB_MOD_REQUIRED",
- "RIC_SGNB_MOD_CONFIRM",
- "RIC_SGNB_MOD_REFUSE",
- "RIC_SGNB_RELEASE_REQUEST",
- "RIC_SGNB_RELEASE_CONFIRM",
- "RIC_SGNB_RELEASE_REQUIRED",
- "RIC_SGNB_RELEASE_REQUEST_ACK",
- "RIC_SGNB_RECONF_COMPLETE",
- "RIC_UE_CONTEXT_RELEASE",
- "RIC_RRC_TRANSFER",
- "RIC_SECONDARY_RAT_DATA_USAGE_REPORT",
- "RIC_SN_STATUS_TRANSFER"
- ]
+ "policies": [1, 2]
+ },
+ "controls": {
+ "test": "test"
},
"metrics": [
{
"name": "SgNBAdditionRequestAcknowledge",
"type": "counter",
"description": "The total number of SG addition request acknowledge events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SgNBAdditionRequestReject",
- "name": "SgNBAdditionRequestReject",
- "type": "counter",
- "description": "The total number of SG addition request reject events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SgNBModificationRequest",
- "name": "SgNBModificationRequest",
- "type": "counter",
- "description": "The total number of SG modification request events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SgNBModificationRequestAcknowledge",
- "name": "SgNBModificationRequestAcknowledge",
- "type": "counter",
- "description": "The total number of SG modification request acknowledge events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SgNBModificationRequestReject",
- "name": "SgNBModificationRequestReject",
- "type": "counter",
- "description": "The total number of SG modification request reject events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SgNBModificationRequired",
- "name": "SgNBModificationRequired",
- "type": "counter",
- "description": "The total number of SG modification required events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SgNBModificationConfirm",
- "name": "SgNBModificationConfirm",
- "type": "counter",
- "description": "The total number of SG modification confirm events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SgNBModificationRefuse",
- "name": "SgNBModificationRefuse",
- "type": "counter",
- "description": "The total number of SG modification refuse events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SgNBReleaseRequest",
- "name": "SgNBReleaseRequest",
- "type": "counter",
- "description": "The total number of SG release request events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SgNBReleaseRequestAcknowledge",
- "name": "SgNBReleaseRequestAcknowledge",
- "type": "counter",
- "description": "The total number of SG release request acknowledge events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SgNBReleaseRequestReject",
- "name": "SgNBReleaseRequestReject",
- "type": "counter",
- "description": "The total number of SG release request reject events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SgNBReleaseRequired",
- "name": "SgNBReleaseRequired",
- "type": "counter",
- "description": "The total number of SG release required events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SgNBReleasenConfirm",
- "name": "SgNBReleasenConfirm",
- "type": "counter",
- "description": "The total number of SG release confirm events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SgNBReconfigurationComplete",
- "name": "SgNBReconfigurationComplete",
- "type": "counter",
- "description": "The total number of SG reconfiguration complete events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "UEContextRelease",
- "name": "UEContextRelease",
- "type": "counter",
- "description": "The total number of SG UE context release events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "RRCTransfer",
- "name": "RRCTransfer",
- "type": "counter",
- "description": "The total number of SG RRC transfers events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SNStatusTransfer",
- "name": "SNStatusTransfer",
- "type": "counter",
- "description": "The total number of SG SN status transfers events processed"
- },
- {
- "objectName": "UEEventStreamingCounters",
- "objectInstance": "SecondaryRATDataUsageReport",
- "name": "SecondaryRATDataUsageReport",
- "type": "counter",
- "description": "The total number of SG secondary RAT data usage reports events processed"
- },
- {
- "objectName": "RMRCounters",
- "objectInstance": "Transmitted",
- "name": "Transmitted",
- "type": "counter",
- "description": "The total number of RMR messages transmited"
- },
- {
- "objectName": "RMRCounters",
- "objectInstance": "Received",
- "name": "Received",
- "type": "counter",
- "description": "The total number of RMR messages received"
- },
- {
- "objectName": "RMRCounters",
- "objectInstance": "TransmitError",
- "name": "TransmitError",
- "type": "counter",
- "description": "The total number of RMR messages transmission errors"
- },
- {
- "objectName": "RMRCounters",
- "objectInstance": "ReceiveError",
- "name": "ReceiveError",
- "type": "counter",
- "description": "The total number of RMR messages receive errors"
- },
- {
- "objectName": "SDLounters",
- "objectInstance": "Stored",
- "name": "Stored",
- "type": "counter",
- "description": "The total number of stored SDL transactions"
- },
- {
- "objectName": "SDLounters",
- "objectInstance": "StoreError",
- "name": "StoreError",
- "type": "counter",
- "description": "The total number of SDL store errors"
}
]
}
mock_json_body_url = {
'config-file.json_url': 'http://0.0.0.0:8080/config-file.json',
- 'schema.json_url': 'http://0.0.0.0:8080/schema.json'
+ 'controls-schema.json_url': 'http://0.0.0.0:8080/schema.json'
+}
+
+mock_json_body_url_without_controls = {
+ 'config-file.json_url': 'http://0.0.0.0:8080/config-file.json'
}
mock_json_body = {
"config-file.json": config_file,
- "schema.json": schema_file
+ "controls-schema.json": controls_schema_file
}
-helm_repo_index_response={'apiVersion': 'v1',
- 'entries':{
- 'test_xapp':[{
- 'apiVersion': 'v1',
- 'appVersion': '1.0',
- 'created': '2020-03-12T19:10:17.178396719Z',
- 'description': 'test xApp Helm Chart',
- 'digest': 'd77dfb3f008e5174e90d79bfe982ef85b5dc5930141f6a1bd9995b2fa35',
- 'name': 'test_xapp',
- 'urls':['charts/test-1.0.0.tgz'],
- 'version': '1.0.0'
- }]
- },
- 'generated': '2020-03-16T16:54:44Z',
- 'serverInfo':{}
- }
+mock_json_body_without_controls = {
+ "config-file.json": config_file
+}
+helm_repo_index_response = {'apiVersion': 'v1',
+ 'entries': {
+ 'test_xapp': [{
+ 'apiVersion': 'v1',
+ 'appVersion': '1.0',
+ 'created': '2020-03-12T19:10:17.178396719Z',
+ 'description': 'test xApp Helm Chart',
+ 'digest': 'd77dfb3f008e5174e90d79bfe982ef85b5dc5930141f6a1bd9995b2fa35',
+ 'name': 'test_xapp',
+ 'urls': ['charts/test-1.0.0.tgz'],
+ 'version': '1.0.0'
+ }]
+ },
+ 'generated': '2020-03-16T16:54:44Z',
+ 'serverInfo': {}
+ }
import os
import tarfile
from flask import Flask, jsonify, send_file
-from tests.constants import config_file, schema_file, helm_repo_index_response
+from tests.constants import config_file, controls_schema_file, helm_repo_index_response
from xapp_onboarder.server import settings
def get_schema():
- return schema_file, 200
+ return controls_schema_file, 200
@app.route('/config-file.json', methods=['GET'])
def get_config_file():
################################################################################
from http import HTTPStatus
-from tests.constants import mock_json_body, mock_json_body_url, helm_repo_index_response
+from tests.constants import mock_json_body, mock_json_body_url_without_controls, mock_json_body_url, mock_json_body_without_controls, helm_repo_index_response
def test_health(client):
assert response.content_type == 'application/json', 'Content type error'
assert response.json == {'status': 'Created'}, 'Onboard failed'
+def test_onboard_without_controls_post(client):
+ url = '/api/v1/onboard'
+ response = client.post(url, json=mock_json_body_without_controls)
+ assert response.status_code == HTTPStatus.BAD_REQUEST, 'Wrong status code'
+ assert response.content_type == 'application/json', 'Content type error'
+ assert response.json == {'error_message': "'__empty_control_section__' is a required property",
+ 'error_source': 'config-file.json',
+ 'status': 'Input payload validation failed'}, 'Onboard failed'
+
+
def test_onboard_download_post(client):
url = '/api/v1/onboard/download'
response = client.post(url, json=mock_json_body_url)
assert response.status_code == HTTPStatus.CREATED, 'Wrong status code'
assert response.content_type == 'application/json', 'Content type error'
- assert response.json == {'status': 'Created'}, 'Onboard failed'
\ No newline at end of file
+ assert response.json == {'status': 'Created'}, 'Onboard failed'
+
+
+def test_onboard_download_without_controls_post(client):
+ url = '/api/v1/onboard/download'
+ response = client.post(url, json=mock_json_body_url_without_controls)
+ assert response.status_code == HTTPStatus.BAD_REQUEST, 'Wrong status code'
+ assert response.content_type == 'application/json', 'Content type error'
+ assert response.json == {'error_message': "'__empty_control_section__' is a required property",
+ 'error_source': 'config-file.json',
+ 'status': 'Input payload validation failed'}, 'Onboard failed'
import os
import shutil
from xapp_onboarder.helm_controller.xApp_builder import xApp, xAppError
-from tests.constants import config_file, schema_file
+from tests.constants import config_file, controls_schema_file
from xapp_onboarder.server import settings
if os.path.exists(chart_workspace_path):
shutil.rmtree(chart_workspace_path)
- xapp = xApp(config_file, schema_file)
+ xapp = xApp(config_file, controls_schema_file)
xapp.package_chart()
assert os.path.isfile(chart_workspace_path + '/test_xapp-1.0.0.tgz'), 'xApp packaging error'
setenv =
PYTHONUNBUFFERED = 1
CHART_REPO_URL = http://0.0.0.0:8080
- DBAAS_SERVICE_HOST = test_dbaas_host
- DBAAS_MASTER_NAME = test_master_name
- DBAAS_SERVICE_SENTINEL_PORT = 29701
- DBAAS_SERVICE_PORT = 3710
MOCK_TEST_MODE = True
lf: CHART_WORKSPACE_PATH = {toxworkdir}/.tmp/xapp_onboarder
lf: MOCK_TEST_HELM_REPO_TEMP_DIR = {toxworkdir}/.tmp/helm_repo
Onboard xApp with the xApp descriptor and its scehma included in the Json body
"""
config_file = request.json.get('config-file.json')
- schema_file = request.json.get('schema.json')
+ controls_schema_file = request.json.get('controls-schema.json')
- return onboard(config_file, schema_file)
+ return onboard(config_file, controls_schema_file)
@ns.route('/download')
Onboard xApp with xApp descriptor and schema downloading URLs
"""
config_file_url = request.json.get('config-file.json_url')
- schema_url = request.json.get('schema.json_url')
+ controls_schema_url = request.json.get('controls-schema.json_url')
- return download_config_and_schema_and_onboard(config_file_url, schema_url)
+ return download_config_and_schema_and_onboard(config_file_url, controls_schema_url)
'version': fields.String(description='Version of the xApp chart', required=True,
pattern='^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'),
}), required=True),
- 'schema.json': fields.Raw(description='Schema file body', required=True),
+ 'controls-schema.json': fields.Raw(description='Controls schema file body', required=False),
})
xapp_descriptor_download_post = api.model('descriptor_remote', {
'config-file.json_url': fields.Url(description='URL to download the config-file.json file', absolute=True, required=True),
- 'schema.json_url': fields.Url(description='URL to download the schema.json file', absolute=True, required=True),
+ 'controls-schema.json_url': fields.Url(description='URL to download the controls schema.json file', absolute=True, required=False),
})
import logging
import json
+import copy
from jsonschema import ValidationError, SchemaError
from jsonschema import validate, Draft7Validator
from xapp_onboarder.helm_controller.xApp_builder import xApp, xAppError
from xapp_onboarder.server import settings
from xapp_onboarder.repo_manager.repo_manager import requests_retry_session, repo_manager
from xapp_onboarder.api.models.response_models import error_message_model, response, status_message_model
-
+from xapp_onboarder.helm_controller.xapp_schema import schema as xapp_schema
log = logging.getLogger(__name__)
-def onboard(config_file, schema_file):
+def onboard(config_file, controls_schema_file):
if not repo_manager.is_repo_ready():
response_message = response(model=error_message_model, status_code=500,
error_source="xapp_onboarder",
status="Service not ready.")
return response_message.get_return()
+ schema_file = copy.deepcopy(xapp_schema)
+
+ if controls_schema_file:
+ schema_file["properties"]["controls"] = controls_schema_file
+
try:
Draft7Validator.check_schema(schema_file)
validate(config_file, schema_file)
return response(model=status_message_model, status_code=201, status="Created").get_return()
-def download_config_and_schema_and_onboard(config_file_url, schema_url):
+def download_config_and_schema_and_onboard(config_file_url, controls_schema_url):
if not repo_manager.is_repo_ready():
response_message = response(model=error_message_model, status_code=500,
error_source="xapp_onboarder",
return response_message.get_return()
config_file = json.loads(response_content.content)
- try:
- response_content = session.get(schema_url, timeout=settings.HTTP_TIME_OUT)
- except Exception as err:
- log.error(err.message)
- response_message = response(model=error_message_model, status_code=500,
- error_source="schema.json",
- error_message=err.message,
- status="Downloading schema.json failed")
- return response_message.get_return()
- else:
- if response_content.status_code != 200:
- error_message = "Wrong response code. {}, {}".format(response_content.status_code, response_content.content.decode("utf-8"))
- log.error(error_message)
+ controls_schema_file = None
+ if controls_schema_url:
+ try:
+ response_content = session.get(controls_schema_url, timeout=settings.HTTP_TIME_OUT)
+ except Exception as err:
+ log.error(err.message)
response_message = response(model=error_message_model, status_code=500,
error_source="schema.json",
- error_message=error_message,
+ error_message=err.message,
status="Downloading schema.json failed")
return response_message.get_return()
- schema_file = json.loads(response_content.content)
+ else:
+ if response_content.status_code != 200:
+ error_message = "Wrong response code. {}, {}".format(response_content.status_code, response_content.content.decode("utf-8"))
+ log.error(error_message)
+ response_message = response(model=error_message_model, status_code=500,
+ error_source="schema.json",
+ error_message=error_message,
+ status="Downloading schema.json failed")
+ return response_message.get_return()
+ controls_schema_file = json.loads(response_content.content)
+
- return onboard(config_file, schema_file)
+ return onboard(config_file, controls_schema_file)
outputfile.write(indented_probe_definition_yaml)
-
def append_config_to_values_yaml(self):
with open(self.chart_workspace_path + '/' + self.chart_name + '/values.yaml', 'a') as outputfile:
yaml.dump(self.config_file, outputfile, default_flow_style=False)
- def append_env_to_config_map(self):
- with open(self.chart_workspace_path + '/' + self.chart_name + '/templates/appenv.yaml', 'a') as outputfile:
- append = {}
- if settings.DBAAS_MASTER_NAME:
- master_name = settings.DBAAS_MASTER_NAME
- service_host = settings.DBAAS_SERVICE_HOST
- sentinel_port = settings.DBAAS_SERVICE_SENTINEL_PORT
- if not service_host:
- raise xAppError(
- "Internal failure. Cannot find environment variable 'DBAAS_SERVICE_HOST'. (Caused by: Misconfiguration of temp deployment)", 500)
- if not sentinel_port:
- raise xAppError(
- "Internal failure. Cannot find environment variable 'DBAAS_SERVICE_SENTINEL_PORT'. (Caused by: Misconfiguration of temp deployment)", 500)
-
- append['DBAAS_MASTER_NAME'] = master_name
- append['DBAAS_SERVICE_HOST'] = service_host
- append['DBAAS_SERVICE_SENTINEL_PORT'] = sentinel_port
- elif settings.DBAAS_SERVICE_HOST:
- service_host = settings.DBAAS_SERVICE_HOST
- service_port = settings.DBAAS_SERVICE_PORT
- if not service_port:
- raise xAppError(
- "Internal failure. Cannot find environment variable 'DBAAS_SERVICE_PORT'. (Caused by: Misconfiguration of temp deployment)", 500)
- append['DBAAS_SERVICE_HOST'] = service_host
- append['DBAAS_SERVICE_PORT'] = service_port
- else:
- raise xAppError(
- "Internal failure. Cannot find environment variable 'DBAAS_SERVICE_HOST' or 'DBAAS_MASTER_NAME'. (Caused by: Misconfiguration of temp deployment)",
- 500)
- output_yaml = yaml.dump(append)
- indented_output_yaml = indent(output_yaml, 2)
- outputfile.write(indented_output_yaml)
-
-
def change_chart_name_version(self):
with open(self.chart_workspace_path + '/' + self.chart_name + '/Chart.yaml', 'r') as inputfile:
self.chart_yaml = yaml.load(inputfile, Loader=yaml.FullLoader)
def package_chart(self):
self.append_config_to_config_map()
self.append_config_to_values_yaml()
- self.append_env_to_config_map()
self.add_probes_to_deployment()
self.change_chart_name_version()
self.helm_lint()
--- /dev/null
+################################################################################
+# Copyright (c) 2020 AT&T Intellectual Property. #
+# #
+# 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. #
+################################################################################
+
+schema = {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "http://o-ran-sc.org/xapp_root.json",
+ "type": "object",
+ "title": "The xApp Root Schema",
+ "required": [
+ "xapp_name",
+ "version",
+ "containers"
+ ],
+ "properties": {
+ "xapp_name": {
+ "$id": "#/properties/xapp_name",
+ "type": "string",
+ "title": "The xApp Name",
+ "default": "xapp",
+ "examples": [
+ "example_xapp"
+ ]
+ },
+ "version": {
+ "$id": "#/properties/version",
+ "type": "string",
+ "title": "The xApp version",
+ "default": "1.0.0",
+ "examples": [
+ "1.0.0"
+ ],
+ "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"
+ },
+ "containers": {
+ "$id": "#/properties/containers",
+ "type": "array",
+ "title": "The Container Schema",
+ "items": {
+ "$id": "#/properties/containers/items",
+ "type": "object",
+ "title": "The Container Items Schema",
+ "required": [
+ "name",
+ "image"
+ ],
+ "properties": {
+ "name": {
+ "$id": "#/properties/containers/items/properties/name",
+ "type": "string",
+ "title": "The xApp Container Name",
+ "default": "xapp",
+ "examples": [
+ "xapp"
+ ]
+ },
+ "image": {
+ "$id": "#/properties/containers/items/properties/image",
+ "type": "object",
+ "title": "The Container Image",
+ "required": [
+ "registry",
+ "name",
+ "tag"
+ ],
+ "properties": {
+ "registry": {
+ "$id": "#/properties/containers/items/properties/image/properties/registry",
+ "type": "string",
+ "title": "The xApp Image Registry",
+ "default": "nexus3.o-ran-sc.org:10002",
+ "examples": [
+ "nexus3.o-ran-sc.org:10002"
+ ],
+ "pattern": "^[A-Za-z0-9\\.-]{1,}\\.[A-Za-z]{1,}(?:\\:\\d+)?$"
+ },
+ "name": {
+ "$id": "#/properties/containers/items/properties/image/properties/name",
+ "type": "string",
+ "title": "The xApp Image Name",
+ "default": "xapp",
+ "examples": [
+ "xapp"
+ ]
+ },
+ "tag": {
+ "$id": "#/properties/containers/items/properties/image/properties/tag",
+ "type": "string",
+ "title": "The xApp Image Tag",
+ "default": "latest",
+ "examples": [
+ "latest"
+ ]
+ }
+ }
+ },
+ "command": {
+ "$id": "#/properties/containers/items/properties/command",
+ "type": "string",
+ "title": "Command To Run The xApp Container",
+ "default": "command",
+ "examples": [
+ "command"
+ ]
+ }
+ }
+ }
+ },
+ "livenessProbe": {
+ "$id": "#/properties/livenessprobe",
+ "type": "object",
+ "title": "The Liveness Probe Definition",
+ "properties": {
+ "exec": {
+ "$id": "#/properties/livenessprobe/exec",
+ "type": "object",
+ "title": "Script of Liveness Probe",
+ "properties": {
+ "command": {
+ "$id": "#/properties/livenessprobe/exec/command",
+ "type": "array",
+ "items": [
+ {
+ "$id": "#/properties/livenessprobe/exec/command/item",
+ "type": "string",
+ "title": "The Command Item",
+ "default": "/bin/sh",
+ "examples": [
+ "/bin/sh"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "command"
+ ]
+ },
+ "httpGet": {
+ "$id": "#/properties/livenessprobe/httpget",
+ "type": "object",
+ "title": "Http of Liveness Probe",
+ "properties": {
+ "path": {
+ "$id": "#/properties/livenessprobe/httpget/path",
+ "type": "string",
+ "title": "The Path of Http Liveness Probe",
+ "default": "/health",
+ "examples": [
+ "/health"
+ ]
+ },
+ "port": {
+ "$id": "#/properties/livenessprobe/httpget/port",
+ "type": "integer",
+ "title": "The Port of Http Liveness Probe",
+ "default": 80,
+ "examples": [
+ 80
+ ]
+ }
+ },
+ "required": [
+ "path",
+ "port"
+ ]
+ },
+ "initialDelaySeconds": {
+ "$id": "#/properties/livenessprobe/initialdelayseconds",
+ "type": "integer",
+ "title": "Initial Delay of Liveness Probe",
+ "default": 5,
+ "examples": [
+ 5
+ ]
+ },
+ "periodSeconds": {
+ "$id": "#/properties/livenessprobe/periodseconds",
+ "type": "integer",
+ "title": "Period of Liveness Probe",
+ "default": 15,
+ "examples": [
+ 15
+ ]
+ }
+ },
+ "oneOf": [
+ {
+ "$id": "#/properties/livenessprobe/oneof/exec",
+ "required": ["exec", "initialDelaySeconds", "periodSeconds"]
+ },
+ {
+ "$id": "#/properties/livenessprobe/oneof/httpget",
+ "required": ["httpGet", "initialDelaySeconds", "periodSeconds"]
+ }
+ ]
+ },
+ "readinessProbe": {
+ "$id": "#/properties/readinessprobe",
+ "type": "object",
+ "title": "The Readiness Probe Definition",
+ "properties": {
+ "exec": {
+ "$id": "#/properties/readinessprobe/exec",
+ "type": "object",
+ "title": "Script of Readiness Probe",
+ "properties": {
+ "command": {
+ "$id": "#/properties/readinessprobe/exec/command",
+ "type": "array",
+ "items": [
+ {
+ "type": "string"
+ }
+ ]
+ }
+ },
+ "required": [
+ "command"
+ ]
+ },
+ "httpGet": {
+ "$id": "#/properties/readinessprobe/httpget",
+ "type": "object",
+ "title": "Http of Readiness Probe",
+ "properties": {
+ "path": {
+ "$id": "#/properties/readinessprobe/httpget/path",
+ "type": "string",
+ "title": "The Path of Http Readiness Probe",
+ "default": "/health",
+ "examples": [
+ "/health"
+ ]
+ },
+ "port": {
+ "$id": "#/properties/readinessprobe/httpget/port",
+ "type": "integer",
+ "title": "The Port of Http Readiness Probe",
+ "default": 80,
+ "examples": [
+ 80
+ ]
+ }
+ },
+ "required": [
+ "path",
+ "port"
+ ]
+ },
+ "initialDelaySeconds": {
+ "$id": "#/properties/readinessprobe/initialdelayseconds",
+ "type": "integer",
+ "title": "Initial Delay of Readiness Probe",
+ "default": 5,
+ "examples": [
+ 5
+ ]
+ },
+ "periodSeconds": {
+ "$id": "#/properties/readinessprobe/periodseconds",
+ "type": "integer",
+ "title": "Period of Readiness Probe",
+ "default": 15,
+ "examples": [
+ 15
+ ]
+ }
+ },
+ "oneOf": [
+ {
+ "$id": "#/properties/readinessprobe/oneof/exec",
+ "required": ["exec", "initialDelaySeconds", "periodSeconds"]
+ },
+ {
+ "$id": "#/properties/readinessprobe/oneof/httpget",
+ "required": ["httpGet", "initialDelaySeconds", "periodSeconds"]
+ }
+ ]
+ },
+ "messaging": {
+ "type": "object",
+ "$id": "#/properties/messaging",
+ "title": "The Messaging Schema",
+ "properties": {
+ "ports": {
+ "$id": "#/properties/messaging/ports",
+ "type": "array",
+ "title": "The Ports for Messaging",
+ "items": {
+ "$id": "#/properties/messaging/ports/items",
+ "type": "object",
+ "title": "The Item of Port",
+ "required": ["name", "container", "port"],
+ "dependencies": {
+ "txMessages": ["rxMessages", "policies"],
+ "rxMessages": ["txMessages", "policies"],
+ "policies": ["rxMessages", "txMessages"]
+ },
+ "properties": {
+ "name": {
+ "$id": "#/properties/messaging/ports/items/name",
+ "type": "string",
+ "title": "The Name of the Port",
+ "default": "App",
+ "examples": [
+ "App"
+ ]
+ },
+ "container": {
+ "$id": "#/properties/messaging/ports/items/container",
+ "type": "string",
+ "title": "The Container of the Port",
+ "default": "xapp",
+ "examples": [
+ "xapp"
+ ]
+ },
+ "port": {
+ "$id": "#/properties/messaging/ports/items/port",
+ "type": "integer",
+ "title": "The Port Number",
+ "default": 8080,
+ "examples": [
+ 8080
+ ]
+ },
+ "description": {
+ "$id": "#/properties/messaging/ports/items/description",
+ "type": "string",
+ "title": "The description for the port",
+ "default": "port description",
+ "examples": [
+ "port description"
+ ]
+ },
+ "txMessages": {
+ "$id": "#/properties/messaging/ports/items/txmessages",
+ "type": "array",
+ "title": "The txMessage Types",
+ "items": {
+ "$id": "#/properties/messaging/ports/items//txmessages/item",
+ "type": "string",
+ "title": "The txMessage Types Item",
+ "default": "RIC_SUB",
+ "examples": [
+ "RIC_SUB"
+ ]
+ }
+ },
+ "rxMessages": {
+ "$id": "#/properties/messaging/ports/items/rxmessages",
+ "type": "array",
+ "title": "The rxMessage Types",
+ "items": {
+ "$id": "#/properties/messaging/ports/items/rxmessages/item",
+ "type": "string",
+ "title": "The rxMessage Types Item",
+ "default": "RIC_SUB",
+ "examples": [
+ "RIC_SUB"
+ ]
+ }
+ },
+ "policies": {
+ "$id": "#/properties/messaging/ports/items/policies",
+ "type": "array",
+ "title": "The Policies Types",
+ "items": {
+ "$id": "#/properties/messaging/ports/items/policies/item",
+ "type": "integer",
+ "title": "The Policy Types Item",
+ "default": 1,
+ "examples": [
+ 1
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "required": [
+ "ports"
+ ]
+
+ },
+ "metrics": {
+ "type": "array",
+ "$id": "#/properties/metrics",
+ "title": "The Metrics Schema",
+ "items": {
+ "$id": "#/properties/metrics/items",
+ "type": "object",
+ "title": "The Metrics Items Schema",
+ "required": [
+ "objectName",
+ "objectInstance",
+ "name",
+ "type",
+ "description"
+ ],
+ "properties": {
+ "objectName": {
+ "$id": "#/properties/metrics/items/objectname",
+ "type": "string",
+ "title": "The Object Name"
+ },
+ "objectInstance": {
+ "$id": "#/properties/metrics/items/objectinstance",
+ "type": "string",
+ "title": "The Object Instance"
+ },
+ "name": {
+ "$id": "#/properties/metrics/items/name",
+ "type": "string",
+ "title": "The Object Name"
+ },
+ "type": {
+ "$id": "#/properties/metrics/items/type",
+ "type": "string",
+ "title": "The Object Type"
+ },
+ "description": {
+ "$id": "#/properties/metrics/items/description",
+ "type": "string",
+ "title": "The Object Description"
+ }
+ }
+ }
+ },
+ "controls": {
+ "required": [
+ "__empty_control_section__"
+ ]
+ }
+
+ }
+}
configMap:
name: {{ include "ricxapp.configmapname" . }}-appconfig
containers:
-{{- range .Values.containers }}
- - name: {{ .name }}
- image: "{{ .image.registry }}/{{ .image.name }}:{{ .image.tag }}"
- {{- if .command }}
- command: [{{ .command }}]
+{{- $containers := .Values.containers }}
+{{- $ports := .Values.messaging.ports }}
+{{- range $container := $containers }}
+ {{- $portlist := list }}
+ {{- range $port := $ports }}
+ {{- if eq $port.container $container.name }}
+ {{- $portlist = append $portlist $port }}
+ {{- end }}
+ {{- end }}
+ - name: {{ $container.name }}
+ image: "{{ $container.image.registry }}/{{ $container.image.name }}:{{ $container.image.tag }}"
+ {{- if $container.command }}
+ command: [{{ $container.command }}]
{{- end}}
imagePullPolicy: {{ $.Values.image_pull_policy }}
- {{- if .ports }}
+ {{- if $portlist }}
ports:
- {{- if .ports.http }}
- - name: http-{{ .name }}
- containerPort: {{ .ports.http }}
- protocol: TCP
- {{- end }}
- {{- if .ports.rmr_data }}
- - name: rmrdata-{{ .name }}
- containerPort: {{ .ports.rmr_data }}
+ {{- range $port := $portlist }}
+ - name: {{ $port.name }}
+ containerPort: {{ $port.port }}
protocol: TCP
- {{- end }}
- {{- if .ports.rmr_route }}
- - name: rmrroute-{{ .name }}
- containerPort: {{ .ports.rmr_route }}
- protocol: TCP
- {{- end }}
- {{- end }}
+ {{- end }}
+ {{- end }}
{{- end }}
volumeMounts:
- name: config-volume
envFrom:
- configMapRef:
name: {{ include "ricxapp.configmapname" . }}-appenv
+ - configMapRef:
+ name: dbaas-appconfig
restartPolicy: Always
MOCK_TEST_HELM_REPO_TEMP_DIR = os.environ.get('MOCK_TEST_HELM_REPO_TEMP_DIR') or '/tmp/mock_helm_repo'
-# Environment variables that will be passed into xApp
-DBAAS_MASTER_NAME = os.environ.get('DBAAS_MASTER_NAME')
-DBAAS_SERVICE_HOST = os.environ.get('DBAAS_SERVICE_HOST')
-DBAAS_SERVICE_SENTINEL_PORT = os.environ.get('DBAAS_SERVICE_SENTINEL_PORT')
-DBAAS_SERVICE_PORT = os.environ.get('DBAAS_SERVICE_PORT')