From b31b12b067953a4a8e9d4d9389b0f23b3a062a78 Mon Sep 17 00:00:00 2001 From: "saul.gill" Date: Fri, 30 May 2025 15:26:39 +0100 Subject: [PATCH] Added demo energy saving rapp Added all rapp artifacts Altered generate.sh to work when executed from anywhere Altered patch-sample-rapps.sh to add chartmuseum url Added README instructions Issue-ID: NONRTRIC-1078 Change-Id: Ic3b393b94457e476b707f8c80f639a4647b8ece8 Signed-off-by: saul.gill --- ...Demo_Energy_Saving_rApp.postman_collection.json | 997 +++++++++++++++++++++ sample-rapp-generator/es-demo-rapp/Dockerfile | 28 + sample-rapp-generator/es-demo-rapp/README.md | 137 +++ .../cloudEventConsumerFor-itdep-installation.sh | 51 ++ .../cloudEventProducerFor-itdep-installation.sh | 66 ++ .../Deployment/HELM/energy-saving-chart/Chart.yaml | 41 + .../resources/config/config.json | 35 + .../HELM/energy-saving-chart/templates/NOTES.txt | 41 + .../energy-saving-chart/templates/_helpers.tpl | 82 ++ .../energy-saving-chart/templates/configmap.yaml | 27 + .../energy-saving-chart/templates/deployment.yaml | 97 ++ .../HELM/energy-saving-chart/templates/hpa.yaml | 51 ++ .../energy-saving-chart/templates/ingress.yaml | 80 ++ .../energy-saving-chart/templates/service.yaml | 34 + .../templates/serviceaccount.yaml | 31 + .../HELM/energy-saving-chart/values.yaml | 131 +++ .../rapp-energy-saving/Definitions/asd.yaml | 30 + .../rapp-energy-saving/Definitions/asd_types.yaml | 72 ++ .../Files/Acm/definition/compositions.json | 254 ++++++ .../Files/Acm/instances/es-instance.json | 56 ++ .../Files/Acm/instances/k8s-instance.json | 35 + .../Files/Acm/instances/kserve-instance.json | 29 + .../Files/Sme/invokers/invoker-app1.json | 14 + .../Files/Sme/invokers/invoker-influxdb.json | 14 + .../Sme/invokers/invoker-kserve-predictor.json | 14 + .../Files/Sme/invokers/invoker-ncmp.json | 14 + .../Sme/providers/es-model-provider-function.json | 20 + .../Files/Sme/providers/provider-function-1.json | 20 + .../Files/Sme/serviceapis/api-set-1.json | 32 + .../Files/Sme/serviceapis/api-set-2.json | 32 + .../Sme/serviceapis/api-set-kserve-predictor.json | 36 + .../rapp-energy-saving/TOSCA-Metadata/TOSCA.meta | 5 + .../es-demo-rapp/rapp-energy-saving/asd.mf | 23 + sample-rapp-generator/es-demo-rapp/src/assist.py | 98 ++ sample-rapp-generator/es-demo-rapp/src/config.json | 35 + sample-rapp-generator/es-demo-rapp/src/data.py | 207 +++++ sample-rapp-generator/es-demo-rapp/src/input.json | 1 + sample-rapp-generator/es-demo-rapp/src/main.py | 209 +++++ .../es-demo-rapp/src/ncmp_client.py | 122 +++ .../es-demo-rapp/src/requirements.txt | 6 + .../es-demo-rapp/src/sme_client.py | 79 ++ .../es-demo-rapp/src/teiv_client.py | 84 ++ sample-rapp-generator/generate.sh | 9 +- scripts/install/patch-sample-rapps.sh | 43 +- 44 files changed, 3510 insertions(+), 12 deletions(-) create mode 100644 sample-rapp-generator/es-demo-rapp/Demo_Energy_Saving_rApp.postman_collection.json create mode 100644 sample-rapp-generator/es-demo-rapp/Dockerfile create mode 100644 sample-rapp-generator/es-demo-rapp/README.md create mode 100644 sample-rapp-generator/es-demo-rapp/cloudEventConsumerFor-itdep-installation.sh create mode 100644 sample-rapp-generator/es-demo-rapp/cloudEventProducerFor-itdep-installation.sh create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/Chart.yaml create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/resources/config/config.json create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/NOTES.txt create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/_helpers.tpl create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/configmap.yaml create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/deployment.yaml create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/hpa.yaml create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/ingress.yaml create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/service.yaml create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/serviceaccount.yaml create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/values.yaml create mode 100755 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Definitions/asd.yaml create mode 100755 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Definitions/asd_types.yaml create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/definition/compositions.json create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/instances/es-instance.json create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/instances/k8s-instance.json create mode 100755 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/instances/kserve-instance.json create mode 100755 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-app1.json create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-influxdb.json create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-kserve-predictor.json create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-ncmp.json create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/providers/es-model-provider-function.json create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/providers/provider-function-1.json create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/serviceapis/api-set-1.json create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/serviceapis/api-set-2.json create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/serviceapis/api-set-kserve-predictor.json create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/TOSCA-Metadata/TOSCA.meta create mode 100644 sample-rapp-generator/es-demo-rapp/rapp-energy-saving/asd.mf create mode 100644 sample-rapp-generator/es-demo-rapp/src/assist.py create mode 100644 sample-rapp-generator/es-demo-rapp/src/config.json create mode 100644 sample-rapp-generator/es-demo-rapp/src/data.py create mode 100644 sample-rapp-generator/es-demo-rapp/src/input.json create mode 100644 sample-rapp-generator/es-demo-rapp/src/main.py create mode 100644 sample-rapp-generator/es-demo-rapp/src/ncmp_client.py create mode 100644 sample-rapp-generator/es-demo-rapp/src/requirements.txt create mode 100644 sample-rapp-generator/es-demo-rapp/src/sme_client.py create mode 100644 sample-rapp-generator/es-demo-rapp/src/teiv_client.py diff --git a/sample-rapp-generator/es-demo-rapp/Demo_Energy_Saving_rApp.postman_collection.json b/sample-rapp-generator/es-demo-rapp/Demo_Energy_Saving_rApp.postman_collection.json new file mode 100644 index 0000000..d7202cd --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/Demo_Energy_Saving_rApp.postman_collection.json @@ -0,0 +1,997 @@ +{ + "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 diff --git a/sample-rapp-generator/es-demo-rapp/Dockerfile b/sample-rapp-generator/es-demo-rapp/Dockerfile new file mode 100644 index 0000000..85f2335 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/Dockerfile @@ -0,0 +1,28 @@ +# ============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" ] diff --git a/sample-rapp-generator/es-demo-rapp/README.md b/sample-rapp-generator/es-demo-rapp/README.md new file mode 100644 index 0000000..e1466e3 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/README.md @@ -0,0 +1,137 @@ +# 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 diff --git a/sample-rapp-generator/es-demo-rapp/cloudEventConsumerFor-itdep-installation.sh b/sample-rapp-generator/es-demo-rapp/cloudEventConsumerFor-itdep-installation.sh new file mode 100644 index 0000000..2d04057 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/cloudEventConsumerFor-itdep-installation.sh @@ -0,0 +1,51 @@ +#!/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 diff --git a/sample-rapp-generator/es-demo-rapp/cloudEventProducerFor-itdep-installation.sh b/sample-rapp-generator/es-demo-rapp/cloudEventProducerFor-itdep-installation.sh new file mode 100644 index 0000000..f4bee87 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/cloudEventProducerFor-itdep-installation.sh @@ -0,0 +1,66 @@ +#!/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." diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/Chart.yaml b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/Chart.yaml new file mode 100644 index 0000000..f7b9238 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/Chart.yaml @@ -0,0 +1,41 @@ +# ============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" diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/resources/config/config.json b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/resources/config/config.json new file mode 100644 index 0000000..2fbd20f --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/resources/config/config.json @@ -0,0 +1,35 @@ +{ + "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 diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/NOTES.txt b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/NOTES.txt new file mode 100644 index 0000000..3766040 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/NOTES.txt @@ -0,0 +1,41 @@ +{{/* +# ============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 }} diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/_helpers.tpl b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/_helpers.tpl new file mode 100644 index 0000000..b4e3f0c --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/_helpers.tpl @@ -0,0 +1,82 @@ +{{/* +# ============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 }} diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/configmap.yaml b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/configmap.yaml new file mode 100644 index 0000000..1ad33be --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/configmap.yaml @@ -0,0 +1,27 @@ +{{/* +# ============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 diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/deployment.yaml b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/deployment.yaml new file mode 100644 index 0000000..1ce26ac --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/deployment.yaml @@ -0,0 +1,97 @@ +{{/* +# ============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 diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/hpa.yaml b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/hpa.yaml new file mode 100644 index 0000000..4b3a7f2 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/hpa.yaml @@ -0,0 +1,51 @@ +{{/* +# ============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 }} diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/ingress.yaml b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/ingress.yaml new file mode 100644 index 0000000..db3a007 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/ingress.yaml @@ -0,0 +1,80 @@ +{{/* +# ============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 }} diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/service.yaml b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/service.yaml new file mode 100644 index 0000000..c4afd79 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/service.yaml @@ -0,0 +1,34 @@ +{{/* +# ============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 }} diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/serviceaccount.yaml b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/serviceaccount.yaml new file mode 100644 index 0000000..fb25c15 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/templates/serviceaccount.yaml @@ -0,0 +1,31 @@ +{{/* +# ============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 }} diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/values.yaml b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/values.yaml new file mode 100644 index 0000000..1cc7102 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Artifacts/Deployment/HELM/energy-saving-chart/values.yaml @@ -0,0 +1,131 @@ +# ============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" ] + diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Definitions/asd.yaml b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Definitions/asd.yaml new file mode 100755 index 0000000..727004b --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Definitions/asd.yaml @@ -0,0 +1,30 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 + +description: rApp energy saving + +imports: + - asd_types.yaml + +topology_template: + node_templates: + applicationServiceDescriptor: + type: tosca.nodes.asd + description: "rapp-energy-saving" + properties: + descriptor_id: 364545c9-f1b1-48bb-a774-2b999e47692d + descriptor_invariant_id: 97f18c6e-125d-470e-81a2-a36034989acd + descriptor_version: 1.0 + schema_version: 2.0 + function_description: rApp description + provider: Ericsson Software Technology (O-RAN SC NONRTRIC team) + application_name: rapp-energy-saving + application_version: 1.0 + artifacts: + energy-saving: + type: tosca.artifacts.asd.deploymentItem + file: "Artifacts/Deployment/HELM/energy-saving-rapp-0.1.0.tgz" + properties: + artifact_type: "helm_chart" + target_server: "chartmuseum" + target_server_uri: "UPDATE_THIS_CHART_MUSEUM_POST_CHARTS_URI" + item_id: 1 \ No newline at end of file diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Definitions/asd_types.yaml b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Definitions/asd_types.yaml new file mode 100755 index 0000000..7ac8c10 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Definitions/asd_types.yaml @@ -0,0 +1,72 @@ +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 diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/definition/compositions.json b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/definition/compositions.json new file mode 100644 index 0000000..9afd529 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/definition/compositions.json @@ -0,0 +1,254 @@ +{ + "tosca_definitions_version": "tosca_simple_yaml_1_3", + "data_types": { + "onap.datatypes.ToscaConceptIdentifier": { + "derived_from": "tosca.datatypes.Root", + "properties": { + "name": { + "type": "string", + "required": true + }, + "version": { + "type": "string", + "required": true + } + } + }, + "org.onap.datatypes.policy.clamp.acm.kserveAutomationCompositionElement.KserveInferenceEntity": { + "version": "1.0.0", + "derived_from": "tosca.datatypes.Root", + "properties": { + "kserveEntityId": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "required": true, + "description": "The name and version of a Configuration Entity to be handled by the Kserve Automation Composition Element" + }, + "name": { + "type": "string", + "required": true, + "description": "Inference service name to be created" + }, + "payload": { + "type": "string", + "required": true, + "description": "Inference Service payload" + } + } + } + }, + "node_types": { + "org.onap.policy.clamp.acm.Participant": { + "version": "1.0.1", + "derived_from": "tosca.nodetypes.Root", + "properties": { + "provider": { + "type": "string", + "required": false + } + } + }, + "org.onap.policy.clamp.acm.AutomationCompositionElement": { + "version": "1.0.1", + "derived_from": "tosca.nodetypes.Root", + "properties": { + "provider": { + "type": "string", + "required": false + }, + "participantType": { + "type": "onap.datatypes.ToscaConceptIdentifier", + "required": true + }, + "startPhase": { + "type": "integer", + "required": false, + "constraints": [ + { + "greater-or-equal": 0 + } + ], + "metadata": { + "common": true + }, + "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" + }, + "passiveToRunningTimeout": { + "type": "integer", + "required": false, + "constraints": [ + { + "greater_or_equal": 0 + } + ], + "default": 60, + "metadata": { + "common": true + }, + "description": "The maximum time in seconds to wait for a state chage from passive to running" + }, + "runningToPassiveTimeout": { + "type": "integer", + "required": false, + "constraints": [ + { + "greater_or_equal": 0 + } + ], + "default": 60, + "metadata": { + "common": true + }, + "description": "The maximum time in seconds to wait for a state chage from running to passive" + }, + "passiveToUninitializedTimeout": { + "type": "integer", + "required": false, + "constraints": [ + { + "greater_or_equal": 0 + } + ], + "default": 60, + "metadata": { + "common": true + }, + "description": "The maximum time in seconds to wait for a state chage from passive to uninitialized" + } + } + }, + "org.onap.policy.clamp.acm.AutomationComposition": { + "version": "1.0.1", + "derived_from": "tosca.nodetypes.Root", + "properties": { + "provider": { + "type": "string", + "required": false, + "metadata": { + "common": true + } + }, + "elements": { + "type": "list", + "required": true, + "metadata": { + "common": true + }, + "entry_schema": { + "type": "onap.datatypes.ToscaConceptIdentifier" + } + } + } + }, + "org.onap.policy.clamp.acm.KserveAutomationCompositionElement": { + "version": "1.0.1", + "derived_from": "org.onap.policy.clamp.acm.AutomationCompositionElement", + "properties": { + "kserveInferenceEntities": { + "type": "list", + "required": true, + "entry_schema": { + "type": "org.onap.datatypes.policy.clamp.acm.kserveAutomationCompositionElement.KserveInferenceEntity", + "type_version": "1.0.0" + }, + "description": "The configuration entities of Kserve inference service" + } + } + }, + "org.onap.policy.clamp.acm.K8SMicroserviceAutomationCompositionElement": { + "version": "1.0.0", + "derived_from": "org.onap.policy.clamp.acm.AutomationCompositionElement", + "properties": { + "chart": { + "type": "string", + "required": true + }, + "configs": { + "type": "list", + "required": false + }, + "requirements": { + "type": "string", + "required": false + }, + "templates": { + "type": "list", + "required": false, + "entry_schema": null + }, + "values": { + "type": "string", + "required": true + } + } + } + }, + "topology_template": { + "node_templates": { + "org.onap.policy.clamp.acm.KserveParticipant": { + "version": "2.3.4", + "type": "org.onap.policy.clamp.acm.Participant", + "type_version": "1.0.1", + "description": "Participant for Kserve requests", + "properties": { + "provider": "ONAP" + } + }, + "org.onap.k8s.acm.K8SAutomationCompositionParticipant": { + "version": "2.3.4", + "type": "org.onap.policy.clamp.acm.Participant", + "type_version": "1.0.1", + "description": "Participant for K8S", + "properties": { + "provider": "ONAP" + } + }, + "onap.policy.clamp.ac.element.KserveAutomationCompositionElement": { + "version": "1.2.3", + "type": "org.onap.policy.clamp.acm.KserveAutomationCompositionElement", + "type_version": "1.0.1", + "description": "Automation composition element for the Kserve Requests", + "properties": { + "provider": "ONAP", + "startPhase": 0, + "participantType": { + "name": "org.onap.policy.clamp.acm.KserveParticipant", + "version": "2.3.4" + }, + "uninitializedToPassiveTimeout": 300, + "statusCheckInterval": 30 + } + }, + "onap.policy.clamp.ac.element.K8S_StarterAutomationCompositionElement": { + "version": "1.2.3", + "type": "org.onap.policy.clamp.acm.K8SMicroserviceAutomationCompositionElement", + "type_version": "1.0.0", + "description": "Automation composition element for the K8S microservice for AC Element Starter", + "properties": { + "provider": "ONAP", + "startPhase": 1, + "uninitializedToPassiveTimeout": 300, + "podStatusCheckInterval": 30 + } + }, + "onap.policy.clamp.ac.element.AutomationCompositionDefinition": { + "version": "1.2.3", + "type": "org.onap.policy.clamp.acm.AutomationComposition", + "type_version": "1.0.1", + "description": "Automation composition for rapp deployment", + "properties": { + "provider": "ONAP", + "elements": [ + { + "name": "onap.policy.clamp.ac.element.K8S_StarterAutomationCompositionElement", + "version": "1.2.3" + }, + { + "name": "onap.policy.clamp.ac.element.KserveAutomationCompositionElement", + "version": "1.2.3" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/instances/es-instance.json b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/instances/es-instance.json new file mode 100644 index 0000000..4b5e18f --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/instances/es-instance.json @@ -0,0 +1,56 @@ +{ + "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 diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/instances/k8s-instance.json b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/instances/k8s-instance.json new file mode 100644 index 0000000..8c073d3 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/instances/k8s-instance.json @@ -0,0 +1,35 @@ +{ + "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 diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/instances/kserve-instance.json b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/instances/kserve-instance.json new file mode 100755 index 0000000..3904412 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Acm/instances/kserve-instance.json @@ -0,0 +1,29 @@ +{ + "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 diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-app1.json b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-app1.json new file mode 100755 index 0000000..0400730 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-app1.json @@ -0,0 +1,14 @@ +[ + { + "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 diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-influxdb.json b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-influxdb.json new file mode 100644 index 0000000..1514bc9 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-influxdb.json @@ -0,0 +1,14 @@ +[ + { + "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 diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-kserve-predictor.json b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-kserve-predictor.json new file mode 100644 index 0000000..60b994d --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-kserve-predictor.json @@ -0,0 +1,14 @@ +[ + { + "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 diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-ncmp.json b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-ncmp.json new file mode 100644 index 0000000..ba325b5 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/invokers/invoker-ncmp.json @@ -0,0 +1,14 @@ +[ + { + "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 diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/providers/es-model-provider-function.json b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/providers/es-model-provider-function.json new file mode 100644 index 0000000..65cd00b --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/providers/es-model-provider-function.json @@ -0,0 +1,20 @@ +{ + "apiProvDomInfo": "ES Model Provider domain", + "apiProvFuncs": [ + { + "apiProvFuncInfo": "ES Prediction as APF", + "apiProvFuncRole": "APF", + "regInfo": { + "apiProvPubKey": "NOT USED here" + } + }, + { + "apiProvFuncInfo": "ES Prediction as AEF", + "apiProvFuncRole": "AEF", + "regInfo": { + "apiProvPubKey": "NOT USED here" + } + } + ], + "regSec": "plain" +} \ No newline at end of file diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/providers/provider-function-1.json b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/providers/provider-function-1.json new file mode 100644 index 0000000..b612ae5 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/providers/provider-function-1.json @@ -0,0 +1,20 @@ +{ + "apiProvDomInfo": "Provider domain", + "apiProvFuncs": [ + { + "apiProvFuncInfo": "Energy Saving as APF", + "apiProvFuncRole": "APF", + "regInfo": { + "apiProvPubKey": "NOT USED here" + } + }, + { + "apiProvFuncInfo": "Energy Saving as AEF", + "apiProvFuncRole": "AEF", + "regInfo": { + "apiProvPubKey": "NOT USED here" + } + } + ], + "regSec": "plain" +} \ No newline at end of file diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/serviceapis/api-set-1.json b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/serviceapis/api-set-1.json new file mode 100644 index 0000000..7287685 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/serviceapis/api-set-1.json @@ -0,0 +1,32 @@ +{ + "apiName": "Energy Saving API Set 1", + "description": "Simple Energy Saving API", + "aefProfiles": [ + { + "aefId": "Energy Saving as AEF", + "description": "Energy Saving API", + "versions": [ + { + "apiVersion": "", + "resources": [ + { + "resourceName": "energysaving", + "commType": "REQUEST_RESPONSE", + "uri": "/energysaving/v1", + "operations": [ + "GET" + ] + } + ] + } + ], + "protocol": "HTTP_1_1", + "interfaceDescriptions": [ + { + "ipv4Addr": "energysaving.nonrtric.svc.cluster.local", + "port": 8080 + } + ] + } + ] +} \ No newline at end of file diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/serviceapis/api-set-2.json b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/serviceapis/api-set-2.json new file mode 100644 index 0000000..19b1a25 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/serviceapis/api-set-2.json @@ -0,0 +1,32 @@ +{ + "apiName": "Energy Saving API Set 2", + "description": "Simple Energy Saving API for Subpath", + "aefProfiles": [ + { + "aefId": "Energy Saving as AEF", + "description": "Energy Saving API for Subpath", + "versions": [ + { + "apiVersion": "", + "resources": [ + { + "resourceName": "energysaving", + "commType": "REQUEST_RESPONSE", + "uri": "/energysaving/v1/subpath1", + "operations": [ + "GET" + ] + } + ] + } + ], + "protocol": "HTTP_1_1", + "interfaceDescriptions": [ + { + "ipv4Addr": "energysaving.nonrtric.svc.cluster.local", + "port": 8080 + } + ] + } + ] +} \ No newline at end of file diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/serviceapis/api-set-kserve-predictor.json b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/serviceapis/api-set-kserve-predictor.json new file mode 100644 index 0000000..84d8dbf --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/Files/Sme/serviceapis/api-set-kserve-predictor.json @@ -0,0 +1,36 @@ +{ + "apiName": "es-predictor-http", + "description": "ES Prediction model endpoints", + "aefProfiles": [ + { + "aefId": "ES Predictor as AEF", + "description": "ES predictor API", + "versions": [ + { + "apiVersion": "", + "resources": [ + { + "resourceName": "root", + "commType": "REQUEST_RESPONSE", + "uri": "/", + "operations": [ + "POST", + "GET", + "PUT", + "DELETE", + "PATCH" + ] + } + ] + } + ], + "protocol": "HTTP_1_1", + "interfaceDescriptions": [ + { + "ipv4Addr": "es-aiml-model-predictor.kserve-test.svc.cluster.local", + "port": 80 + } + ] + } + ] +} \ No newline at end of file diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/TOSCA-Metadata/TOSCA.meta b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/TOSCA-Metadata/TOSCA.meta new file mode 100644 index 0000000..3da5f0d --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/TOSCA-Metadata/TOSCA.meta @@ -0,0 +1,5 @@ +TOSCA-Meta-File-Version: 1.0 +CSAR-Version: 1.0 +Created-By: Ericsson Software Technology (O-RAN SC NONRTRIC team) +Entry-Definitions: Definitions/asd.yaml +ETSI-Entry-Manifest: asd.mf \ No newline at end of file diff --git a/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/asd.mf b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/asd.mf new file mode 100644 index 0000000..0dcefc3 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/rapp-energy-saving/asd.mf @@ -0,0 +1,23 @@ +metadata: + application_name: rapp-energy-saving + application_provider: Ericsson Software Technology (O-RAN SC NONRTRIC team) + release_date_time: 2025-06-03T12:34:00+00:00 + entry_definition_type: asd + +Source: asd.mf +Source: Definitions/asd.yaml +Source: Definitions/asd_types.yaml +Source: TOSCA-Metadata/TOSCA.meta +Source: Files/Acm/definition/compositions.json +Source: Files/Acm/instances/es-instance.json +Source: Files/Acm/instances/k8s-instance.json +Source: Files/Acm/instances/kserve-instance.json +Source: Files/SME/invokers/invoker-app1.json +Source: Files/SME/invokers/invoker-ncmp.json +Source: Files/SME/invokers/invoker-kserve-predictor.json +Source: Files/SME/invokers/invoker-influxdb.json +Source: Files/Sme/provider/provider-function-1.json +Source: Files/Sme/provider/es-model-provider-function.json +Source: Files/Sme/serviceapis/api-set-1.json +Source: Files/Sme/serviceapis/api-set-2.json +Source: Files/Sme/serviceapis/api-set-kserve-predictor.json diff --git a/sample-rapp-generator/es-demo-rapp/src/assist.py b/sample-rapp-generator/es-demo-rapp/src/assist.py new file mode 100644 index 0000000..27d6fe8 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/src/assist.py @@ -0,0 +1,98 @@ +# ============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 diff --git a/sample-rapp-generator/es-demo-rapp/src/config.json b/sample-rapp-generator/es-demo-rapp/src/config.json new file mode 100644 index 0000000..c39467d --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/src/config.json @@ -0,0 +1,35 @@ +{ + "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 diff --git a/sample-rapp-generator/es-demo-rapp/src/data.py b/sample-rapp-generator/es-demo-rapp/src/data.py new file mode 100644 index 0000000..74662ec --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/src/data.py @@ -0,0 +1,207 @@ +# ============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") diff --git a/sample-rapp-generator/es-demo-rapp/src/input.json b/sample-rapp-generator/es-demo-rapp/src/input.json new file mode 100644 index 0000000..41367ce --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/src/input.json @@ -0,0 +1 @@ +{"signature_name": "serving_default", "instances": [[[42.59, 50.3, 73.18]]]} \ No newline at end of file diff --git a/sample-rapp-generator/es-demo-rapp/src/main.py b/sample-rapp-generator/es-demo-rapp/src/main.py new file mode 100644 index 0000000..97dfd1c --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/src/main.py @@ -0,0 +1,209 @@ +# ============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() diff --git a/sample-rapp-generator/es-demo-rapp/src/ncmp_client.py b/sample-rapp-generator/es-demo-rapp/src/ncmp_client.py new file mode 100644 index 0000000..0093f88 --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/src/ncmp_client.py @@ -0,0 +1,122 @@ +# ============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}") + diff --git a/sample-rapp-generator/es-demo-rapp/src/requirements.txt b/sample-rapp-generator/es-demo-rapp/src/requirements.txt new file mode 100644 index 0000000..e99dedc --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/src/requirements.txt @@ -0,0 +1,6 @@ +influxdb_client +pandas +requests +influxdb +pandas +schedule diff --git a/sample-rapp-generator/es-demo-rapp/src/sme_client.py b/sample-rapp-generator/es-demo-rapp/src/sme_client.py new file mode 100644 index 0000000..9ec3dcb --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/src/sme_client.py @@ -0,0 +1,79 @@ +# ============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 diff --git a/sample-rapp-generator/es-demo-rapp/src/teiv_client.py b/sample-rapp-generator/es-demo-rapp/src/teiv_client.py new file mode 100644 index 0000000..3b4456b --- /dev/null +++ b/sample-rapp-generator/es-demo-rapp/src/teiv_client.py @@ -0,0 +1,84 @@ +# ============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 diff --git a/sample-rapp-generator/generate.sh b/sample-rapp-generator/generate.sh index e3d0e6e..7dcbc94 100755 --- a/sample-rapp-generator/generate.sh +++ b/sample-rapp-generator/generate.sh @@ -2,7 +2,7 @@ # ============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. @@ -33,8 +33,9 @@ if ! command -v helm &> /dev/null; then exit 1 fi +ORIGINAL_DIR=$(pwd) DIRECTORY=${1%/} -PACKAGENAME="$DIRECTORY.csar" +PACKAGENAME="$(basename "$DIRECTORY").csar" HELM_DIR="$DIRECTORY/Artifacts/Deployment/HELM" EXCLUDE_DIRS=() @@ -56,9 +57,9 @@ checkHelmPackage() { 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 diff --git a/scripts/install/patch-sample-rapps.sh b/scripts/install/patch-sample-rapps.sh index 5ad1c10..a4c4ed8 100755 --- a/scripts/install/patch-sample-rapps.sh +++ b/scripts/install/patch-sample-rapps.sh @@ -1,6 +1,6 @@ #!/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. @@ -17,22 +17,43 @@ # 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." @@ -41,7 +62,7 @@ done 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." @@ -51,6 +72,12 @@ for file in $(find $WORKSPACE -type f -name "*.json"); do 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" -- 2.16.6