--- /dev/null
+{
+ "info": {
+ "_postman_id": "4dab7b43-234f-4695-b0ad-11bc178b1a62",
+ "name": "Demo Energy Saving rApp",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
+ "_exporter_id": "14764781"
+ },
+ "item": [
+ {
+ "name": "Energy Saving Usecase",
+ "item": [
+ {
+ "name": "Cleanup",
+ "item": [
+ {
+ "name": "Get All Templates ACM-Direct",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test(\"Status code is 200\", function () {",
+ " pm.response.to.have.status(200);",
+ " let jsonResponse = JSON.parse(responseBody);",
+ " let compositionCleanId = jsonResponse.service_templates[0].metadata.compositionId;",
+ " console.log(\"compositionCleanId: \" + compositionCleanId);",
+ " pm.collectionVariables.set(\"compositionCleanId\", compositionCleanId); // Fixed variable name",
+ "});"
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ }
+ ],
+ "protocolProfileBehavior": {
+ "disableBodyPruning": true
+ },
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "{{ACM_PWD}}",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "{{ACM_USER}}",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{ACM_PORT}}/onap/policy/clamp/acm/v2/compositions",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{ACM_PORT}}",
+ "path": [
+ "onap",
+ "policy",
+ "clamp",
+ "acm",
+ "v2",
+ "compositions"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get Template ACM-Direct",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ ""
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ }
+ ],
+ "protocolProfileBehavior": {
+ "disableBodyPruning": true
+ },
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "{{ACM_PWD}}",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "{{ACM_USER}}",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{ACM_PORT}}/onap/policy/clamp/acm/v2/compositions/{{compositionCleanId}}",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{ACM_PORT}}",
+ "path": [
+ "onap",
+ "policy",
+ "clamp",
+ "acm",
+ "v2",
+ "compositions",
+ "{{compositionCleanId}}"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get All Instances ACM-Direct",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test(\"Status code is 200\", function () {",
+ " pm.response.to.have.status(200);",
+ " let jsonResponse = JSON.parse(responseBody);",
+ " let instanceCleanId = jsonResponse.automationCompositionList[0].instanceId;",
+ " console.log(\"instanceCleanId: \" + instanceCleanId);",
+ " pm.collectionVariables.set(\"instanceCleanId\", instanceCleanId); // Fixed variable name",
+ "});"
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ }
+ ],
+ "protocolProfileBehavior": {
+ "disableBodyPruning": true
+ },
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "{{ACM_PWD}}",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "{{ACM_USER}}",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"name\": \"K8DemoInstance0\",\r\n \"version\": \"1.0.1\",\r\n \"compositionId\": \"{{compositionId}}\",\r\n \"description\": \"Demo automation composition instance 0\",\r\n \"elements\": {\r\n \"d7be552e-bcc5-4478-b64d-797dbaec8f55\": {\r\n \"id\": \"d7be552e-bcc5-4478-b64d-797dbaec8f55\",\r\n \"definition\": {\r\n \"name\": \"onap.policy.clamp.ac.element.K8S_StarterAutomationCompositionElement\",\r\n \"version\": \"1.2.3\"\r\n },\r\n \"description\": \"Starter Automation Composition Element for the Hello World\",\r\n \"properties\": {\r\n \"chart\": {\r\n \"chartId\": {\r\n \"name\": \"hello-world-chart\",\r\n \"version\": \"0.1.0\"\r\n },\r\n \"namespace\": \"nonrtric\",\r\n \"releaseName\": \"hello-world-chart\",\r\n \"podName\": \"hello-world-chart\",\r\n \"repository\": {\r\n \"repoName\": \"local\",\r\n \"address\": \"UPDATE_THIS_CHART_MUSEUM_GET_CHARTS_URI\"\r\n },\r\n \"overrideParams\": {\r\n \"appId\": \"DO_NOT_CHANGE_THIS_RAPP_INSTANCE_ID\",\r\n \"smeDiscoveryEndpoint\": \"DO_NOT_CHANGE_THIS_SME_DISCOVERY_ENDPOINT\"\r\n }\r\n }\r\n }\r\n }\r\n }\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{ACM_PORT}}/onap/policy/clamp/acm/v2/compositions/{{compositionCleanId}}/instances",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{ACM_PORT}}",
+ "path": [
+ "onap",
+ "policy",
+ "clamp",
+ "acm",
+ "v2",
+ "compositions",
+ "{{compositionCleanId}}",
+ "instances"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get Instance ACM-Direct",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ ""
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ }
+ ],
+ "protocolProfileBehavior": {
+ "disableBodyPruning": true
+ },
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "{{ACM_PWD}}",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "{{ACM_USER}}",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"name\": \"K8DemoInstance0\",\r\n \"version\": \"1.0.1\",\r\n \"compositionId\": \"{{compositionId}}\",\r\n \"description\": \"Demo automation composition instance 0\",\r\n \"elements\": {\r\n \"d7be552e-bcc5-4478-b64d-797dbaec8f55\": {\r\n \"id\": \"d7be552e-bcc5-4478-b64d-797dbaec8f55\",\r\n \"definition\": {\r\n \"name\": \"onap.policy.clamp.ac.element.K8S_StarterAutomationCompositionElement\",\r\n \"version\": \"1.2.3\"\r\n },\r\n \"description\": \"Starter Automation Composition Element for the Hello World\",\r\n \"properties\": {\r\n \"chart\": {\r\n \"chartId\": {\r\n \"name\": \"hello-world-chart\",\r\n \"version\": \"0.1.0\"\r\n },\r\n \"namespace\": \"nonrtric\",\r\n \"releaseName\": \"hello-world-chart\",\r\n \"podName\": \"hello-world-chart\",\r\n \"repository\": {\r\n \"repoName\": \"local\",\r\n \"address\": \"UPDATE_THIS_CHART_MUSEUM_GET_CHARTS_URI\"\r\n },\r\n \"overrideParams\": {\r\n \"appId\": \"DO_NOT_CHANGE_THIS_RAPP_INSTANCE_ID\",\r\n \"smeDiscoveryEndpoint\": \"DO_NOT_CHANGE_THIS_SME_DISCOVERY_ENDPOINT\"\r\n }\r\n }\r\n }\r\n }\r\n }\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{ACM_PORT}}/onap/policy/clamp/acm/v2/compositions/{{compositionCleanId}}/instances/{{instanceCleanId}}",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{ACM_PORT}}",
+ "path": [
+ "onap",
+ "policy",
+ "clamp",
+ "acm",
+ "v2",
+ "compositions",
+ "{{compositionCleanId}}",
+ "instances",
+ "{{instanceCleanId}}"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Undeploy Rapp Instance ACM-Direct",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "{{ACM_PWD}}",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "{{ACM_USER}}",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "PUT",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"deployOrder\": \"UNDEPLOY\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{ACM_PORT}}/onap/policy/clamp/acm/v2/compositions/{{compositionCleanId}}/instances/{{instanceCleanId}}",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{ACM_PORT}}",
+ "path": [
+ "onap",
+ "policy",
+ "clamp",
+ "acm",
+ "v2",
+ "compositions",
+ "{{compositionCleanId}}",
+ "instances",
+ "{{instanceCleanId}}"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Delete Instance ACM-Direct",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test(\"Status code is 201\", function () {",
+ " pm.response.to.have.status(201);",
+ " let location = pm.response.headers.get('Location')",
+ " console.log(\"location: \" + location)",
+ " let composition1Id = location.split('/')[2];",
+ " console.log(\"compositionId: \" + composition1Id)",
+ " pm.environment.set(\"composition1Id\", composition1Id);",
+ "});"
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ }
+ ],
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "{{ACM_PWD}}",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "{{ACM_USER}}",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"name\": \"K8DemoInstance0\",\r\n \"version\": \"1.0.1\",\r\n \"compositionId\": \"{{compositionId}}\",\r\n \"description\": \"Demo automation composition instance 0\",\r\n \"elements\": {\r\n \"d7be552e-bcc5-4478-b64d-797dbaec8f55\": {\r\n \"id\": \"d7be552e-bcc5-4478-b64d-797dbaec8f55\",\r\n \"definition\": {\r\n \"name\": \"onap.policy.clamp.ac.element.K8S_StarterAutomationCompositionElement\",\r\n \"version\": \"1.2.3\"\r\n },\r\n \"description\": \"Starter Automation Composition Element for the Hello World\",\r\n \"properties\": {\r\n \"chart\": {\r\n \"chartId\": {\r\n \"name\": \"hello-world-chart\",\r\n \"version\": \"0.1.0\"\r\n },\r\n \"namespace\": \"nonrtric\",\r\n \"releaseName\": \"hello-world-chart\",\r\n \"podName\": \"hello-world-chart\",\r\n \"repository\": {\r\n \"repoName\": \"local\",\r\n \"address\": \"UPDATE_THIS_CHART_MUSEUM_GET_CHARTS_URI\"\r\n },\r\n \"overrideParams\": {\r\n \"appId\": \"DO_NOT_CHANGE_THIS_RAPP_INSTANCE_ID\",\r\n \"smeDiscoveryEndpoint\": \"DO_NOT_CHANGE_THIS_SME_DISCOVERY_ENDPOINT\"\r\n }\r\n }\r\n }\r\n }\r\n }\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{ACM_PORT}}/onap/policy/clamp/acm/v2/compositions/{{compositionCleanId}}/instances/{{instanceCleanId}}",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{ACM_PORT}}",
+ "path": [
+ "onap",
+ "policy",
+ "clamp",
+ "acm",
+ "v2",
+ "compositions",
+ "{{compositionCleanId}}",
+ "instances",
+ "{{instanceCleanId}}"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Deprime Rapp ACM-Direct",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "{{ACM_PWD}}",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "{{ACM_USER}}",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "PUT",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"primeOrder\": \"DEPRIME\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{ACM_PORT}}/onap/policy/clamp/acm/v2/compositions/{{compositionCleanId}}",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{ACM_PORT}}",
+ "path": [
+ "onap",
+ "policy",
+ "clamp",
+ "acm",
+ "v2",
+ "compositions",
+ "{{compositionCleanId}}"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Delete Template ACM-Direct",
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "{{ACM_PWD}}",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "{{ACM_USER}}",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{ACM_PORT}}/onap/policy/clamp/acm/v2/compositions/{{compositionCleanId}}",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{ACM_PORT}}",
+ "path": [
+ "onap",
+ "policy",
+ "clamp",
+ "acm",
+ "v2",
+ "compositions",
+ "{{compositionCleanId}}"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get Participants ACM-Direct",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test(\"Status code is 201\", function () {",
+ " pm.response.to.have.status(201);",
+ " let location = pm.response.headers.get('Location')",
+ " console.log(\"location: \" + location)",
+ " let composition1Id = location.split('/')[2];",
+ " console.log(\"compositionId: \" + composition1Id)",
+ " pm.environment.set(\"composition1Id\", composition1Id);",
+ "});"
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ }
+ ],
+ "protocolProfileBehavior": {
+ "disableBodyPruning": true
+ },
+ "request": {
+ "auth": {
+ "type": "basic",
+ "basic": [
+ {
+ "key": "password",
+ "value": "{{ACM_PWD}}",
+ "type": "string"
+ },
+ {
+ "key": "username",
+ "value": "{{ACM_USER}}",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"tosca_definitions_version\": \"tosca_simple_yaml_1_3\",\r\n \"data_types\": {\r\n \"onap.datatypes.ToscaConceptIdentifier\": {\r\n \"derived_from\": \"tosca.datatypes.Root\",\r\n \"properties\": {\r\n \"name\": {\r\n \"type\": \"string\",\r\n \"required\": true\r\n },\r\n \"version\": {\r\n \"type\": \"string\",\r\n \"required\": true\r\n }\r\n }\r\n }\r\n },\r\n \"node_types\": {\r\n \"org.onap.policy.clamp.acm.Participant\": {\r\n \"version\": \"1.0.1\",\r\n \"derived_from\": \"tosca.nodetypes.Root\",\r\n \"properties\": {\r\n \"provider\": {\r\n \"type\": \"string\",\r\n \"required\": false\r\n }\r\n }\r\n },\r\n \"org.onap.policy.clamp.acm.AutomationCompositionElement\": {\r\n \"version\": \"1.0.1\",\r\n \"derived_from\": \"tosca.nodetypes.Root\",\r\n \"properties\": {\r\n \"provider\": {\r\n \"type\": \"string\",\r\n \"required\": false\r\n },\r\n \"participantType\": {\r\n \"type\": \"onap.datatypes.ToscaConceptIdentifier\",\r\n \"required\": true\r\n },\r\n \"startPhase\": {\r\n \"type\": \"integer\",\r\n \"required\": false,\r\n \"constraints\": [\r\n {\r\n \"greater-or-equal\": 0\r\n }\r\n ],\r\n \"metadata\": {\r\n \"common\": true\r\n },\r\n \"description\": \"A value indicating the start phase in which this automation composition element will be started, the first start phase is zero. Automation Composition Elements are started in their start_phase order and stopped in reverse start phase order. Automation Composition Elements with the same start phase are started and stopped simultaneously\"\r\n },\r\n \"passiveToRunningTimeout\": {\r\n \"type\": \"integer\",\r\n \"required\": false,\r\n \"constraints\": [\r\n {\r\n \"greater_or_equal\": 0\r\n }\r\n ],\r\n \"default\": 60,\r\n \"metadata\": {\r\n \"common\": true\r\n },\r\n \"description\": \"The maximum time in seconds to wait for a state chage from passive to running\"\r\n },\r\n \"runningToPassiveTimeout\": {\r\n \"type\": \"integer\",\r\n \"required\": false,\r\n \"constraints\": [\r\n {\r\n \"greater_or_equal\": 0\r\n }\r\n ],\r\n \"default\": 60,\r\n \"metadata\": {\r\n \"common\": true\r\n },\r\n \"description\": \"The maximum time in seconds to wait for a state chage from running to passive\"\r\n },\r\n \"passiveToUninitializedTimeout\": {\r\n \"type\": \"integer\",\r\n \"required\": false,\r\n \"constraints\": [\r\n {\r\n \"greater_or_equal\": 0\r\n }\r\n ],\r\n \"default\": 60,\r\n \"metadata\": {\r\n \"common\": true\r\n },\r\n \"description\": \"The maximum time in seconds to wait for a state chage from passive to uninitialized\"\r\n }\r\n }\r\n },\r\n \"org.onap.policy.clamp.acm.AutomationComposition\": {\r\n \"version\": \"1.0.1\",\r\n \"derived_from\": \"tosca.nodetypes.Root\",\r\n \"properties\": {\r\n \"provider\": {\r\n \"type\": \"string\",\r\n \"required\": false,\r\n \"metadata\": {\r\n \"common\": true\r\n }\r\n },\r\n \"elements\": {\r\n \"type\": \"list\",\r\n \"required\": true,\r\n \"metadata\": {\r\n \"common\": true\r\n },\r\n \"entry_schema\": {\r\n \"type\": \"onap.datatypes.ToscaConceptIdentifier\"\r\n }\r\n }\r\n }\r\n },\r\n \"org.onap.policy.clamp.acm.K8SMicroserviceAutomationCompositionElement\": {\r\n \"version\": \"1.0.0\",\r\n \"derived_from\": \"org.onap.policy.clamp.acm.AutomationCompositionElement\",\r\n \"properties\": {\r\n \"chart\": {\r\n \"type\": \"string\",\r\n \"required\": true\r\n },\r\n \"configs\": {\r\n \"type\": \"list\",\r\n \"required\": false\r\n },\r\n \"requirements\": {\r\n \"type\": \"string\",\r\n \"required\": false\r\n },\r\n \"templates\": {\r\n \"type\": \"list\",\r\n \"required\": false,\r\n \"entry_schema\": null\r\n },\r\n \"values\": {\r\n \"type\": \"string\",\r\n \"required\": true\r\n }\r\n }\r\n }\r\n },\r\n \"topology_template\": {\r\n \"node_templates\": {\r\n \"org.onap.k8s.acm.K8SAutomationCompositionParticipant\": {\r\n \"version\": \"2.3.4\",\r\n \"type\": \"org.onap.policy.clamp.acm.Participant\",\r\n \"type_version\": \"1.0.1\",\r\n \"description\": \"Participant for K8S\",\r\n \"properties\": {\r\n \"provider\": \"ONAP\"\r\n }\r\n },\r\n \"onap.policy.clamp.ac.element.K8S_StarterAutomationCompositionElement\": {\r\n \"version\": \"1.2.3\",\r\n \"type\": \"org.onap.policy.clamp.acm.K8SMicroserviceAutomationCompositionElement\",\r\n \"type_version\": \"1.0.0\",\r\n \"description\": \"Automation composition element for the K8S microservice for AC Element Starter\",\r\n \"properties\": {\r\n \"provider\": \"ONAP\",\r\n \"startPhase\": 0,\r\n \"uninitializedToPassiveTimeout\": 300,\r\n \"podStatusCheckInterval\": 30\r\n }\r\n },\r\n \"onap.policy.clamp.ac.element.AutomationCompositionDefinition\": {\r\n \"version\": \"1.2.3\",\r\n \"type\": \"org.onap.policy.clamp.acm.AutomationComposition\",\r\n \"type_version\": \"1.0.1\",\r\n \"description\": \"Automation composition for rapp deployment\",\r\n \"properties\": {\r\n \"provider\": \"ONAP\",\r\n \"elements\": [\r\n {\r\n \"name\": \"onap.policy.clamp.ac.element.K8S_StarterAutomationCompositionElement\",\r\n \"version\": \"1.2.3\"\r\n }\r\n ]\r\n }\r\n }\r\n }\r\n }\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{ACM_PORT}}/onap/policy/clamp/acm/v2/participants",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{ACM_PORT}}",
+ "path": [
+ "onap",
+ "policy",
+ "clamp",
+ "acm",
+ "v2",
+ "participants"
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "Onboard ES Rapp",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "multipart/form-data",
+ "type": "text",
+ "disabled": true
+ }
+ ],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "file",
+ "type": "file",
+ "src": "/home/saul/Projects/oran-projects/nonrtric-prototyping/es-rapp/rapp-energy-saving.csar"
+ },
+ {
+ "key": "file2",
+ "type": "file",
+ "src": [],
+ "disabled": true
+ }
+ ]
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{PORT}}/rapps/{{rappId}}",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{PORT}}",
+ "path": [
+ "rapps",
+ "{{rappId}}"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get Rapps",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{PORT}}/rapps",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{PORT}}",
+ "path": [
+ "rapps"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Prime Rapp",
+ "request": {
+ "method": "PUT",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"primeOrder\": \"PRIME\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{PORT}}/rapps/{{rappId}}",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{PORT}}",
+ "path": [
+ "rapps",
+ "{{rappId}}"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get All Rapp Instances",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{PORT}}/rapps/{{rappId}}/instance",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{PORT}}",
+ "path": [
+ "rapps",
+ "{{rappId}}",
+ "instance"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Create Rapp Instance ES",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "var jsonData = pm.response.json();\r",
+ "if(jsonData != null && jsonData.rappInstanceId != null) {\r",
+ "\r",
+ "pm.collectionVariables.set(\"rappInstanceId\", jsonData.rappInstanceId);\r",
+ "}"
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"acm\":{\r\n \"instance\": \"es-instance\"\r\n },\r\n \"sme\": {\r\n \"providerFunction\": \"es-model-provider-function\",\r\n \"serviceApis\": \"api-set-kserve-predictor\",\r\n \"invokers\": \"invoker-app1\"\r\n }\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{PORT}}/rapps/{{rappId}}/instance",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{PORT}}",
+ "path": [
+ "rapps",
+ "{{rappId}}",
+ "instance"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get Rapp Instance",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{PORT}}/rapps/{{rappId}}/instance/{{rappInstanceId}}",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{PORT}}",
+ "path": [
+ "rapps",
+ "{{rappId}}",
+ "instance",
+ "{{rappInstanceId}}"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Deploy Rapp Instance",
+ "request": {
+ "method": "PUT",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"deployOrder\": \"DEPLOY\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{PORT}}/rapps/{{rappId}}/instance/{{rappInstanceId}}",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{PORT}}",
+ "path": [
+ "rapps",
+ "{{rappId}}",
+ "instance",
+ "{{rappInstanceId}}"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Undeploy Rapp Instance",
+ "request": {
+ "method": "PUT",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"deployOrder\": \"UNDEPLOY\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{PORT}}/rapps/{{rappId}}/instance/{{rappInstanceId}}",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{PORT}}",
+ "path": [
+ "rapps",
+ "{{rappId}}",
+ "instance",
+ "{{rappInstanceId}}"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Delete Rapp Instance",
+ "request": {
+ "method": "DELETE",
+ "header": [],
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{PORT}}/rapps/{{rappId}}/instance/{{rappInstanceId}}",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{PORT}}",
+ "path": [
+ "rapps",
+ "{{rappId}}",
+ "instance",
+ "{{rappInstanceId}}"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Deprime Rapp",
+ "request": {
+ "method": "PUT",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"primeOrder\": \"DEPRIME\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{PORT}}/rapps/{{rappId}}",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{PORT}}",
+ "path": [
+ "rapps",
+ "{{rappId}}"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Delete ES Rapp",
+ "request": {
+ "method": "DELETE",
+ "header": [],
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{PORT}}/rapps/{{rappId}}",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{PORT}}",
+ "path": [
+ "rapps",
+ "{{rappId}}"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Predict",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"signature_name\": \"serving_default\",\n \"instances\": [\n [\n [\n 8.15,\n 11.51,\n 57.12\n ]\n ]\n ]\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://{{REMOTE-IP}}:{{PREDICT_PORT}}/v1/models/es-aiml-model:predict",
+ "protocol": "http",
+ "host": [
+ "{{REMOTE-IP}}"
+ ],
+ "port": "{{PREDICT_PORT}}",
+ "path": [
+ "v1",
+ "models",
+ "es-aiml-model:predict"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Model readiness",
+ "protocolProfileBehavior": {
+ "disableBodyPruning": true
+ },
+ "request": {
+ "method": "GET",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:40077/v1/models/es-aiml-model",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "40077",
+ "path": [
+ "v1",
+ "models",
+ "es-aiml-model"
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ }
+ ],
+ "variable": [
+ {
+ "key": "REMOTE-IP",
+ "value": "localhost"
+ },
+ {
+ "key": "ACM_PORT",
+ "value": "30442"
+ },
+ {
+ "key": "compositionCleanId",
+ "value": "969b740c-dde4-410c-ade2-8db2061e1531"
+ },
+ {
+ "key": "instanceCleanId",
+ "value": "1aaed9b1-4d23-441f-9107-5e3f36f4f323"
+ },
+ {
+ "key": "PORT",
+ "value": "36797"
+ },
+ {
+ "key": "rappId",
+ "value": "energy-saving-1"
+ },
+ {
+ "key": "rappInstanceId",
+ "value": "083dea57-f177-4c0e-be1b-a3282d16f586"
+ },
+ {
+ "key": "PREDICT_PORT",
+ "value": "40077"
+ },
+ {
+ "key": "ACM_USER",
+ "value": "runtimeUser"
+ },
+ {
+ "key": "ACM_PWD",
+ "value": "ZiboTipdZeyt9]"
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+
+FROM python:3.10.17-alpine3.21
+
+WORKDIR /app
+
+COPY src/ /app
+
+RUN pip install -r requirements.txt
+
+COPY src/ /app
+
+CMD ["python", "main.py", "--generate_db_data=True", "--use_sme_db=True", "--random_predictions=False" ]
--- /dev/null
+# Energy Saving rApp Demo
+The code, helm chart and rApp specification contained in this directory is used to deploy the Energy Saving rApp demo.
+The demo is designed to showcase the capabilities of the rApp platform in managing energy consumption in a network.
+The instructions below describe how to:
+
+- Deploy the demo in a Kubernetes cluster.
+- Create and deploy the Energy Saving rApp.
+- Confirm that the Energy Saving rApp is running and managing energy consumption in the network.
+- Undeploy the Energy Saving rApp.
+- Troubleshoot any issues that may arise during the deployment or undeployment process.
+
+## Prerequisites
+- A Kubernetes cluster, various versions and sims supported - see [here](https://gerrit.o-ran-sc.org/r/gitweb?p=it/dep.git;a=blob_plain;f=smo-install/README.md;hb=HEAD).
+- See prerequisites [here](https://gerrit.o-ran-sc.org/r/gitweb?p=it/dep.git;a=blob_plain;f=smo-install/README.md;hb=HEAD)
+- A clone of this repository.
+- Postman.
+
+## Deployment Steps
+
+### SMO Installation
+For a complete guide on the installation of the SMO rApp platform,
+please follow the instructions [here](https://gerrit.o-ran-sc.org/r/gitweb?p=it/dep.git;a=blob_plain;f=smo-install/README.md;hb=HEAD).
+
+### Energy Saving rApp Deployment Preparation
+1. The rApp needs to know the address and port of your chart repository. We can do this by running the following command:
+ ```bash
+ cd scripts/install
+ ./patch-sample-rapps.sh -i IP_ADD -p PORT -r "es-demo-rapp/rapp-energy-saving"
+ ```
+2. Navigate to the `es-demo-rapp` directory.
+3. To generate the rApp csar and helm chart, run the following command:
+ ```bash
+ ./generate.sh rapp-energy-saving
+ ```
+4. Make sure to expose the rappmanager service in the `nonrtric` namespace. This is done by running the following command:
+ ```bash
+ kubectl expose service rappmanager --type=NodePort --name=rappmanager-exposed -n nonrtric
+ ```
+5. You will be using the postman collection provided in the main directory of this repository to create the rApp.
+6. Open Postman and import the `rapp-energy-saving.postman_collection.json` file.
+7. It is important to note the collection-level variables in the postman collection.
+ 1. REMOTE-IP: This is the location the rappmanager service is deployed/exposed to.
+ 2. PORT: This should be the port where the rappmanager is exposed.
+ 3. rappId: This is the ID of the rApp you will be creating. It should be unique and can be any alphanumeric string.
+ 4. rappInstanceId: It will be automatically populated when you create the rApp instance.
+ 5. PREIDCT_PORT: This is the port where the prediction service is running. It should be set to `40077` by default.
+
+### rApp Deployment
+1. In Postman, select the `Onboard ES rApp` request from the collection. Send this request.
+2. Then run the `Get Rapps` request to confirm that the rApp has been onboarded successfully.
+3. Run the `Prime rApp` request to prime the rApp.
+4. Run `Get All Rapp Instances` to confirm that no rApp instance have been created.
+5. Run the `Create Rapp Instance ES` request to create an instance of the rApp.
+6. Run the `Get Rapp Instance` request to confirm that the rApp instance has been created successfully.
+7. Run the `Deploy Rapp Instance` to trigger installation of the rApp instance.
+8. The above deployment can take time, so you can run the `Get Rapp Instance` request to check the status of the rApp instance.
+9. You can also monitor the kubernetes pods in the `nonrtric` namespace to see if the rApp instance helm charts are being deployed.
+
+### Confirmation
+1. To confirm successful running of the demo energy saving rApp, we can look at the kubernetes logs of the pod.
+ ```bash
+ kubectl logs -f app.kubernetes.io/name=energy-saving-rapp -n nonrtric
+ ```
+2. You should see logs indicating that the rApp is running and managing energy consumption in the network.
+3. For example, you should see:
+ 1. logs of cells being turned off and on based on the energy consumption in the network.
+ 2. Predictions of energy consumption based on the current network load being returned to make poweer management decisions.
+
+## Undeployment
+Undeployment of the rApp can also be done with the Postman collection.
+1. Run the `Undeploy Rapp Instance` request to undeploy the rApp instance. This takes some time.
+2. Run the `Get Rapp Instance` request to confirm that the rApp instance has been undeployed successfully.
+3. Run the `Delete Rapp Instance` request to delete the rApp instance.
+4. Run the `Get All Rapp Instances` request to confirm that no rApp instances are present.
+5. Run the `Deprime rApp` request to deprime the rApp.
+6. Run the `Delete ES Rapp` request to delete the rApp.
+7. This should conclude the undeployment of the Energy Saving rApp.
+
+## Troubleshooting
+If you encounter any issues during the deployment or undeployment of the rApp, please check the following:
+1. Is deployment of the pods stuck in ACM?
+ - Check the logs of the ACM pod and the kubernetes participant in the `onap` namespace.
+ - Is there any indication that install of the pods failed?
+ - Check the deployment status of the pods
+2. Is there an issue with the SME part of the installation?
+ - Check the logs of the servicemanager pod in the `nonrtric` namespace.
+ - Is there any indication that the SME is not able to communicate with the rApp Manager?
+
+### Clean Up
+If there is a case of a failed deployment that cannot be cleaned up via the API, we can use the following steps.
+When cleaning up, it is best to carry out both ACM Cleanup and SME Cleanup - detailed below.
+
+#### ACM Cleanup
+Consult the postman collection under the "Cleanup" directory for the ACM cleanup steps.
+1. Run `Get All Templates ACM-Direct`
+2. Run `Get Template ACM-Direct`
+3. Run `Get All Instances ACM-Direct`
+4. Run `Get Instance ACM-Direct`
+5. The above will populate the postman collection variables with the template and instance IDs.
+6. Run `Undeploy Instance ACM-Direct` to undeploy the instance.
+ Wait for the pods to undeploy (if they are stuck or leftover).
+7. Run `Delete Instance ACM-Direct` to delete the instance.
+8. Run `Delete Template ACM-Direct` to delete the template.
+9. That should conclude the ACM cleanup.
+
+#### SME Cleanup
+SME cleanup requires some manual steps.
+1. Delete all the kong services and routes. Put this in some "script.sh" file and run it. The place where you run it
+ should have access to the cluster.
+ ```bash
+ SERVICEMANAGER_POD=$(kubectl get pods -o custom-columns=NAME:.metadata.name -l app.kubernetes.io/name=servicemanager --no-headers -n nonrtric)
+ if [[ -n $SERVICEMANAGER_POD ]]; then
+ kubectl exec $SERVICEMANAGER_POD -n nonrtric -- ./kongclearup
+ else
+ echo "Error - Servicemanager pod not found, didn't delete Kong routes and services for ServiceManager."
+ fi
+
+ ```
+2. Once the above has been run, we must restart some pods.
+ ```bash
+ kubectl delete pod -l app.kubernetes.io/name=servicemanager -n nonrtric
+ kubectl delete pod -l app.kubernetes.io/name=rappmanager -n nonrtric
+ kubectl delete pod -l app.kubernetes.io/name=capifcore -n nonrtric
+ ```
+3. Wait for these pods to come up again to a `Running` state.
+4. Now we need to add some preloaded SME configurations.
+ 1. In the `it/dep` repository, navigate to the `nonrtric/servicemanager-preload` directory.
+ 2. Preload some of the nonrtric services by running the following command:
+ ```bash
+ ./servicemanager-preload.sh config-nonrtric.yaml
+ ```
+ 3. Preload the SMO services by running the following command:
+ ```bash
+ ./servicemanager-preload.sh config-smo.yaml
+ ```
+5. This should conclude the SME cleanup. Then we can attempt to redeploy the rApp again with whatever
+ changes we made to fix the issues.
\ No newline at end of file
--- /dev/null
+#!/bin/bash
+
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+while getopts "u:p:" opt; do
+ case $opt in
+ u) KAFKA_USER="$OPTARG" ;;
+ p) KAFKA_PASSWORD="$OPTARG" ;;
+ *) echo "Usage: $0 [-u KAFKA_USER] [-p KAFKA_PASSWORD]" >&2; exit 1 ;;
+ esac
+done
+
+if [ -z "$KAFKA_USER" ] || [ -z "$KAFKA_PASSWORD" ]; then
+ echo "Error: All arguments (-u, -p) are required." >&2
+ echo "Usage: $0 [-u KAFKA_USER] [-p KAFKA_PASSWORD]" >&2
+ exit 1
+fi
+
+
+KAFKA_NAMESPACE="${1:-onap}"
+KAFKA_TOPIC="${2:-topology-inventory-ingestion}"
+KAFKA_POD_NAME="${3:-onap-strimzi-kafka-0}"
+GROUP_ID="${4:-topology-inventory-ingestion-consumer}"
+OFFSET_RESET="${5:-earliest}"
+
+echo "Consuming messages from Kafka topic '$KAFKA_TOPIC'..."
+
+kubectl exec -it "$KAFKA_POD_NAME" -n "$KAFKA_NAMESPACE" -- /opt/kafka/bin/kafka-console-consumer.sh \
+ --bootstrap-server onap-strimzi-kafka-bootstrap.onap:9092 \
+ --topic "$KAFKA_TOPIC" \
+ --consumer-property security.protocol=SASL_PLAINTEXT \
+ --consumer-property sasl.mechanism=SCRAM-SHA-512 \
+ --consumer-property "sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username=\"${KAFKA_USER}\" password=\"${KAFKA_PASSWORD}\";" \
+ --group "$GROUP_ID" \
+ --from-beginning \
+ --timeout-ms 60000 \
+ --consumer-property auto.offset.reset="$OFFSET_RESET"
\ No newline at end of file
--- /dev/null
+#!/bin/bash
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+
+while getopts "u:p:" opt; do
+ case $opt in
+ u) KAFKA_USER="$OPTARG" ;;
+ p) KAFKA_PASSWORD="$OPTARG" ;;
+ *) echo "Usage: $0 [-u KAFKA_USER] [-p KAFKA_PASSWORD]" >&2; exit 1 ;;
+ esac
+done
+
+if [ -z "$KAFKA_USER" ] || [ -z "$KAFKA_PASSWORD" ]; then
+ echo "Error: All arguments (-u, -p) are required." >&2
+ echo "Usage: $0 [-u KAFKA_USER] [-p KAFKA_PASSWORD]" >&2
+ exit 1
+fi
+
+KAFKA_NAMESPACE="${1:-onap}"
+KAFKA_TOPIC="${2:-topology-inventory-ingestion}"
+KAFKA_POD_NAME="${3:-onap-strimzi-kafka-0}"
+FILE_DUPLICATION_FACTOR="${4:-1}" # How many times to use the same CloudEvent file
+FOLDER_OF_CLOUDEVENTS="${5:-events}" # All files in this folder are written to kafka
+# number of generated files == number of files in the $FOLDER_OF_CLOUDEVENTS * $FILE_DUPLICATION_FACTOR
+
+echo "Producing messages to Kafka topic '$KAFKA_TOPIC'..."
+
+# Loop to produce multiple messages
+for ((i=0; i<$FILE_DUPLICATION_FACTOR; i++)); do
+ for file in "$FOLDER_OF_CLOUDEVENTS"/*; do
+ if [ -f "$file" ]; then
+ # Replace new lines and write to kafka
+ sed ':a;N;$!ba;s/\n/ /g' $file | \
+ kubectl exec -i "$KAFKA_POD_NAME" -n "$KAFKA_NAMESPACE" -- /opt/kafka/bin/kafka-console-producer.sh \
+ --bootstrap-server onap-strimzi-kafka-bootstrap.onap:9092 \
+ --topic "$KAFKA_TOPIC" \
+ --producer-property security.protocol=SASL_PLAINTEXT \
+ --producer-property sasl.mechanism=SCRAM-SHA-512 \
+ --producer-property "sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username=\"${KAFKA_USER}\" password=\"${KAFKA_PASSWORD}\";" \
+ --property parse.headers=true \
+ --property headers.key.separator=::: \
+ --property headers.delimiter=,,, \
+ --property parse.key=false || {
+ echo "Failed to produce message from file $file"
+ exit 1
+ }
+ fi
+ done
+ echo "$((i+1))/$FILE_DUPLICATION_FACTOR rounds completed"
+done
+
+echo "Message production completed."
--- /dev/null
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+
+apiVersion: v2
+name: energy-saving-rapp
+description: A Helm chart for Kubernetes
+
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+# Versions are expected to follow Semantic Versioning (https://semver.org/)
+version: 0.1.0
+
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application. Versions are not expected to
+# follow Semantic Versioning. They should reflect the version the application is using.
+# It is recommended to use it with quotes.
+appVersion: "1.16.0"
--- /dev/null
+{
+ "SME": {
+ "sme_discovery_endpoint": "{{ .Values.environment.smeDiscoveryEndpoint }}",
+ "host": "localhost",
+ "port": 31575,
+ "ncmp_invoker_id": "{{ .Values.environment.appId }}",
+ "ncmp_api_name": "{{ .Values.ncmp.apiName }}",
+ "ncmp_resource_name": "{{ .Values.ncmp.resourceName }}",
+ "resource_id": "{{ .Values.ncmp.resourceId}}",
+ "influxdb_invoker_id": "{{ .Values.environment.appId }}",
+ "influxdb_api_name": "{{ .Values.influxdb.apiName }}",
+ "influxdb_resource_name": "{{ .Values.influxdb.resourceName }}",
+ "kserve_invoker_id": "{{ .Values.environment.appId }}",
+ "kserve_api_name": "{{ .Values.kserve.apiName }}",
+ "kserve_resource_name": "{{ .Values.kserve.resourceName }}",
+ "teiv_invoker_id": "{{ .Values.environment.appId }}",
+ "teiv_api_name": "{{ .Values.teiv.apiName }}",
+ "teiv_resource_name": "{{ .Values.teiv.resourceName }}",
+ "odufunction_id": "{{ .Values.teiv.oduFunctionId }}"
+ },
+ "DB": {
+ "host": "localhost",
+ "port": 8086,
+ "bucket": "{{ .Values.influxdb.bucket }}",
+ "org": "{{ .Values.influxdb.org }}",
+ "token": "{{ .Values.influxdb.token }}",
+ "user": "{{ .Values.influxdb.user }}",
+ "password": "{{ .Values.influxdb.password }}",
+ "example_data_file": "example_data.json",
+ "database": "{{ .Values.influxdb.bucket }}",
+ "measurement": "CellReports",
+ "ssl": false,
+ "address": "http://localhost:8086"
+ }
+}
\ No newline at end of file
--- /dev/null
+{{/*
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+*/}}
+
+1. Get the application URL by running these commands:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+ {{- range .paths }}
+ http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
+ {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "energy-saving.fullname" . }})
+ export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "energy-saving.fullname" . }}'
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "energy-saving.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+ echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "energy-saving.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+ export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+ echo "Visit http://127.0.0.1:8080 to use your application"
+ kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
--- /dev/null
+{{/*
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+*/}}
+
+
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "energy-saving.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "energy-saving.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "energy-saving.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "energy-saving.labels" -}}
+helm.sh/chart: {{ include "energy-saving.chart" . }}
+{{ include "energy-saving.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "energy-saving.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "energy-saving.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "energy-saving.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "energy-saving.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
--- /dev/null
+{{/*
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+*/}}
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ include "energy-saving.name" . }}-configmap
+ labels:
+ {{- include "energy-saving.labels" . | nindent 4 }}
+data:
+{{ tpl (.Files.Glob "resources/config/config.json").AsConfig . | indent 2 }}
\ No newline at end of file
--- /dev/null
+{{/*
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+*/}}
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "energy-saving.fullname" . }}
+ labels:
+ {{- include "energy-saving.labels" . | nindent 4 }}
+spec:
+ {{- if not .Values.autoscaling.enabled }}
+ replicas: {{ .Values.replicaCount }}
+ {{- end }}
+ selector:
+ matchLabels:
+ {{- include "energy-saving.selectorLabels" . | nindent 6 }}
+ template:
+ metadata:
+ {{- with .Values.podAnnotations }}
+ annotations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ labels:
+ {{- include "energy-saving.selectorLabels" . | nindent 8 }}
+ spec:
+ {{- with .Values.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ serviceAccountName: {{ include "energy-saving.serviceAccountName" . }}
+ securityContext:
+ {{- toYaml .Values.podSecurityContext | nindent 8 }}
+ containers:
+ - name: {{ .Chart.Name }}
+ securityContext:
+ {{- toYaml .Values.securityContext | nindent 12 }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ ports:
+ - name: http
+ containerPort: {{ .Values.service.port }}
+ protocol: TCP
+ resources:
+ {{- toYaml .Values.resources | nindent 12 }}
+ env:
+ - name: APP_ID
+ value: {{ .Values.environment.appId | quote }}
+ - name: SME_DISCOVERY_ENDPOINT
+ value: {{ .Values.environment.smeDiscoveryEndpoint | quote }}
+ volumeMounts:
+ - mountPath: /app/config.json
+ name: config
+ subPath: config.json
+ command:
+ {{- if .Values.appStartup.command }}
+ {{- toYaml .Values.appStartup.command | nindent 12 }}
+ {{- end }}
+ args:
+ {{- if .Values.appStartup.args }}
+ {{- toYaml .Values.appStartup.args | nindent 12 }}
+ {{- end }}
+ {{- with .Values.nodeSelector }}
+ nodeSelector:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.affinity }}
+ affinity:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.tolerations }}
+ tolerations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ volumes:
+ - name: config
+ configMap:
+ name: {{ include "energy-saving.name" . }}-configmap
+ defaultMode: 0755
+ items:
+ - key: config.json
+ path: config.json
--- /dev/null
+{{/*
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+*/}}
+
+{{- if .Values.autoscaling.enabled }}
+apiVersion: autoscaling/v2
+kind: HorizontalPodAutoscaler
+metadata:
+ name: {{ include "energy-saving.fullname" . }}
+ labels:
+ {{- include "energy-saving.labels" . | nindent 4 }}
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: {{ include "energy-saving.fullname" . }}
+ minReplicas: {{ .Values.autoscaling.minReplicas }}
+ maxReplicas: {{ .Values.autoscaling.maxReplicas }}
+ metrics:
+ {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: cpu
+ target:
+ type: Utilization
+ averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
+ {{- end }}
+ {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: memory
+ target:
+ type: Utilization
+ averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ {{- end }}
+{{- end }}
--- /dev/null
+{{/*
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+*/}}
+
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "energy-saving.fullname" . -}}
+{{- $svcPort := .Values.service.port -}}
+{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
+ {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
+ {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
+ {{- end }}
+{{- end }}
+{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1
+{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1beta1
+{{- else -}}
+apiVersion: extensions/v1beta1
+{{- end }}
+kind: Ingress
+metadata:
+ name: {{ $fullName }}
+ labels:
+ {{- include "energy-saving.labels" . | nindent 4 }}
+ {{- with .Values.ingress.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
+ ingressClassName: {{ .Values.ingress.className }}
+ {{- end }}
+ {{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ {{- range .hosts }}
+ - {{ . | quote }}
+ {{- end }}
+ secretName: {{ .secretName }}
+ {{- end }}
+ {{- end }}
+ rules:
+ {{- range .Values.ingress.hosts }}
+ - host: {{ .host | quote }}
+ http:
+ paths:
+ {{- range .paths }}
+ - path: {{ .path }}
+ {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
+ pathType: {{ .pathType }}
+ {{- end }}
+ backend:
+ {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
+ service:
+ name: {{ $fullName }}
+ port:
+ number: {{ $svcPort }}
+ {{- else }}
+ serviceName: {{ $fullName }}
+ servicePort: {{ $svcPort }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
+{{- end }}
--- /dev/null
+{{/*
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+*/}}
+
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "energy-saving.fullname" . }}
+ labels:
+ {{- include "energy-saving.labels" . | nindent 4 }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ {{- include "energy-saving.selectorLabels" . | nindent 4 }}
--- /dev/null
+{{/*
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+*/}}
+
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ include "energy-saving.serviceAccountName" . }}
+ labels:
+ {{- include "energy-saving.labels" . | nindent 4 }}
+ {{- with .Values.serviceAccount.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+{{- end }}
--- /dev/null
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+
+# Default values for energy-saving.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+
+image:
+ repository: "nexus3.onap.org:10001/estdemoimages/es-rapp"
+ pullPolicy: Always
+ # Overrides the image tag whose default is the chart appVersion.
+ tag: "0.1.1"
+
+imagePullSecrets: []
+nameOverride: "energy-saving-rapp"
+fullnameOverride: "energy-saving-rapp"
+
+serviceAccount:
+ # Specifies whether a service account should be created
+ create: true
+ # Annotations to add to the service account
+ annotations: {}
+ # The name of the service account to use.
+ # If not set and create is true, a name is generated using the fullname template
+ name: ""
+
+podAnnotations: {}
+
+podSecurityContext: {}
+ # fsGroup: 2000
+
+securityContext: {}
+ # capabilities:
+ # drop:
+ # - ALL
+ # readOnlyRootFilesystem: true
+ # runAsNonRoot: true
+ # runAsUser: 1000
+
+service:
+ type: ClusterIP
+ port: 8080
+
+ingress:
+ enabled: false
+ className: ""
+ annotations: {}
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: "true"
+ hosts:
+ - host: chart-example.local
+ paths:
+ - path: /
+ pathType: ImplementationSpecific
+ tls: []
+ # - secretName: chart-example-tls
+ # hosts:
+ # - chart-example.local
+
+resources: {}
+ # We usually recommend not to specify default resources and to leave this as a conscious
+ # choice for the user. This also increases chances charts run on environments with little
+ # resources, such as Minikube. If you do want to specify resources, uncomment the following
+ # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+ # limits:
+ # cpu: 100m
+ # memory: 128Mi
+ # requests:
+ # cpu: 100m
+ # memory: 128Mi
+
+autoscaling:
+ enabled: false
+ minReplicas: 1
+ maxReplicas: 100
+ targetCPUUtilizationPercentage: 80
+ # targetMemoryUtilizationPercentage: 80
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
+environment:
+ appId: "energy-saving"
+ smeDiscoveryEndpoint: "http://sme-discovery.default.svc.cluster.local:8080/service-apis/v1/allServiceAPIs"
+
+influxdb:
+ token: "WcrfLfqC63uCxfDa15C1sb6WtG5fhzrS"
+ user: "admin"
+ password: "mySuP3rS3cr3tT0keN"
+ bucket: "pm-bucket"
+ org: "est"
+ apiName: "influxdb2-http"
+ resourceName: "root"
+
+
+ncmp:
+ apiName: "ncmp-dmi-plugin-http"
+ resourceName: "root"
+ resourceId: "/_3gpp-common-managed-element:ManagedElement=ManagedElement-002/_3gpp-nr-nrm-gnbdufunction:GNBDUFunction=GNBDUFunction-001/_3gpp-nr-nrm-nrcelldu:NRCellDU=NRCellDU-001/attributes"
+
+kserve:
+ apiName: "es-predictor-http"
+ resourceName: "root"
+
+teiv:
+ apiName: "topology-exposure-http"
+ resourceName: "root"
+ oduFunctionId: "GNBDUFunction-001"
+
+appStartup:
+ command: ["/bin/sh", "-c"]
+ args: [ "python main.py --generate_db_data=True --use_sme_db=True --random_predictions=False" ]
+
--- /dev/null
+tosca_definitions_version: tosca_simple_yaml_1_2\r
+\r
+description: rApp energy saving\r
+\r
+imports:\r
+ - asd_types.yaml\r
+\r
+topology_template:\r
+ node_templates:\r
+ applicationServiceDescriptor:\r
+ type: tosca.nodes.asd\r
+ description: "rapp-energy-saving"\r
+ properties:\r
+ descriptor_id: 364545c9-f1b1-48bb-a774-2b999e47692d\r
+ descriptor_invariant_id: 97f18c6e-125d-470e-81a2-a36034989acd\r
+ descriptor_version: 1.0\r
+ schema_version: 2.0\r
+ function_description: rApp description\r
+ provider: Ericsson Software Technology (O-RAN SC NONRTRIC team)\r
+ application_name: rapp-energy-saving\r
+ application_version: 1.0\r
+ artifacts:\r
+ energy-saving:\r
+ type: tosca.artifacts.asd.deploymentItem\r
+ file: "Artifacts/Deployment/HELM/energy-saving-rapp-0.1.0.tgz"\r
+ properties:\r
+ artifact_type: "helm_chart"\r
+ target_server: "chartmuseum"\r
+ target_server_uri: "UPDATE_THIS_CHART_MUSEUM_POST_CHARTS_URI"\r
+ item_id: 1
\ No newline at end of file
--- /dev/null
+tosca_definitions_version: tosca_simple_yaml_1_2
+description: ASD types definitions version 0.1
+node_types:
+ tosca.nodes.asd:
+ derived_from: tosca.nodes.Root
+ description: "The ASD node type"
+ version: 0.1
+ properties:
+ descriptor_id:
+ type: string # UUID
+ required: true
+ description: Identifier of this ASD. It is in UUID format as specified in RFC 4122
+ descriptor_invariant_id:
+ type: string # UUID
+ required: true
+ description: >
+ Identifier of this descriptor in a version independent manner. This attribute
+ is invariant across versions of ASD. It is in UUID format as specified in RFC 4122
+ descriptor_version:
+ type: string
+ required: true
+ description: Identifies the version of the ASD.
+ schema_version:
+ type: string
+ required: true
+ description: Identifies the Identifies the version of this ASD’s schema.
+ function_description:
+ type: string
+ required: false
+ description: Description of the application service described by this ASD.
+ provider:
+ type: string
+ required: true
+ description: Identifies the provider of the ASD.
+ application_name:
+ type: string
+ required: true
+ description: Name to identify the application service described by this ASD
+ application_version:
+ type: string
+ required: true
+ description: Identifies the version of the application service described by this ASD.
+
+artifact_types:
+tosca.artifacts.asd.deploymentItem:
+ version: 0.1
+ derived_from: tosca.artifacts.Root
+ description: "Describes the artifact type of asd deployment item"
+ file: "Relative path of the artifact in the package"
+ properties:
+ item_id:
+ description: "The identifier of this asd deployment item"
+ required: true
+ type: string
+ artifact_type:
+ description: >
+ Specify artifact type.
+ required: true
+ type: string
+ constraints:
+ - valid_values: ["helm_chart"]
+ target_server:
+ description: >
+ Specify target server for artifact.
+ required: true
+ type: string
+ constraints:
+ - valid_values: [ "chartmuseum" ]
+ target_server_uri:
+ description: "URI of the target server"
+ required: true
+ type: string
\ No newline at end of file
--- /dev/null
+{\r
+ "tosca_definitions_version": "tosca_simple_yaml_1_3",\r
+ "data_types": {\r
+ "onap.datatypes.ToscaConceptIdentifier": {\r
+ "derived_from": "tosca.datatypes.Root",\r
+ "properties": {\r
+ "name": {\r
+ "type": "string",\r
+ "required": true\r
+ },\r
+ "version": {\r
+ "type": "string",\r
+ "required": true\r
+ }\r
+ }\r
+ },\r
+ "org.onap.datatypes.policy.clamp.acm.kserveAutomationCompositionElement.KserveInferenceEntity": {\r
+ "version": "1.0.0",\r
+ "derived_from": "tosca.datatypes.Root",\r
+ "properties": {\r
+ "kserveEntityId": {\r
+ "type": "onap.datatypes.ToscaConceptIdentifier",\r
+ "required": true,\r
+ "description": "The name and version of a Configuration Entity to be handled by the Kserve Automation Composition Element"\r
+ },\r
+ "name": {\r
+ "type": "string",\r
+ "required": true,\r
+ "description": "Inference service name to be created"\r
+ },\r
+ "payload": {\r
+ "type": "string",\r
+ "required": true,\r
+ "description": "Inference Service payload"\r
+ }\r
+ }\r
+ }\r
+ },\r
+ "node_types": {\r
+ "org.onap.policy.clamp.acm.Participant": {\r
+ "version": "1.0.1",\r
+ "derived_from": "tosca.nodetypes.Root",\r
+ "properties": {\r
+ "provider": {\r
+ "type": "string",\r
+ "required": false\r
+ }\r
+ }\r
+ },\r
+ "org.onap.policy.clamp.acm.AutomationCompositionElement": {\r
+ "version": "1.0.1",\r
+ "derived_from": "tosca.nodetypes.Root",\r
+ "properties": {\r
+ "provider": {\r
+ "type": "string",\r
+ "required": false\r
+ },\r
+ "participantType": {\r
+ "type": "onap.datatypes.ToscaConceptIdentifier",\r
+ "required": true\r
+ },\r
+ "startPhase": {\r
+ "type": "integer",\r
+ "required": false,\r
+ "constraints": [\r
+ {\r
+ "greater-or-equal": 0\r
+ }\r
+ ],\r
+ "metadata": {\r
+ "common": true\r
+ },\r
+ "description": "A value indicating the start phase in which this automation composition element will be started, the first start phase is zero. Automation Composition Elements are started in their start_phase order and stopped in reverse start phase order. Automation Composition Elements with the same start phase are started and stopped simultaneously"\r
+ },\r
+ "passiveToRunningTimeout": {\r
+ "type": "integer",\r
+ "required": false,\r
+ "constraints": [\r
+ {\r
+ "greater_or_equal": 0\r
+ }\r
+ ],\r
+ "default": 60,\r
+ "metadata": {\r
+ "common": true\r
+ },\r
+ "description": "The maximum time in seconds to wait for a state chage from passive to running"\r
+ },\r
+ "runningToPassiveTimeout": {\r
+ "type": "integer",\r
+ "required": false,\r
+ "constraints": [\r
+ {\r
+ "greater_or_equal": 0\r
+ }\r
+ ],\r
+ "default": 60,\r
+ "metadata": {\r
+ "common": true\r
+ },\r
+ "description": "The maximum time in seconds to wait for a state chage from running to passive"\r
+ },\r
+ "passiveToUninitializedTimeout": {\r
+ "type": "integer",\r
+ "required": false,\r
+ "constraints": [\r
+ {\r
+ "greater_or_equal": 0\r
+ }\r
+ ],\r
+ "default": 60,\r
+ "metadata": {\r
+ "common": true\r
+ },\r
+ "description": "The maximum time in seconds to wait for a state chage from passive to uninitialized"\r
+ }\r
+ }\r
+ },\r
+ "org.onap.policy.clamp.acm.AutomationComposition": {\r
+ "version": "1.0.1",\r
+ "derived_from": "tosca.nodetypes.Root",\r
+ "properties": {\r
+ "provider": {\r
+ "type": "string",\r
+ "required": false,\r
+ "metadata": {\r
+ "common": true\r
+ }\r
+ },\r
+ "elements": {\r
+ "type": "list",\r
+ "required": true,\r
+ "metadata": {\r
+ "common": true\r
+ },\r
+ "entry_schema": {\r
+ "type": "onap.datatypes.ToscaConceptIdentifier"\r
+ }\r
+ }\r
+ }\r
+ },\r
+ "org.onap.policy.clamp.acm.KserveAutomationCompositionElement": {\r
+ "version": "1.0.1",\r
+ "derived_from": "org.onap.policy.clamp.acm.AutomationCompositionElement",\r
+ "properties": {\r
+ "kserveInferenceEntities": {\r
+ "type": "list",\r
+ "required": true,\r
+ "entry_schema": {\r
+ "type": "org.onap.datatypes.policy.clamp.acm.kserveAutomationCompositionElement.KserveInferenceEntity",\r
+ "type_version": "1.0.0"\r
+ },\r
+ "description": "The configuration entities of Kserve inference service"\r
+ }\r
+ }\r
+ },\r
+ "org.onap.policy.clamp.acm.K8SMicroserviceAutomationCompositionElement": {\r
+ "version": "1.0.0",\r
+ "derived_from": "org.onap.policy.clamp.acm.AutomationCompositionElement",\r
+ "properties": {\r
+ "chart": {\r
+ "type": "string",\r
+ "required": true\r
+ },\r
+ "configs": {\r
+ "type": "list",\r
+ "required": false\r
+ },\r
+ "requirements": {\r
+ "type": "string",\r
+ "required": false\r
+ },\r
+ "templates": {\r
+ "type": "list",\r
+ "required": false,\r
+ "entry_schema": null\r
+ },\r
+ "values": {\r
+ "type": "string",\r
+ "required": true\r
+ }\r
+ }\r
+ }\r
+ },\r
+ "topology_template": {\r
+ "node_templates": {\r
+ "org.onap.policy.clamp.acm.KserveParticipant": {\r
+ "version": "2.3.4",\r
+ "type": "org.onap.policy.clamp.acm.Participant",\r
+ "type_version": "1.0.1",\r
+ "description": "Participant for Kserve requests",\r
+ "properties": {\r
+ "provider": "ONAP"\r
+ }\r
+ },\r
+ "org.onap.k8s.acm.K8SAutomationCompositionParticipant": {\r
+ "version": "2.3.4",\r
+ "type": "org.onap.policy.clamp.acm.Participant",\r
+ "type_version": "1.0.1",\r
+ "description": "Participant for K8S",\r
+ "properties": {\r
+ "provider": "ONAP"\r
+ }\r
+ },\r
+ "onap.policy.clamp.ac.element.KserveAutomationCompositionElement": {\r
+ "version": "1.2.3",\r
+ "type": "org.onap.policy.clamp.acm.KserveAutomationCompositionElement",\r
+ "type_version": "1.0.1",\r
+ "description": "Automation composition element for the Kserve Requests",\r
+ "properties": {\r
+ "provider": "ONAP",\r
+ "startPhase": 0,\r
+ "participantType": {\r
+ "name": "org.onap.policy.clamp.acm.KserveParticipant",\r
+ "version": "2.3.4"\r
+ },\r
+ "uninitializedToPassiveTimeout": 300,\r
+ "statusCheckInterval": 30\r
+ }\r
+ },\r
+ "onap.policy.clamp.ac.element.K8S_StarterAutomationCompositionElement": {\r
+ "version": "1.2.3",\r
+ "type": "org.onap.policy.clamp.acm.K8SMicroserviceAutomationCompositionElement",\r
+ "type_version": "1.0.0",\r
+ "description": "Automation composition element for the K8S microservice for AC Element Starter",\r
+ "properties": {\r
+ "provider": "ONAP",\r
+ "startPhase": 1,\r
+ "uninitializedToPassiveTimeout": 300,\r
+ "podStatusCheckInterval": 30\r
+ }\r
+ },\r
+ "onap.policy.clamp.ac.element.AutomationCompositionDefinition": {\r
+ "version": "1.2.3",\r
+ "type": "org.onap.policy.clamp.acm.AutomationComposition",\r
+ "type_version": "1.0.1",\r
+ "description": "Automation composition for rapp deployment",\r
+ "properties": {\r
+ "provider": "ONAP",\r
+ "elements": [\r
+ {\r
+ "name": "onap.policy.clamp.ac.element.K8S_StarterAutomationCompositionElement",\r
+ "version": "1.2.3"\r
+ },\r
+ {\r
+ "name": "onap.policy.clamp.ac.element.KserveAutomationCompositionElement",\r
+ "version": "1.2.3"\r
+ }\r
+ ]\r
+ }\r
+ }\r
+ }\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "ESInstance0",
+ "version": "1.0.1",
+ "compositionId": "DO_NOT_CHANGE_THIS_COMPOSITION_ID",
+ "description": "Energy Saving automation composition instance 0",
+ "elements": {
+ "d7be552e-bcc5-4478-b64d-797dbaec8f55": {
+ "id": "d7be552e-bcc5-4478-b64d-797dbaec8f55",
+ "definition": {
+ "name": "onap.policy.clamp.ac.element.K8S_StarterAutomationCompositionElement",
+ "version": "1.2.3"
+ },
+ "description": "Starter Automation Composition Element for the Hello World",
+ "properties": {
+ "chart": {
+ "chartId": {
+ "name": "energy-saving-rapp",
+ "version": "0.1.0"
+ },
+ "namespace": "nonrtric",
+ "releaseName": "energy-saving-rapp",
+ "podName": "energy-saving-rapp",
+ "repository": {
+ "repoName": "local",
+ "address": "UPDATE_THIS_ADDRESS"
+ },
+ "overrideParams": {
+ "environment.appId": "DO_NOT_CHANGE_THIS_RAPP_INSTANCE_ID",
+ "environment.smeDiscoveryEndpoint": "DO_NOT_CHANGE_THIS_SME_DISCOVERY_ENDPOINT"
+ }
+ }
+ }
+ },
+ "709c62b3-8918-41b9-a747-d21eb79c6c20": {
+ "id": "709c62b3-8918-41b9-a747-d21eb79c6c20",
+ "definition": {
+ "name": "onap.policy.clamp.ac.element.KserveAutomationCompositionElement",
+ "version": "1.2.3"
+ },
+ "description": "Starter Automation Composition Element for the Demo",
+ "properties": {
+ "kserveInferenceEntities": [
+ {
+ "kserveInferenceEntityId": {
+ "name": "es-aiml-model-entity",
+ "version": "1.0.1"
+ },
+ "name": "es-aiml-model",
+ "namespace": "kserve-test",
+ "payload": "{\"apiVersion\": \"serving.kserve.io/v1beta1\",\"kind\": \"InferenceService\",\"metadata\": {\"name\": \"es-aiml-model\"},\"spec\": {\"predictor\": {\"tensorflow\": {\"storageUri\": \"https://huggingface.co/saulgillEST/es-aiml-model/resolve/main/Model.zip\"}}}}"
+ }
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "K8ESInstance0",
+ "version": "1.0.1",
+ "compositionId": "DO_NOT_CHANGE_THIS_COMPOSITION_ID",
+ "description": "Energy Saving automation composition instance 0",
+ "elements": {
+ "d7be552e-bcc5-4478-b64d-797dbaec8f55": {
+ "id": "d7be552e-bcc5-4478-b64d-797dbaec8f55",
+ "definition": {
+ "name": "onap.policy.clamp.ac.element.K8S_StarterAutomationCompositionElement",
+ "version": "1.2.3"
+ },
+ "description": "Starter Automation Composition Element for the Hello World",
+ "properties": {
+ "chart": {
+ "chartId": {
+ "name": "energy-saving-rapp",
+ "version": "0.1.0"
+ },
+ "namespace": "nonrtric",
+ "releaseName": "energy-saving-rapp",
+ "podName": "energy-saving-rapp",
+ "repository": {
+ "repoName": "local",
+ "address": "http://10.101.1.170:18080"
+ },
+ "overrideParams": {
+ "environment.appId": "DO_NOT_CHANGE_THIS_RAPP_INSTANCE_ID",
+ "environment.smeDiscoveryEndpoint": "DO_NOT_CHANGE_THIS_SME_DISCOVERY_ENDPOINT"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "KserveInstance0",
+ "version": "1.0.1",
+ "compositionId": "DO_NOT_CHANGE_THIS_COMPOSITION_ID",
+ "description": "Demo automation composition instance 0",
+ "elements": {
+ "709c62b3-8918-41b9-a747-d21eb79c6c20": {
+ "id": "709c62b3-8918-41b9-a747-d21eb79c6c20",
+ "definition": {
+ "name": "onap.policy.clamp.ac.element.KserveAutomationCompositionElement",
+ "version": "1.2.3"
+ },
+ "description": "Starter Automation Composition Element for the Demo",
+ "properties": {
+ "kserveInferenceEntities": [
+ {
+ "kserveInferenceEntityId": {
+ "name": "es-aiml-model-entity",
+ "version": "1.0.1"
+ },
+ "name": "es-aiml-model",
+ "namespace": "kserve-test",
+ "payload": "{\"apiVersion\": \"serving.kserve.io/v1beta1\",\"kind\": \"InferenceService\",\"metadata\": {\"name\": \"es-aiml-model\"},\"spec\": {\"predictor\": {\"tensorflow\": {\"storageUri\": \"https://huggingface.co/saulgillEST/es-aiml-model/resolve/main/Model.zip\"}}}}"
+ }
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+[
+ {
+ "apiInvokerInformation": "DO_NOT_CHANGE_THIS_RAPP_INSTANCE_ID",
+ "apiList": [
+ {}
+ ],
+ "notificationDestination": "http://invoker-app1:8086/callback",
+ "onboardingInformation": {
+ "apiInvokerPublicKey": "{PUBLIC_KEY_INVOKER_1}",
+ "apiInvokerCertificate": "apiInvokerCertificate"
+ },
+ "requestTestNotification": true
+ }
+]
\ No newline at end of file
--- /dev/null
+[
+ {
+ "apiInvokerInformation": "DO_NOT_CHANGE_THIS_RAPP_INSTANCE_ID",
+ "apiList": [
+ { "apiName": "influxdb2-http" }
+ ],
+ "notificationDestination": "http://NOT_USED_HERE",
+ "onboardingInformation": {
+ "apiInvokerPublicKey": "{PUBLIC_KEY_INVOKER_1}",
+ "apiInvokerCertificate": "apiInvokerCertificate"
+ },
+ "requestTestNotification": true
+ }
+]
\ No newline at end of file
--- /dev/null
+[
+ {
+ "apiInvokerInformation": "DO_NOT_CHANGE_THIS_RAPP_INSTANCE_ID",
+ "apiList": [
+ { "apiName": "ES-Predictor-API" }
+ ],
+ "notificationDestination": "http://NOT_USED_HERE",
+ "onboardingInformation": {
+ "apiInvokerPublicKey": "{PUBLIC_KEY_INVOKER_1}",
+ "apiInvokerCertificate": "apiInvokerCertificate"
+ },
+ "requestTestNotification": true
+ }
+]
\ No newline at end of file
--- /dev/null
+[
+ {
+ "apiInvokerInformation": "DO_NOT_CHANGE_THIS_RAPP_INSTANCE_ID",
+ "apiList": [
+ { "apiName": "ncmp-dmi-plugin-http" }
+ ],
+ "notificationDestination": "http://NOT_USED_HERE",
+ "onboardingInformation": {
+ "apiInvokerPublicKey": "{PUBLIC_KEY_INVOKER_1}",
+ "apiInvokerCertificate": "apiInvokerCertificate"
+ },
+ "requestTestNotification": true
+ }
+]
\ No newline at end of file
--- /dev/null
+{\r
+ "apiProvDomInfo": "ES Model Provider domain",\r
+ "apiProvFuncs": [\r
+ {\r
+ "apiProvFuncInfo": "ES Prediction as APF",\r
+ "apiProvFuncRole": "APF",\r
+ "regInfo": {\r
+ "apiProvPubKey": "NOT USED here"\r
+ }\r
+ },\r
+ {\r
+ "apiProvFuncInfo": "ES Prediction as AEF",\r
+ "apiProvFuncRole": "AEF",\r
+ "regInfo": {\r
+ "apiProvPubKey": "NOT USED here"\r
+ }\r
+ }\r
+ ],\r
+ "regSec": "plain"\r
+}
\ No newline at end of file
--- /dev/null
+{\r
+ "apiProvDomInfo": "Provider domain",\r
+ "apiProvFuncs": [\r
+ {\r
+ "apiProvFuncInfo": "Energy Saving as APF",\r
+ "apiProvFuncRole": "APF",\r
+ "regInfo": {\r
+ "apiProvPubKey": "NOT USED here"\r
+ }\r
+ },\r
+ {\r
+ "apiProvFuncInfo": "Energy Saving as AEF",\r
+ "apiProvFuncRole": "AEF",\r
+ "regInfo": {\r
+ "apiProvPubKey": "NOT USED here"\r
+ }\r
+ }\r
+ ],\r
+ "regSec": "plain"\r
+}
\ No newline at end of file
--- /dev/null
+{\r
+ "apiName": "Energy Saving API Set 1",\r
+ "description": "Simple Energy Saving API",\r
+ "aefProfiles": [\r
+ {\r
+ "aefId": "Energy Saving as AEF",\r
+ "description": "Energy Saving API",\r
+ "versions": [\r
+ {\r
+ "apiVersion": "",\r
+ "resources": [\r
+ {\r
+ "resourceName": "energysaving",\r
+ "commType": "REQUEST_RESPONSE",\r
+ "uri": "/energysaving/v1",\r
+ "operations": [\r
+ "GET"\r
+ ]\r
+ }\r
+ ]\r
+ }\r
+ ],\r
+ "protocol": "HTTP_1_1",\r
+ "interfaceDescriptions": [\r
+ {\r
+ "ipv4Addr": "energysaving.nonrtric.svc.cluster.local",\r
+ "port": 8080\r
+ }\r
+ ]\r
+ }\r
+ ]\r
+}
\ No newline at end of file
--- /dev/null
+{\r
+ "apiName": "Energy Saving API Set 2",\r
+ "description": "Simple Energy Saving API for Subpath",\r
+ "aefProfiles": [\r
+ {\r
+ "aefId": "Energy Saving as AEF",\r
+ "description": "Energy Saving API for Subpath",\r
+ "versions": [\r
+ {\r
+ "apiVersion": "",\r
+ "resources": [\r
+ {\r
+ "resourceName": "energysaving",\r
+ "commType": "REQUEST_RESPONSE",\r
+ "uri": "/energysaving/v1/subpath1",\r
+ "operations": [\r
+ "GET"\r
+ ]\r
+ }\r
+ ]\r
+ }\r
+ ],\r
+ "protocol": "HTTP_1_1",\r
+ "interfaceDescriptions": [\r
+ {\r
+ "ipv4Addr": "energysaving.nonrtric.svc.cluster.local",\r
+ "port": 8080\r
+ }\r
+ ]\r
+ }\r
+ ]\r
+}
\ No newline at end of file
--- /dev/null
+{\r
+ "apiName": "es-predictor-http",\r
+ "description": "ES Prediction model endpoints",\r
+ "aefProfiles": [\r
+ {\r
+ "aefId": "ES Predictor as AEF",\r
+ "description": "ES predictor API",\r
+ "versions": [\r
+ {\r
+ "apiVersion": "",\r
+ "resources": [\r
+ {\r
+ "resourceName": "root",\r
+ "commType": "REQUEST_RESPONSE",\r
+ "uri": "/",\r
+ "operations": [\r
+ "POST",\r
+ "GET",\r
+ "PUT",\r
+ "DELETE",\r
+ "PATCH"\r
+ ]\r
+ }\r
+ ]\r
+ }\r
+ ],\r
+ "protocol": "HTTP_1_1",\r
+ "interfaceDescriptions": [\r
+ {\r
+ "ipv4Addr": "es-aiml-model-predictor.kserve-test.svc.cluster.local",\r
+ "port": 80\r
+ }\r
+ ]\r
+ }\r
+ ]\r
+}
\ No newline at end of file
--- /dev/null
+TOSCA-Meta-File-Version: 1.0\r
+CSAR-Version: 1.0\r
+Created-By: Ericsson Software Technology (O-RAN SC NONRTRIC team)\r
+Entry-Definitions: Definitions/asd.yaml\r
+ETSI-Entry-Manifest: asd.mf
\ No newline at end of file
--- /dev/null
+metadata:\r
+ application_name: rapp-energy-saving\r
+ application_provider: Ericsson Software Technology (O-RAN SC NONRTRIC team)\r
+ release_date_time: 2025-06-03T12:34:00+00:00\r
+ entry_definition_type: asd\r
+\r
+Source: asd.mf\r
+Source: Definitions/asd.yaml\r
+Source: Definitions/asd_types.yaml\r
+Source: TOSCA-Metadata/TOSCA.meta\r
+Source: Files/Acm/definition/compositions.json\r
+Source: Files/Acm/instances/es-instance.json\r
+Source: Files/Acm/instances/k8s-instance.json\r
+Source: Files/Acm/instances/kserve-instance.json\r
+Source: Files/SME/invokers/invoker-app1.json\r
+Source: Files/SME/invokers/invoker-ncmp.json\r
+Source: Files/SME/invokers/invoker-kserve-predictor.json\r
+Source: Files/SME/invokers/invoker-influxdb.json\r
+Source: Files/Sme/provider/provider-function-1.json\r
+Source: Files/Sme/provider/es-model-provider-function.json\r
+Source: Files/Sme/serviceapis/api-set-1.json\r
+Source: Files/Sme/serviceapis/api-set-2.json\r
+Source: Files/Sme/serviceapis/api-set-kserve-predictor.json\r
--- /dev/null
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+
+import json
+import random
+import requests
+import logging
+from sme_client import SMEClient
+
+logger = logging.getLogger(__name__)
+
+class ASSIST(object):
+
+ def __init__(self):
+ self.kserve_url = None
+ self.invoker_id = None
+ self.api_name = None
+ self.resource_name = None
+ self.config()
+ self.get_url_from_sme()
+
+ def config(self):
+ with open('config.json', 'r') as f:
+ config = json.load(f)
+
+ assist_config = config.get("SME", {})
+ self.invoker_id = assist_config.get("kserve_invoker_id")
+ self.api_name = assist_config.get("kserve_api_name")
+ self.resource_name = assist_config.get("kserve_resource_name")
+
+ def get_url_from_sme(self):
+ sme_client = SMEClient(
+ invoker_id=self.invoker_id,
+ api_name=self.api_name,
+ resource_name=self.resource_name
+ )
+
+ self.kserve_url = sme_client.discover_service()
+
+ if self.kserve_url is None:
+ logger.error("Failed to discover KServe URL.")
+ return None
+ logger.info(f"Discovered KServe URL: {self.kserve_url}")
+
+ def send_request_to_server(self, json_data, randomize=False):
+
+ if(not randomize):
+ with open('input.json', 'w') as f:
+ json.dump(json_data, f)
+
+ # Commented out for local testing
+ # url = 'http://localhost:8080/v1/models/es-aiml-model:predict'
+ # headers = {'Host': 'es-aiml-model-predictor-default.default.svc.cluster.local'}
+ url = self.kserve_url + 'v1/models/es-aiml-model:predict'
+ host = self.kserve_url.split('/')[2].split(':')[0]
+ headers = {'Host': host }
+
+ with open('input.json', 'rb') as f:
+ response = requests.post(url, headers=headers, data=f)
+ logger.info("Prediction result")
+ logger.info(response.text)
+ return response.status_code, response.text
+ else:
+ data = json.dumps({
+ "predictions": self.random_predictions()
+ })
+
+ logger.info("Prediction result")
+ logger.info(data)
+
+ return 200, data
+
+ def random_predictions(self):
+ predictions = []
+ # Generate random decision for all rows
+ power_value = random.random()
+ for _ in range(3): # Generate 3 rows of predictions
+ if power_value < 0.5: # 50% chance for all values < 0.4
+ row = [random.uniform(0.0, 0.039) for _ in range(3)]
+ else: # 50% chance for all values >= 0.4
+ row = [random.uniform(0.4, 1.0) for _ in range(3)]
+ predictions.append(row)
+
+ return predictions
\ No newline at end of file
--- /dev/null
+{
+ "SME": {
+ "sme_discovery_endpoint": "http://localhost:31575/service-apis/v1/allServiceAPIs",
+ "host": "localhost",
+ "port": 31575,
+ "ncmp_invoker_id": "6a965002-ed7c-4f69-855c-ab9196f86e61",
+ "ncmp_api_name": "ncmp-dmi-plugin-http",
+ "ncmp_resource_name": "root",
+ "resource_id": "/_3gpp-common-managed-element:ManagedElement=ManagedElement-002/_3gpp-nr-nrm-gnbdufunction:GNBDUFunction=GNBDUFunction-001/_3gpp-nr-nrm-nrcelldu:NRCellDU=NRCellDU-001/attributes",
+ "influxdb_invoker_id": "6a965002-ed7c-4f69-855c-ab9196f86e61",
+ "influxdb_api_name": "influxdb2-http",
+ "influxdb_resource_name": "root",
+ "kserve_invoker_id": "6a965002-ed7c-4f69-855c-ab9196f86e61",
+ "kserve_api_name": "es-predictor-http",
+ "kserve_resource_name": "root",
+ "teiv_invoker_id": "6a965002-ed7c-4f69-855c-ab9196f86e61",
+ "teiv_api_name": "topology-exposure-http",
+ "teiv_resource_name": "root",
+ "odufunction_id": "GNBDUFunction-001"
+ },
+ "DB": {
+ "host": "localhost",
+ "port": 8086,
+ "bucket": "pm-bucket",
+ "org": "est",
+ "token": "WcrfLfqC63uCxfDa15C1sb6WtG5fhzrS",
+ "user": "admin",
+ "password": "mySuP3rS3cr3tT0keN",
+ "example_data_file": "example_data.json",
+ "database": "pm-bucket",
+ "measurement": "CellReports",
+ "ssl": false,
+ "address": "http://localhost:8086"
+ }
+}
\ No newline at end of file
--- /dev/null
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+
+import time
+import logging
+from influxdb.exceptions import InfluxDBClientError, InfluxDBServerError
+from requests.exceptions import RequestException, ConnectionError
+import influxdb_client
+from datetime import datetime, timedelta
+import random
+import json
+from sme_client import SMEClient
+from influxdb_client.client.write_api import SYNCHRONOUS
+import pandas as pd
+
+logger = logging.getLogger(__name__)
+
+class DATABASE(object):
+
+ def __init__(self, dbname='Timeseries', user='user', password='password', host="influxdb_ip", port='influxdb_port', path='', ssl=False):
+ self.token = None
+ self.org = None
+ self.bucket = None
+ self.data = None
+ self.host = host
+ self.port = port
+ self.user = user
+ self.password = password
+ self.ssl = ssl
+ self.dbname = dbname
+ self.client = None
+ self.address = None
+ self.influx_invoker_id = None
+ self.influx_api_name = None
+ self.influx_resource_name = None
+ self.config()
+
+ # Set pandas options to display all rows and columns
+ pd.set_option('display.max_rows', None) # Show all rows
+ pd.set_option('display.max_columns', None) # Show all columns
+ pd.set_option('display.width', 1000) # Adjust the width to avoid line breaks
+ pd.set_option('display.colheader_justify', 'left') # Align column headers to the left
+
+ def get_url_from_sme(self):
+ sme_client = SMEClient(
+ invoker_id=self.influx_invoker_id,
+ api_name=self.influx_api_name,
+ resource_name=self.influx_resource_name
+ )
+
+ self.influx_url = sme_client.discover_service()
+
+ logger.info("InfluxDB URL: {}".format(self.influx_url))
+
+ if self.influx_url is not None:
+ self.address = self.influx_url
+ logger.debug(f"InfluxDB URL: {self.influx_url}")
+ self.host = self.influx_url.split(":")[1].replace("//", "")
+ logger.debug(f"InfluxDB Host: {self.host}")
+ self.port = self.influx_url.split(":")[2].split("/")[0]
+ logger.debug(f"InfluxDB Port: {self.port}")
+ self.address = self.influx_url.rstrip('/')
+ logger.debug(f"InfluxDB Address: {self.address}")
+ else:
+ logger.error("Failed to discover InfluxDB URL from SME.")
+
+ # Connect with influxdb
+ def connect(self):
+ if self.client is not None:
+ self.client.close()
+
+ try:
+ self.client = influxdb_client.InfluxDBClient(url=self.address, org=self.org, token=self.token)
+ version = self.client.version()
+ logger.info("Connected to Influx Database, InfluxDB version : {}".format(version))
+ return True
+
+ except (RequestException, InfluxDBClientError, InfluxDBServerError, ConnectionError):
+ logger.error("Failed to establish a new connection with InflulxDB, Please check your url/hostname")
+ time.sleep(120)
+
+ # Query information
+ def read_data(self, train=False, valid=False, limit=False):
+
+ self.data = None
+ query = 'from(bucket:"{}")'.format(self.bucket)
+ query += '|> range(start: -10m) '
+ query += ' |> filter(fn: (r) => r["_measurement"] == "o-ran-pm")'
+ query += ' |> filter(fn: (r) => r["_field"] == "CellID" or r["_field"] == "DRB.UEThpUl" or r["_field"] == "RRU.PrbUsedUl" or r["_field"] == "PEE.AvgPower") '
+ #query += ' |> filter(fn: (r) => r["_field"] == "CellID" or r["_field"] == "RRC.ConnMean" or r["_field"] == "DRB.UEThpUl" or r["_field"] == "RRU.PrbUsedUl" or r["_field"] == "PEE.AvgPower") '
+ query += ' |> pivot(rowKey: ["_time"], columnKey: ["_field"], valueColumn: "_value") '
+ result = self.query(query)
+ self.data = result
+ return result
+
+ # Query data
+ def query(self, query):
+ while True:
+ try:
+ query_api = self.client.query_api()
+ result = query_api.query_data_frame(org=self.org, query=query)
+ logger.debug(f'Cell data : {result}')
+ return result
+ except (RequestException, InfluxDBClientError, InfluxDBServerError, ConnectionError) as e:
+ logger.error(f'Failed to query influxdb: {e}, retrying in 60 seconds...')
+ time.sleep(60)
+
+ def mapping(self, data):
+ data[['S', 'B', 'C']] = data['CellID'].str.extract(r'S(\d+)/[BN](\d+)/C(\d+)')
+ data[['S', 'B', 'C']] = data[['S', 'B', 'C']].astype(int)
+ data = data.sort_values(by=['B', 'S', 'C'])
+ data['cellidnumber'] = data.groupby(['B', 'S', 'C']).ngroup().add(1)
+ data = data.drop(['S', 'B', 'C'], axis=1)
+ return data
+
+ def generate_synthetic_data(self):
+ data = []
+ fields = ["CellID", "DRB.UEThpUl", "RRU.PrbUsedUl", "PEE.AvgPower"]
+ # fields = ["CellID", "DRB.UEThpUl", "RRU.PrbUsedUl", "PEE.AvgPower", "RRC.ConnMean"]
+ other_fields = ["TestFilterField1", "TestFilterField2", "TestFilterField3", "TestFilterField4"]
+ measurements = ["o-ran-pm", "test-filter-measurement1", "test-filter-measurement2", "test-filter-measurement3", "test-filter-measurement4"]
+
+ # Generate matching records (synchronized _time for each group of 4)
+ for _ in range(50): # 50 records, each with 4 rows sharing the same time
+ common_time = datetime.now() - timedelta(minutes=random.randint(0, 60))
+ iso_time = common_time.isoformat()
+
+ for field in fields:
+ value = (
+ f"S{random.randint(1,9)}/B{random.randint(1,9)}/C{random.randint(1,9)}"
+ if field == "CellID"
+ else round(random.uniform(1, 100), 2)
+ )
+ record = {
+ "_time": iso_time,
+ "_measurement": "o-ran-pm",
+ "_field": field,
+ "_value": value
+ }
+ data.append(record)
+
+ # Generate non-matching records (randomized)
+ for _ in range(50):
+ record = {
+ "_time": (datetime.now() - timedelta(minutes=random.randint(0, 60))).isoformat(),
+ "_measurement": random.choice(measurements),
+ "_field": random.choice(other_fields),
+ "_value": str(round(random.uniform(0, 100), 2))
+ }
+ data.append(record)
+
+ # Log data to be written
+ print(pd.DataFrame(data).to_string())
+
+ self.write_synthetic_data_to_db(data)
+
+
+ def write_synthetic_data_to_db(self, data):
+
+ write_api = self.client.write_api(write_options=SYNCHRONOUS)
+ for record in data:
+ point = influxdb_client.Point(record["_measurement"]) \
+ .field(record["_field"], record["_value"]) \
+ .time(datetime.fromisoformat(record["_time"]))
+ write_api.write(bucket=self.bucket, org=self.org, record=point)
+ write_api.flush()
+ write_api.close()
+ logger.info("Synthetic data successfully written to InfluxDB.")
+
+ def config(self):
+
+ with open('config.json', 'r') as f:
+ config = json.load(f)
+
+ # Load the SME configuration from the JSON file
+ sme_config = config.get("SME", {})
+ self.influx_invoker_id = sme_config.get("influxdb_invoker_id")
+ self.influx_api_name = sme_config.get("influxdb_api_name")
+ self.influx_resource_name = sme_config.get("influxdb_resource_name")
+
+ # Initialize the InfluxDB client
+ influx_config = config.get("DB", {})
+ self.token = influx_config.get("token")
+ self.org = influx_config.get("org")
+ self.bucket = influx_config.get("bucket")
+ self.address = influx_config.get("address")
+ self.host = influx_config.get("host")
+ self.port = influx_config.get("port")
+ self.ssl = influx_config.get("ssl")
+ self.dbname = influx_config.get("database")
+ self.meas = influx_config.get("measurement")
+ self.user = influx_config.get("user")
+ self.password = influx_config.get("password")
--- /dev/null
+{"signature_name": "serving_default", "instances": [[[42.59, 50.3, 73.18]]]}
\ No newline at end of file
--- /dev/null
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+
+import argparse
+import time
+import pandas as pd
+import schedule
+import logging
+from data import DATABASE
+from assist import ASSIST
+from ncmp_client import NCMP_CLIENT
+from teiv_client import TEIV_CLIENT
+import json
+
+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+logger = logging.getLogger(__name__)
+
+
+class ESrapp():
+ def __init__(self, generate_db_data=True, use_sme_db=False, random_predictions=False):
+
+ # Initialize the local storage of cell status
+ self.cell_power_status = {}
+ self.teiv_cells = {}
+
+ # Initialize the database and prediction client
+ self.db = DATABASE()
+ self.assist=ASSIST()
+
+ self.random_predictions = random_predictions
+
+ if use_sme_db:
+ # Get the InfluxDB URL from SME
+ self.db.get_url_from_sme()
+
+ self.db.connect()
+
+ if generate_db_data:
+ # Use local InfluxDB and generate synthetic data - only for local testing
+ self.db.generate_synthetic_data()
+
+
+ self.threshold = 50
+ # Initialize the NCMP client - allows us to query the cells from the RAN and power them on/off
+ self.ncmp_client = NCMP_CLIENT()
+ self.index = 1
+
+ # Get the ODU function ID from the database
+ self.teiv_client = TEIV_CLIENT()
+ #self.get_teiv_cells()
+ # Create Policy Manager instance
+ #self.policy_manager = PolicyManager(base_url="http://192.168.8.111:32080/a1mediator/A1-P/v2", policy_type_id=20008)
+
+ # Create policy type and policy instance
+ #self.policy_manager.create_policy_type()
+
+ def entry(self):
+ schedule.every(10).seconds.do(self.inference)
+
+ while True:
+ schedule.run_pending()
+
+ # Send data to ML rApp
+ def inference(self):
+ data = self.db.read_data()
+
+ if data.empty:
+ logger.info("No data to process... skipping this iteration of inference.")
+ return
+
+ data_mapping = self.mapping(data)
+ groups = data_mapping.groupby("CellID")
+ for group_name, group_data in groups:
+ json_data = self.generate_json_data(group_data)
+ logger.info(f"Send data to ML rApp {group_name}: {json_data}")
+ status_code, response_text = self.assist.send_request_to_server(json_data, randomize=self.random_predictions)
+ if not self.check_and_perform_action(response_text):
+ cell_id_number = group_data['cellidnumber'].iloc[0]
+ logger.info(f"Turn on the cell {group_name}")
+ # Create policy instance with the cell_id_number before performing action
+ #self.policy_manager.create_policy_instance(cell_id_number)
+ # Wait for 3 seconds before performing the action
+ time.sleep(3)
+
+ if cell_id_number not in self.cell_power_status:
+ logger.debug(f"Cell {cell_id_number} not in local cache. Adding it...")
+ self.cell_power_status[cell_id_number] = "off"
+ # Check if the cell is already powered on
+ if self.cell_power_status[cell_id_number] == "on":
+ logger.debug(f"Cell {cell_id_number} is already powered on.")
+ # continue
+ else:
+ self.ncmp_client.power_on_cell(cell_id_number)
+ self.cell_power_status[cell_id_number] = "on"
+ else:
+ cell_id_number = group_data['cellidnumber'].iloc[0]
+ logger.info(f"Turn off the cell {group_name}")
+ # Create policy instance with the cell_id_number before performing action
+ #self.policy_manager.create_policy_instance(cell_id_number)
+ # Wait for 3 seconds before performing the action
+ time.sleep(3)
+
+ if cell_id_number not in self.cell_power_status:
+ logger.debug(f"Cell {cell_id_number} not in local cache. Adding it...")
+ self.cell_power_status[cell_id_number] = "on"
+
+ if self.cell_power_status[cell_id_number] == "off":
+ logger.debug(f"Cell {cell_id_number} is already powered off.")
+ # continue
+ else:
+ self.ncmp_client.power_off_cell(cell_id_number)
+ self.cell_power_status[cell_id_number] = "off"
+
+ # Generate the input data for ML rApp
+ def generate_json_data(self, data):
+ # rrc_conn_mean_values = data["RRC.ConnMean"].tolist()
+ drb_ue_thp_ul_values = data["DRB.UEThpUl"].tolist()
+ rru_prb_used_ul_values = data["RRU.PrbUsedUl"].tolist()
+ pee_avg_power_values = data["PEE.AvgPower"].tolist()
+
+ instances = [
+ [
+ [drb, rru, pee]
+ for drb, rru, pee in zip(
+ drb_ue_thp_ul_values,
+ rru_prb_used_ul_values,
+ pee_avg_power_values
+ )
+ ]
+ ]
+
+ json_data = {"signature_name": "serving_default", "instances": instances}
+ logger.debug(f'Generated JSON data: {json_data}')
+ return json_data
+ # Mapping CellID and Cell name
+ def mapping(self, data):
+ data = pd.DataFrame(data)
+ # TODO: This regex is not likely to match all cell IDs. Will need to be improved.
+ data[['S', 'B', 'C']] = data['CellID'].str.extract(r'S(\d+)/[BN](\d+)/C(\d+)')
+ data[['S', 'B', 'C']] = data[['S', 'B', 'C']].astype(int)
+ data = data.sort_values(by=['B', 'S', 'C'])
+ data['cellidnumber'] = data.groupby(['B', 'S', 'C']).ngroup().add(1)
+ data = data.drop(['S', 'B', 'C'], axis=1)
+ return data
+
+
+ def check_and_perform_action(self, data):
+ response_obj = json.loads(data)
+ predictions = response_obj.get('predictions')
+ if predictions:
+ for prediction in predictions:
+ if all(pred < 0.04 for pred in prediction):
+ return True
+ elif all(pred >= 0.04 for pred in prediction):
+ return False
+ return False
+
+
+
+ # def get_teiv_cells(self):
+ # # Get the TEIV cells from the teiv
+ # odufunction_id = self.teiv_client.odufunction_id
+ # logger.info("ODU function ID: " + str(odufunction_id))
+ # # Get the NRCellDUs from the TEIV
+ # nrcelldus = self.teiv_client.get_nrcelldus(odufunction_id)
+ # if nrcelldus is None:
+ # logger.error("Failed to retrieve NRCellDUs.")
+ # return None
+ # # Extract the cell IDs from the NRCellDUs
+ # self.teiv_cells = self.teiv_client.search_entity_data_for_ids(nrcelldus)
+ # logger.info("NRCellDUs: " + str(self.nrcelldus))
+
+
+if __name__ == "__main__":
+
+ def str2bool(v):
+ if isinstance(v, bool):
+ return v
+ if v.lower() in ('yes', 'true', 't', 'y', '1'):
+ return True
+ elif v.lower() in ('no', 'false', 'f', 'n', '0'):
+ return False
+ else:
+ raise argparse.ArgumentTypeError('Boolean value expected.')
+
+ parser = argparse.ArgumentParser(description="Run ESrapp with optional localdb data generation.")
+ parser.add_argument("--generate_db_data", type=str2bool, default=True, help="Set to True to generate data in db.")
+ parser.add_argument("--use_sme_db", type=str2bool, default=False, help="Set to True use SME url for DB.")
+ parser.add_argument("--random_predictions", type=str2bool, default=False, help="Set to True to generate random predictions.")
+ args = parser.parse_args()
+
+ rapp = ESrapp(generate_db_data=args.generate_db_data, use_sme_db=args.use_sme_db, random_predictions=args.random_predictions)
+ logger.debug("ES xApp starting")
+
+ rapp.entry()
--- /dev/null
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+
+import json
+import logging
+from sme_client import SMEClient
+
+logger = logging.getLogger(__name__)
+
+class NCMP_CLIENT(object):
+ def __init__(self):
+ with open('config.json', 'r') as f:
+ config = json.load(f)
+
+ sme_config = config.get("SME", {})
+ self.host = sme_config.get("host")
+ self.port = sme_config.get("port")
+ self.ncmp_invoker_id = sme_config.get("ncmp_invoker_id")
+ self.ncmp_api_name = sme_config.get("ncmp_api_name")
+ self.ncmp_resource_name = sme_config.get("ncmp_resource_name")
+ self.resourse_identifier = sme_config.get("resource_id")
+ self.ncmp_uri = None
+
+ sme_client = SMEClient(
+ invoker_id=self.ncmp_invoker_id,
+ api_name=self.ncmp_api_name,
+ resource_name=self.ncmp_resource_name
+ )
+
+ self.ncmp_uri = sme_client.discover_service()
+
+ print("Discovered NCMP URI: ", self.ncmp_uri)
+
+ def power_off_cell(self, endpoint):
+
+ # This log is all it does in testing
+ logger.info("Powering-off cell " + str(endpoint) + " successful")
+
+ # It expects the SME ncmp endpoint to call power off
+ # endpoint_with_query = f"{endpoint}?resourceIdentifier={self.resourse_identifier}"
+ #
+ # headers = {
+ # "Content-Type": "application/json"
+ # }
+ #
+ # body = {
+ # "attributes": {
+ # "administrativeState": "LOCKED"
+ # }
+ # }
+ #
+ # response = requests.patch(endpoint_with_query, data=body, headers=headers)
+ #
+ # if response.status_code == 200:
+ # logger.info("Power-off successful. " + response.text)
+ # return response.json()
+ # else:
+ # logger.error(f"Error in connection to NCMP for power off: {response.status_code}")
+ # logger.error(response.text)
+ # return None
+
+ def power_on_cell(self, endpoint):
+
+ # This log is all it does in testing
+ logger.info("Powering-on cell " + str(endpoint) + " successful")
+
+ # It expects the SME ncmp endpoint to call power on
+ # endpoint_with_query = f"{endpoint}?resourceIdentifier={self.resourse_identifier}"
+ #
+ # headers = {
+ # "Content-Type": "application/json"
+ # }
+ #
+ # body = {
+ # "attributes": {
+ # "administrativeState": "UNLOCKED"
+ # }
+ # }
+ #
+ # response = requests.patch(endpoint_with_query, data=body, headers=headers)
+ #
+ # if response.status_code == 200:
+ # logger.info("Power-on successful. " + response.text)
+ # return response.json()
+ # else:
+ # logger.error(f"Error in connection to NCMP for power on: {response.status_code}")
+ # logger.error(response.text)
+ # return None
+
+# if __name__ == "__main__":
+# logging.basicConfig(level=logging.INFO) # Set up logging for better visibility
+#
+# # Instantiate the NcmpClient
+# ncmp_client = NCMP_CLIENT()
+
+# Test the discover_ncmp_via_sme method
+# Discover service
+
+# Test the power_off_cell method
+#if discovery_result:
+# power_off_result = ncmp_client.power_off_cell(discovery_result)
+# print(f"Power Off Result: {power_off_result}")
+
+# Test the power_on_cell method
+#if discovery_result:
+# power_on_result = ncmp_client.power_on_cell(discovery_result)
+# print(f"Power On Result: {power_on_result}")
+
--- /dev/null
+influxdb_client
+pandas
+requests
+influxdb
+pandas
+schedule
--- /dev/null
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+
+import requests
+import logging
+import json
+
+logger = logging.getLogger(__name__)
+
+class SMEClient:
+ def __init__(self, invoker_id, api_name, resource_name):
+ self.host = None
+ self.port = None
+ self.invoker_id = invoker_id
+ self.api_name = api_name
+ self.resource_name = resource_name
+
+ with open('config.json', 'r') as f:
+ config = json.load(f)
+
+ sme_config = config.get("SME", {})
+ self.host = sme_config.get("host")
+ self.port = sme_config.get("port")
+ self.sme_discovery_endpoint = sme_config.get("sme_discovery_endpoint")
+
+ def discover_service(self):
+ #url = f"http://{self.host}:{self.port}/service-apis/v1/allServiceAPIs"
+ #url = f"http://{self.sme_discovery_endpoint}"
+ query = f"api-invoker-id=api_invoker_id_{self.invoker_id}&api-name={self.api_name}"
+ full_url = f"{self.sme_discovery_endpoint}?{query}"
+ logger.info(f"Full URL for service discovery: {full_url}")
+
+ try:
+ response = requests.get(full_url, headers={"Content-Type": "application/json"})
+ if response.status_code == 200:
+ logger.info("Service discovery successful.")
+ return self.parse_uri(response.json())
+ else:
+ logger.error(f"Failed to discover service. Status code: {response.status_code}")
+ logger.error(response.text)
+ return None
+ except requests.RequestException as e:
+ logger.error(f"Error during service discovery: {e}")
+ return None
+
+ def parse_uri(self, response):
+ try:
+ logger.debug("Parsing SME response to extract URI.")
+ service = response["serviceAPIDescriptions"][0]
+ profile = service["aefProfiles"][0]
+ version = profile["versions"][0]
+ resource = next(
+ (res for res in version["resources"] if res["resourceName"] == self.resource_name),
+ None
+ )
+ uri = resource["uri"] if resource else None
+
+ interface = profile["interfaceDescriptions"][0]
+ ipv4_addr = interface.get("ipv4Addr")
+ port = interface.get("port")
+
+ return f"http://{ipv4_addr}:{port}{uri}" if uri else f"http://{ipv4_addr}:{port}"
+ except (KeyError, IndexError, TypeError) as e:
+ logger.error(f"Error parsing URI: {e}")
+ return None
\ No newline at end of file
--- /dev/null
+# ============LICENSE_START===============================================
+# Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+# ========================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=================================================
+#
+
+import json
+import logging
+import requests
+import urllib.parse
+from sme_client import SMEClient
+
+logger = logging.getLogger(__name__)
+
+class TEIV_CLIENT(object):
+ def __init__(self):
+ with open('config.json', 'r') as f:
+ config = json.load(f)
+
+ sme_config = config.get("SME", {})
+ self.host = sme_config.get("host")
+ self.port = sme_config.get("port")
+ self.teiv_invoker_id = sme_config.get("teiv_invoker_id")
+ self.teiv_api_name = sme_config.get("teiv_api_name")
+ self.teiv_resource_name = sme_config.get("teiv_resource_name")
+ self.odufunction_id = sme_config.get("odufunction_id")
+ self.teiv_uri = None
+
+ sme_client = SMEClient(
+ invoker_id=self.teiv_invoker_id,
+ api_name=self.teiv_api_name,
+ resource_name=self.teiv_resource_name
+ )
+
+ self.teiv_uri = sme_client.discover_service()
+
+ print("Discovered TEIV URI: ", self.teiv_uri)
+
+ def get_nrcelldus(self, odufunction_id):
+
+ scope_filter = f"/provided-by-oduFunction[@id=\"{odufunction_id}\"]"
+ encoded_scope_filter = urllib.parse.quote(scope_filter)
+ endpoint = (
+ f"{self.teiv_uri}domains/RAN/entity-types/NRCellDU/entities?"
+ f"scopeFilter={encoded_scope_filter}&targetFilter=/attributes;/sourceIds"
+ )
+ logger.info("TEIV full endpoint: " + endpoint)
+ response = requests.get(endpoint)
+
+ if response.status_code == 200:
+ logger.info("Retrieved NRCellDUs. " + response.text)
+ return response.json()
+ else:
+ logger.error(f"Error in connection to TEIV: {response.status_code}")
+ logger.error(response.text)
+ return None
+
+ def search_entity_data_for_ids(self, json_data):
+ items = json_data.get("items", [])
+ ids = []
+ for item in items:
+ for key in item:
+ ids.extend(entity.get('id') for entity in item[key] if 'id' in entity)
+ return ids
+
+# if __name__ == "__main__":
+# logging.basicConfig(level=logging.INFO) # Set up logging for better visibility
+#
+# # Instantiate the TEIVClient
+# teiv_client = TEIV_CLIENT()
+#
+# nrcelldu_json = teiv_client.get_nrcelldus(teiv_client.odufunction_id)
+# nrcelldu_ids = teiv_client.search_entity_data_for_ids(nrcelldu_json)
\ No newline at end of file
# ============LICENSE_START===============================================
# Copyright (C) 2023 Nordix Foundation. All rights reserved.
-# Copyright (C) 2024 OpenInfra Foundation Europe. All rights reserved.
+# Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved.
# ========================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
exit 1
fi
+ORIGINAL_DIR=$(pwd)
DIRECTORY=${1%/}
-PACKAGENAME="$DIRECTORY.csar"
+PACKAGENAME="$(basename "$DIRECTORY").csar"
HELM_DIR="$DIRECTORY/Artifacts/Deployment/HELM"
EXCLUDE_DIRS=()
if [ -d "$DIRECTORY" ]; then
checkHelmPackage
- rm -f $PACKAGENAME 2> /dev/null
+ rm -f $ORIGINAL_DIR/$PACKAGENAME 2> /dev/null
pushd $DIRECTORY
- zip -r ../$PACKAGENAME * $([ ${#EXCLUDE_DIRS[@]} -gt 0 ] && printf " -x %s" "${EXCLUDE_DIRS[@]}")
+ zip -r $ORIGINAL_DIR/$PACKAGENAME * $([ ${#EXCLUDE_DIRS[@]} -gt 0 ] && printf " -x %s" "${EXCLUDE_DIRS[@]}")
popd
echo -e "rApp package $PACKAGENAME generated."
else
#!/bin/bash
# ============LICENSE_START===============================================
-# Copyright (C) 2024 OpenInfra Foundation Europe. All rights reserved.
+# Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved.
# ========================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
#
echo "######### Patching Sample rApps #########"
+
+# Default values
+IP_ADDRESS=$(hostname -I | sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 | awk '{print $1}')
+PORT=8879
+
+# Parse optional arguments
+while getopts "i:p:r:" opt; do
+ case $opt in
+ i) IP_ADDRESS="$OPTARG" ;;
+ p) PORT="$OPTARG" ;;
+ r) RAPP="$OPTARG" ;;
+ *) echo "Usage: $0 [-i IP_ADDRESS] [-p PORT] [-r RAPP]" >&2; exit 1 ;;
+ esac
+done
+
+echo "IP Address : $IP_ADDRESS"
+echo "Port : $PORT"
+echo "Rapp Directory: $RAPP"
+
+
echo "Replacing hardcoded variables inside sample-rapp-generator:"
echo "UPDATE_THIS_MACHINE_IP"
echo "UPDATE_THIS_CHART_MUSEUM_GET_CHARTS_URI"
echo "UPDATE_THIS_CHART_MUSEUM_POST_CHARTS_URI"
+echo "UPDATE_THIS_ADDRESS"
+
CWD=$(pwd)
export WORKSPACE="$CWD/../../sample-rapp-generator"
-IP_ADDRESS=$(hostname -I | sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 | awk '{print $1}')
-echo "IP Address : $IP_ADDRESS"
-
-CHART_REPO_GET_URI=${CHART_REPO_GET_URI:-'http://'$IP_ADDRESS':8879/charts'}
-CHART_REPO_POST_URI=${CHART_REPO_POST_URI:-'http://'$IP_ADDRESS':8879/charts/api/charts'}
+CHARTREPO_IP_PORT=${CHART_REPO_GET_URI:-'http://'$IP_ADDRESS':'$PORT''}
+echo "CHARTREPO_IP_PORT: $CHARTREPO_IP_PORT"
+CHART_REPO_GET_URI=${CHART_REPO_GET_URI:-'http://'$IP_ADDRESS':'$PORT'/charts'}
+CHART_REPO_POST_URI=${CHART_REPO_POST_URI:-'http://'$IP_ADDRESS':'$PORT'/api/charts'}
echo "Replacing charts repo post url in yaml files....."
echo "Chart repository post URI : $CHART_REPO_POST_URI"
-for file in $(find $WORKSPACE -type f -name "*.yaml"); do
+for file in $(find $WORKSPACE/$RAPP -type f -name "*.yaml"); do
sed -i "s|UPDATE_THIS_CHART_MUSEUM_POST_CHARTS_URI|${CHART_REPO_POST_URI}|g" "$file"
if grep -q "$CHART_REPO_POST_URI" "$file"; then
echo "$file is updated."
echo "Replacing charts repo get url and machine ip in json files....."
echo "Chart repository get URI : $CHART_REPO_GET_URI"
-for file in $(find $WORKSPACE -type f -name "*.json"); do
+for file in $(find $WORKSPACE/$RAPP -type f \( -name "*.yaml" -o -name "*.json" \)); do
sed -i "s|UPDATE_THIS_CHART_MUSEUM_GET_CHARTS_URI|${CHART_REPO_GET_URI}|g" "$file"
if grep -q "$CHART_REPO_GET_URI" "$file"; then
echo "$file is updated."
if grep -q "$IP_ADDRESS" "$file"; then
echo "UPDATE_THIS_MACHINE_IP updated in file: $file"
fi
+
+ sed -i "s|UPDATE_THIS_ADDRESS|${CHARTREPO_IP_PORT}|g" "$file"
+ if grep -q "$CHARTREPO_IP_PORT" "$file"; then
+ echo "UPDATE_THIS_ADDRESS updated in file: $file"
+ fi
+
done
echo "UPDATE_THIS_MACHINE_IP=$IP_ADDRESS"
echo "UPDATE_THIS_CHART_MUSEUM_GET_CHARTS_URI=$CHART_REPO_GET_URI"