--- /dev/null
+FROM python:3.7.4\r
+\r
+RUN python --version\r
+\r
+ADD pip-requirements.txt pip-requirements.txt\r
+ADD a1-mediator-vth.py a1-mediator-vth.py\r
+ADD config.json config.json\r
+\r
+RUN mkdir -p /otf/logs\r
+\r
+RUN python -m pip install --proxy -r pip-requirements.txt\r
+\r
+ENTRYPOINT ["python", "a1-mediator-vth.py"]\r
--- /dev/null
+#!/usr/bin/env groovy\r
+\r
+/* Copyright (c) 2019 AT&T Intellectual Property. #\r
+# #\r
+# Licensed under the Apache License, Version 2.0 (the "License"); #\r
+# you may not use this file except in compliance with the License. #\r
+# You may obtain a copy of the License at #\r
+# #\r
+# http://www.apache.org/licenses/LICENSE-2.0 #\r
+# #\r
+# Unless required by applicable law or agreed to in writing, software #\r
+# distributed under the License is distributed on an "AS IS" BASIS, #\r
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #\r
+# See the License for the specific language governing permissions and #\r
+# limitations under the License. #\r
+##############################################################################*/\r
+\r
+properties([[$class: 'ParametersDefinitionProperty', parameterDefinitions: [\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'PHASE', defaultValue: "BUILD"],\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'ENV', defaultValue: "dev"],\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'MECHID', defaultValue: "id_otf_dev"],\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'KUBE_CONFIG', defaultValue: "kubeConfig-dev"],\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'TILLER_NAMESPACE', defaultValue: "registry.hub.docker.io"]\r
+]]])\r
+\r
+\r
+echo "Build branch: ${env.BRANCH_NAME}"\r
+\r
+node("docker"){\r
+ stage 'Checkout'\r
+ checkout scm\r
+ PHASES=PHASE.tokenize( '_' );\r
+ echo "PHASES : " + PHASES\r
+\r
+\r
+ ARTIFACT_ID="a1-mediator-vth";\r
+ VERSION="0.0.1-SNAPSHOT";\r
+ NAMESPACE="org-oran-otf"\r
+ DOCKER_REGISTRY="registry.hub.docker.io"\r
+\r
+ if( ENV.equalsIgnoreCase("dev") ){\r
+ IMAGE_NAME=DOCKER_REGISTRY + "/" + NAMESPACE + ".dev" + "/" + ARTIFACT_ID + ":" + VERSION\r
+\r
+ }\r
+ if( ENV.equalsIgnoreCase("prod") || ENV.equalsIgnoreCase("prod-dr")){\r
+ IMAGE_NAME=DOCKER_REGISTRY + "/" + NAMESPACE + ".prod" + "/" + ARTIFACT_ID + ":" + VERSION\r
+\r
+ }\r
+\r
+ if( ENV.equalsIgnoreCase("st") ){\r
+ IMAGE_NAME=DOCKER_REGISTRY + "/" + NAMESPACE + ".st" + "/" + ARTIFACT_ID + ":" + VERSION\r
+\r
+ }\r
+\r
+ echo "Artifact: " + IMAGE_NAME\r
+\r
+ withEnv(["PATH=${env.PATH}:${env.WORKSPACE}/linux-amd64", "HELM_HOME=${env.WORKSPACE}"]) {\r
+\r
+ echo "PATH=${env.PATH}"\r
+ echo "HELM_HOME=${env.HELM_HOME}"\r
+\r
+ if (PHASES.contains("BUILD")){\r
+\r
+ stage 'Publish Artifact'\r
+\r
+ withCredentials([usernamePassword(credentialsId: MECHID, usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {\r
+ dir("smo-vth") {\r
+ echo "Artifact: " + IMAGE_NAME\r
+\r
+ sh """\r
+ docker login $DOCKER_REGISTRY --username $USERNAME --password $PASSWORD\r
+ docker build -t $IMAGE_NAME .\r
+ docker push $IMAGE_NAME\r
+ """\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ if (PHASES.contains("DEPLOY") || PHASES.contains("UNDEPLOY")) {\r
+\r
+ stage 'Init Helm'\r
+\r
+ //check if helm exists if not install\r
+ if(fileExists('linux-amd64/helm')){\r
+ sh """\r
+ echo "helm is already installed"\r
+ """\r
+ }\r
+ else{\r
+ //download helm\r
+ sh """\r
+ echo "installing helm"\r
+ wget https://storage.googleapis.com/kubernetes-helm/helm-v2.14.3-linux-amd64.tar.gz\r
+ tar -xf helm-v2.14.3-linux-amd64.tar.gz\r
+ rm helm-v2.14.3-linux-amd64.tar.gz\r
+ """\r
+ }\r
+\r
+ withCredentials([file(credentialsId: KUBE_CONFIG, variable: 'KUBECONFIG')]) {\r
+\r
+ dir('a1-mediator-vth/helm'){\r
+ //check if charts are valid, and then perform dry run, if successful then upgrade/install charts\r
+\r
+ if (PHASES.contains("UNDEPLOY") ) {\r
+ stage 'Undeploy'\r
+\r
+ sh """\r
+ helm delete --tiller-namespace=$TILLER_NAMESPACE --purge $ARTIFACT_ID\r
+ """\r
+ }\r
+\r
+ //NOTE Double quotes are used below to access groovy variables like artifact_id and tiller_namespace\r
+ if (PHASES.contains("DEPLOY") ){\r
+ stage 'Deploy'\r
+ withCredentials([usernamePassword(credentialsId: MECHID, usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {\r
+\r
+ sh """\r
+ helm version\r
+ echo "Validate Yaml"\r
+ helm lint $ARTIFACT_ID\r
+\r
+ echo "View Helm Templates"\r
+ helm template $ARTIFACT_ID --set appName=$ARTIFACT_ID \\r
+ --set appName=$ARTIFACT_ID \\r
+ --set version=$VERSION \\r
+ --set env=$ENV \\r
+ --set image=$IMAGE_NAME \\r
+ --set namespace=$TILLER_NAMESPACE\r
+\r
+ echo "Perform Dry Run Of Install"\r
+ helm upgrade --tiller-namespace=$TILLER_NAMESPACE --install --dry-run $ARTIFACT_ID $ARTIFACT_ID \\r
+ --set appName=$ARTIFACT_ID \\r
+ --set version=$VERSION \\r
+ --set env=$ENV \\r
+ --set image=$IMAGE_NAME \\r
+ --set namespace=$TILLER_NAMESPACE\r
+\r
+ echo "Helm Install/Upgrade"\r
+ helm upgrade --tiller-namespace=$TILLER_NAMESPACE --install $ARTIFACT_ID $ARTIFACT_ID \\r
+ --set appName=$ARTIFACT_ID \\r
+ --set version=$VERSION \\r
+ --set env=$ENV \\r
+ --set image=$IMAGE_NAME \\r
+ --set namespace=$TILLER_NAMESPACE\r
+\r
+ """\r
+ }\r
+ }\r
+\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+}\r
--- /dev/null
+# Copyright (c) 2019 AT&T Intellectual Property. #\r
+# #\r
+# Licensed under the Apache License, Version 2.0 (the "License"); #\r
+# you may not use this file except in compliance with the License. #\r
+# You may obtain a copy of the License at #\r
+# #\r
+# http://www.apache.org/licenses/LICENSE-2.0 #\r
+# #\r
+# Unless required by applicable law or agreed to in writing, software #\r
+# distributed under the License is distributed on an "AS IS" BASIS, #\r
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #\r
+# See the License for the specific language governing permissions and #\r
+# limitations under the License. #\r
+################################################################################\r
+# File name: a1-mediator-vth.py #\r
+# Description: vth for a1-mediator-vth service #\r
+# Date created: 08/22/2019 #\r
+# Last modified: 04/02/2020 #\r
+# Python Version: 3.7.4 #\r
+# Author: Jackie Chen (jv246a) #\r
+# Email: jv246a@att.com #\r
+################################################################################\r
+import datetime\r
+import json\r
+import logging\r
+from logging import FileHandler\r
+import os\r
+\r
+import requests\r
+from flask import Flask, request, jsonify\r
+\r
+# redirect http to https\r
+app = Flask(__name__)\r
+\r
+# Prevents print statement every time an endpoint is triggered.\r
+logging.getLogger("werkzeug").setLevel(logging.WARNING)\r
+\r
+\r
+def sendCallback(url, data):\r
+ try:\r
+ if type(data) is not dict:\r
+ data = {"msg": data}\r
+ app.logger.info("sending callback")\r
+ requests.post(url, json=data)\r
+ except Exception as e:\r
+ app.logger.info(e)\r
+ return\r
+\r
+def unix_time_millis(dt):\r
+ epoch = datetime.datetime.utcfromtimestamp(0)\r
+ return (dt - epoch).total_seconds() * 1000.0\r
+\r
+\r
+@app.route("/otf/vth/oran/a1/v1/health", methods=['GET'])\r
+def getHealth():\r
+ return "UP"\r
+\r
+\r
+@app.route("/otf/vth/oran/a1/v1", methods=['POST'])\r
+def executeRicRequest():\r
+ response_data = {\r
+ 'vthResponse': {\r
+ 'testDuration': '',\r
+ 'dateTimeUTC': str(datetime.datetime.now()),\r
+ 'abstractMessage': '',\r
+ 'resultData': {}\r
+ }\r
+ }\r
+\r
+ startTime = unix_time_millis(datetime.datetime.now())\r
+ ret_url = request.args.get('retURL')\r
+ try:\r
+ if not request.is_json:\r
+ raise ValueError("request must be json")\r
+\r
+ requestData = request.get_json()\r
+\r
+ app.logger.info("A1 requestData:" + str(requestData))\r
+\r
+ action = requestData['action'].lower()\r
+ _check_incoming_request(requestData)\r
+\r
+ os.environ['NO_PROXY'] = '127.0.0.1' # TODO testing purpose w/ mock server. Needs to remove on final version\r
+ with open('config.json') as configFile:\r
+ config = json.load(configFile)\r
+\r
+ baseAddress = config['base_address']\r
+ if action == 'health_check' or action == 'list_policy':\r
+ res = requests.get(baseAddress + config['actions_path'][action])\r
+ response_data['vthResponse']['resultData']['statusCode'] = res.status_code\r
+ if action == 'health_check':\r
+ response_data['vthResponse']['resultData']['resultOutput'] = res.text\r
+ else:\r
+ response_data['vthResponse']['resultData']['resultOutput'] = res.json()\r
+ elif action == 'list_policy_instance':\r
+ res = requests.get(baseAddress + config['actions_path'][action]\r
+ .format(policy_type_id=requestData['policy_type_id']))\r
+ response_data['vthResponse']['resultData']['statusCode'] = res.status_code\r
+ response_data['vthResponse']['resultData']['resultOutput'] = res.json()\r
+ elif action == 'get_policy_instance_status':\r
+ res = requests.get(baseAddress + config['actions_path'][action]\r
+ .format(policy_type_id=requestData['policy_type_id'],\r
+ policy_instance_id=requestData['policy_instance_id']))\r
+ response_data['vthResponse']['resultData']['statusCode'] = res.status_code\r
+ response_data['vthResponse']['resultData']['resultOutput'] = res.json()\r
+ elif action == 'edit_policy':\r
+ res = _send_edit_request(requestData, config)\r
+ response_data['vthResponse']['resultData']['statusCode'] = res.status_code\r
+ if requestData['request_type'].lower() == 'get' and res.status_code == 200:\r
+ response_data['vthResponse']['resultData']['resultOutput'] = res.json()\r
+ else:\r
+ response_data['vthResponse']['resultData']['resultOutput'] = res.text\r
+ elif action == 'edit_policy_instance':\r
+ res = _send_edit_request(requestData, config)\r
+ response_data['vthResponse']['resultData']['statusCode'] = res.status_code\r
+ if requestData['request_type'].lower() == 'get' and res.status_code == 200:\r
+ response_data['vthResponse']['resultData']['resultOutput'] = res.json()\r
+ else:\r
+ response_data['vthResponse']['resultData']['resultOutput'] = res.text\r
+\r
+ except Exception as ex:\r
+ endTime = unix_time_millis(datetime.datetime.now())\r
+ totalTime = endTime - startTime\r
+ response_data['vthResponse']['testDuration'] = totalTime\r
+ response_data['vthResponse']['abstractMessage'] = str(ex)\r
+ return jsonify(response_data)\r
+\r
+ endTime = unix_time_millis(datetime.datetime.now())\r
+ totalTime = endTime - startTime\r
+\r
+ response_data['vthResponse']['testDuration'] = totalTime\r
+\r
+ if ret_url is not None:\r
+ sendCallback(ret_url, response_data)\r
+ return '', 200\r
+\r
+ return jsonify(response_data), 200\r
+\r
+\r
+def _send_edit_request(request_data, config):\r
+ baseAddress = config['base_address']\r
+ path = ''\r
+ action = request_data['action']\r
+ policy_type_id = request_data['policy_type_id']\r
+ request_type = request_data['request_type']\r
+ if action == "edit_policy":\r
+ path = baseAddress + config['actions_path'][action].format(policy_type_id=policy_type_id)\r
+ if action == 'edit_policy_instance':\r
+ instance_id = request_data['policy_instance_id']\r
+ path = baseAddress + config['actions_path'][action].format(policy_type_id=policy_type_id,\r
+ policy_instance_id=instance_id)\r
+ if request_type == 'get':\r
+ return requests.get(path)\r
+ if request_type == 'put':\r
+ payload = request_data['payload']\r
+ return requests.put(path, payload)\r
+ if request_type == 'delete':\r
+ return requests.delete(path)\r
+\r
+\r
+def _check_incoming_request(requestData): # check if the request is valid\r
+ if 'action' not in requestData:\r
+ raise KeyError('no action was specify')\r
+\r
+ action = requestData['action'].lower()\r
+ edit_actions = ['edit_policy', 'edit_policy_instance']\r
+ requires_policy_id = ['edit_policy', 'list_policy_instance'\r
+ , 'edit_policy_instance', 'get_policy_instance_status']\r
+ requires_policy_instance_id = ['edit_policy_instance', 'get_policy_instance_status']\r
+ possible_actions = ['health_check', 'list_policy', 'edit_policy', 'list_policy_instance'\r
+ , 'edit_policy_instance', 'get_policy_instance_status']\r
+ possible_request_type = ['get', 'put', 'delete']\r
+\r
+ if action not in possible_actions:\r
+ raise KeyError("invalid action")\r
+ if action in edit_actions: # request type is required\r
+ if 'request_type' not in requestData:\r
+ raise KeyError('this action: ' + action + ' requires a request type')\r
+ if requestData['request_type'] not in possible_request_type:\r
+ raise KeyError('this request_type: ' + requestData['request_type'] + ' is not valid')\r
+ if requestData['request_type'] == 'put' and 'payload' not in requestData:\r
+ raise KeyError('put request requires a payload')\r
+ if action in requires_policy_id:\r
+ if 'policy_type_id' not in requestData:\r
+ raise KeyError('this action: ' + action + ' requires a policy_type_id')\r
+ if action in requires_policy_instance_id:\r
+ if 'policy_instance_id' not in requestData:\r
+ raise KeyError('this action: ' + action + ' requires a policy_instance_id')\r
+\r
+\r
+if __name__ == '__main__':\r
+ logHandler = FileHandler('a1-mediator-vth.log', mode='a')\r
+ logHandler.setLevel(logging.INFO)\r
+ app.logger.setLevel(logging.INFO)\r
+ app.logger.addHandler(logHandler)\r
+ # context = ('opt/cert/otf.pem', 'opt/cert/privateKey.pem')\r
+ # app.run(debug = False, host = '0.0.0.0', port = 5000, ssl_context = context)\r
+ app.run(debug=False, host='0.0.0.0', port=5000)\r
--- /dev/null
+const express = require('express')\r
+const app = express()\r
+const port = 3000\r
+\r
+app.use(express.json())\r
+\r
+app.get('/', (req, res) => res.send('Hello World!'))\r
+\r
+app.get('/a1-p/healthcheck',function(req,res){\r
+ console.log("health checking")\r
+ res.sendStatus(200)\r
+})\r
+\r
+app.get('/a1-p/policytypes',function(req,res){\r
+ res.status=(200)\r
+ res.send([20000, 20020])\r
+})\r
+\r
+app.get('/a1-p/policytypes/:policy_type_id',function(req,res){\r
+ res.status=(200)\r
+ policy_type_id = req.params['policy_type_id']\r
+ res.send({"name": "example policy instance","description":"fake description","policy_type_id": policy_type_id,"create_schema":"{name:sample object}"})\r
+})\r
+app.delete('/a1-p/policytypes/:policy_type_id',function(req,res){\r
+ res.sendStatus(204)\r
+})\r
+\r
+app.put('/a1-p/policytypes/:policy_type_id',function(req,res){\r
+ console.log(req.body)\r
+ res.sendStatus(201)\r
+})\r
+\r
+app.get('/a1-p/policytypes/:policy_type_id/policies',function(req,res){\r
+ console.log('listing policies')\r
+ res.status=(200)\r
+ res.send(["3d2157af-6a8f-4a7c-810f-38c2f824bf12", "06911bfc-c127-444a-8eb1-1bffad27cc3d"])\r
+})\r
+\r
+app.get('/a1-p/policytypes/:policy_type_id/policies/:policy_instance_id',function(req,res){\r
+ res.status=(200)\r
+ policy_type_id = req.params['policy_type_id']\r
+ policy_instance_id = req.params['policy_instance_id']\r
+ res.send({"name": "example policy instance","description":"fake description","policy_type_id": policy_type_id,"create_schema":"{name:sample object}"})\r
+})\r
+app.delete('/a1-p/policytypes/:policy_type_id/policies/:policy_instance_id',function(req,res){\r
+ res.sendStatus(202)\r
+})\r
+\r
+app.put('/a1-p/policytypes/:policy_type_id/policies/:policy_instance_id',function(req,res){\r
+ console.log(req.body)\r
+ res.sendStatus(202)\r
+})\r
+\r
+app.get('/a1-p/policytypes/:policy_type_id/policies/:policy_instance_id/status',function(req,res){\r
+ res.status(200)\r
+ res.send({"properties":{"instance_status": "fake status","enum": "IN EFFECT"}})\r
+})\r
+\r
+\r
+\r
+\r
+\r
+\r
+app.get('/appmgr/ric/v1/xapps',function(req,res){\r
+ res.status(200)\r
+ res.send([{"name":"admin-xapp","status":"deployed","version":"1.0","instances":null},{"name":"mcxapp","status":"deployed","version":"1.0","instances":[{"name":"mcxapp-649d7494-h5tjb","status":"running","ip":"service-ricxapp-mcxapp-rmr.ricxapp","port":4560,"txMessages":null,"rxMessages":["RIC_SUB_RESP","RIC_SUB_FAILURE","RIC_SUB_DEL_RESP","RIC_SUB_DEL_FAILURE","RIC_INDICATION"]}]},{"name":"ueec","status":"deployed","version":"1.0","instances":[{"name":"ueec-6675694b75-jtnz6","status":"running","ip":"service-ricxapp-ueec-rmr.ricxapp","port":4560,"txMessages":["RIC_SUB_REQ","RIC_SUB_DEL_REQ"],"rxMessages":["RIC_SUB_RESP","RIC_SUB_FAILURE","RIC_SUB_DEL_RESP","RIC_SUB_DEL_FAILURE","RIC_INDICATION"]}]}])\r
+})\r
+\r
+app.post('/appmgr/ric/v1/xapps', function(req,res){\r
+ res.statusMessage = 'Created'\r
+ res.status(201)\r
+ res.send({"result_output":{"name":"anr","status":"deployed","version":"1.0","instances":[{"name":"anr-7d4c47b4bb-jlslm","status":"running","ip":"service-ricxapp-anr-rmr.ricxapp","port":4560,"txMessages":null,"rxMessages":["RIC_SGNB_ADDITION_REQ","RIC_RRC_TRANSFER"]}]}})\r
+})\r
+\r
+app.delete('/appmgr/ric/v1/xapps/:name',function(req,res){\r
+ res.sendStatus(204)\r
+})\r
+\r
+app.listen(port, () => console.log(`Example app listening on port ${port}!`))\r
+\r
--- /dev/null
+{
+ "name": "a1_mock_server",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "accepts": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+ "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+ "requires": {
+ "mime-types": "~2.1.24",
+ "negotiator": "0.6.2"
+ }
+ },
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+ },
+ "body-parser": {
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+ "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+ "requires": {
+ "bytes": "3.1.0",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
+ "on-finished": "~2.3.0",
+ "qs": "6.7.0",
+ "raw-body": "2.4.0",
+ "type-is": "~1.6.17"
+ }
+ },
+ "bytes": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+ "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
+ },
+ "content-disposition": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+ "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+ "requires": {
+ "safe-buffer": "5.1.2"
+ }
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+ },
+ "cookie": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+ "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
+ },
+ "cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+ },
+ "express": {
+ "version": "4.17.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
+ "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+ "requires": {
+ "accepts": "~1.3.7",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.19.0",
+ "content-disposition": "0.5.3",
+ "content-type": "~1.0.4",
+ "cookie": "0.4.0",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.1.2",
+ "fresh": "0.5.2",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.5",
+ "qs": "6.7.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.1.2",
+ "send": "0.17.1",
+ "serve-static": "1.14.1",
+ "setprototypeof": "1.1.1",
+ "statuses": "~1.5.0",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
+ "unpipe": "~1.0.0"
+ }
+ },
+ "forwarded": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+ },
+ "http-errors": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+ "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.1",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+ },
+ "mime-db": {
+ "version": "1.43.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
+ "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
+ },
+ "mime-types": {
+ "version": "2.1.26",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
+ "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
+ "requires": {
+ "mime-db": "1.43.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ },
+ "negotiator": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+ },
+ "proxy-addr": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
+ "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
+ "requires": {
+ "forwarded": "~0.1.2",
+ "ipaddr.js": "1.9.1"
+ }
+ },
+ "qs": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+ },
+ "raw-body": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+ "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+ "requires": {
+ "bytes": "3.1.0",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "send": {
+ "version": "0.17.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+ "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.7.2",
+ "mime": "1.6.0",
+ "ms": "2.1.1",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.1",
+ "statuses": "~1.5.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+ }
+ }
+ },
+ "serve-static": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+ "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.17.1"
+ }
+ },
+ "setprototypeof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+ },
+ "toidentifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+ },
+ "type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ }
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+ }
+ }
+}
--- /dev/null
+{
+ "name": "a1_mock_server",
+ "version": "1.0.0",
+ "description": "mock for a1 ",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "jackie chen (jv246a)",
+ "license": "ISC",
+ "dependencies": {
+ "express": "^4.17.1"
+ }
+}
--- /dev/null
+{\r
+ "base_address": "127.0.0.1:3000",\r
+ "actions_path": {\r
+ "health_check": "/a1-p/healthcheck",\r
+ "list_policy": "/a1-p/policytypes",\r
+ "edit_policy": "/a1-p/policytypes/{policy_type_id}",\r
+ "list_policy_instance": "/a1-p/policytypes/{policy_type_id}/policies",\r
+ "edit_policy_instance": "/a1-p/policytypes/{policy_type_id}/policies/{policy_instance_id}",\r
+ "get_policy_instance_status": "/a1-p/policytypes/{policy_type_id}/policies/{policy_instance_id}/status"\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+# Patterns to ignore when building packages.\r
+# This supports shell glob matching, relative path matching, and\r
+# negation (prefixed with !). Only one pattern per line.\r
+.DS_Store\r
+# Common VCS dirs\r
+.git/\r
+.gitignore\r
+.bzr/\r
+.bzrignore\r
+.hg/\r
+.hgignore\r
+.svn/\r
+# Common backup files\r
+*.swp\r
+*.bak\r
+*.tmp\r
+*~\r
+# Various IDEs\r
+.project\r
+.idea/\r
+*.tmproj\r
--- /dev/null
+apiVersion: v1\r
+appVersion: "1.0"\r
+description: A Helm chart for the a1 mediator Virtual Test Head\r
+name: a1-mediator-vth\r
+version: 0.0.1\r
--- /dev/null
+apiVersion: extensions/v1beta1\r
+kind: Deployment\r
+metadata:\r
+ name: {{ .Values.appName}}\r
+ namespace: {{.Values.namespace}}\r
+ labels:\r
+ app: {{ .Values.appName}}\r
+ version: {{.Values.version}}\r
+spec:\r
+ revisionHistoryLimit: 1\r
+ minReadySeconds: 10\r
+ strategy:\r
+ # indicate which strategy we want for rolling update\r
+ type: RollingUpdate\r
+ rollingUpdate:\r
+ maxSurge: 0\r
+ maxUnavailable: 1\r
+ replicas: {{ .Values.replicas}}\r
+ selector:\r
+ matchLabels:\r
+ app: {{ .Values.appName}}\r
+ version: {{.Values.version}}\r
+ template:\r
+ metadata:\r
+ labels:\r
+ app: {{ .Values.appName}}\r
+ version: {{.Values.version}}\r
+ spec:\r
+ serviceAccount: default\r
+ volumes:\r
+ - name: {{ .Values.appName}}-cert-volume\r
+ secret:\r
+ secretName: {{.Values.sharedCert}}\r
+ optional: true\r
+ items:\r
+ - key: PEM_CERT\r
+ path: otf.pem\r
+ - key: PEM_KEY\r
+ path: privateKey.pem\r
+# {{ if or (eq .Values.env "st") (eq .Values.env "prod-dr")}} TODO UNCOMMENT WHEN PUSHING TO ORAN\r
+# {{else}}\r
+# - name: logging-pvc\r
+# persistentVolumeClaim:\r
+# {{if eq .Values.env "prod"}}\r
+# claimName: {{ .Values.pvc.prod | quote }}\r
+# {{ else }}\r
+# claimName: {{ .Values.pvc.dev | quote }}\r
+# {{ end }}\r
+# {{end}}\r
+ containers:\r
+ - name: {{ .Values.appName}}\r
+ image: {{ .Values.image}}\r
+ imagePullPolicy: Always\r
+ ports:\r
+ - name: http\r
+ containerPort: 5000\r
+ nodePort: {{.Values.nodePort}}\r
+ protocol: TCP\r
+# {{ if eq .Values.env "st"}} TODO UNCOMMENT FOR ORAN?\r
+# resources:\r
+# limits:\r
+# memory: "512Mi"\r
+# cpu: "500m"\r
+# requests:\r
+# memory: "256Mi"\r
+# cpu: "100m"\r
+# {{else}}\r
+# resources:\r
+# limits:\r
+# memory: "1Gi"\r
+# cpu: "1"\r
+# requests:\r
+# memory: "1Gi"\r
+# cpu: "1"\r
+# {{end}}\r
+ env:\r
+ - name: NAMESPACE\r
+ value: {{.Values.namespace}}\r
+ - name: APP_NAME\r
+ value: {{ .Values.appName}}\r
+ - name: APP_VERSION\r
+ value: {{.Values.version}}\r
+ volumeMounts:\r
+ - name: {{.Values.appName}}-cert-volume\r
+ mountPath: /opt/cert\r
+# {{ if or (eq .Values.env "st") (eq .Values.env "prod-dr")}}\r
+# {{else}}\r
+# - name: logging-pvc\r
+# mountPath: "/otf/logs"\r
+# {{end}}\r
+ livenessProbe:\r
+ httpGet:\r
+ path: {{.Values.health}}\r
+ port: http\r
+ scheme: HTTP\r
+ httpHeaders:\r
+ - name: X-Custom-Header\r
+ value: Alive\r
+ initialDelaySeconds: 30\r
+ timeoutSeconds: 30\r
+ periodSeconds: 30\r
+ readinessProbe:\r
+ httpGet:\r
+ path: {{.Values.health}}\r
+ port: http\r
+ scheme: HTTP\r
+ httpHeaders:\r
+ - name: X-Custom-Header\r
+ value: Ready\r
+ initialDelaySeconds: 30\r
+ timeoutSeconds: 30\r
+ periodSeconds: 30\r
+ restartPolicy: Always\r
--- /dev/null
+apiVersion: v1\r
+kind: Service\r
+metadata:\r
+ name: {{ .Values.appName }}\r
+ namespace: {{ .Values.namespace}}\r
+ labels:\r
+ app: {{ .Values.appName }}\r
+ version: {{ .Values.version}}\r
+spec:\r
+ type: NodePort\r
+ ports:\r
+ - name: http\r
+ port: 5000\r
+ protocol: TCP\r
+ nodePort: {{ .Values.nodePort}}\r
+ selector:\r
+ app: {{ .Values.appName }}\r
+ version: {{ .Values.version}}\r
--- /dev/null
+appName: a1-mediator-vth\r
+env: dev\r
+version: 0.0.1-SNAPSHOT\r
+image: a1-mediator-vth:0.0.1-SNAPSHOT\r
+namespace: org-oran-otf\r
+nodePort: 32324\r
+replicas: 1\r
+health : /otf/vth/oran/a1/v1/health\r
+sharedCert: otf-cert-secret-builder\r
+pvc:\r
+ dev: org-oran-otf-dev-logs-pv\r
+ prod: org-oran-otf-prod-logs-pv\r
--- /dev/null
+flask\r
+flask-cors\r
+FLASK\r
+FLASK-CORS\r
+requests
\ No newline at end of file
--- /dev/null
+# Copyright (c) 2019 AT&T Intellectual Property. #\r
+# #\r
+# Licensed under the Apache License, Version 2.0 (the "License"); #\r
+# you may not use this file except in compliance with the License. #\r
+# You may obtain a copy of the License at #\r
+# #\r
+# http://www.apache.org/licenses/LICENSE-2.0 #\r
+# #\r
+# Unless required by applicable law or agreed to in writing, software #\r
+# distributed under the License is distributed on an "AS IS" BASIS, #\r
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #\r
+# See the License for the specific language governing permissions and #\r
+# limitations under the License. #\r
+################################################################################\r
+\r
+FROM python:3.7.4\r
+\r
+RUN python --version\r
+\r
+ADD pip-requirements.txt pip-requirements.txt\r
+ADD dmaap_vth.py dmaap_vth.py\r
+ADD config.ini config.ini\r
+\r
+RUN mkdir -p /otf/logs\r
+\r
+RUN python -m pip install -r pip-requirements.txt\r
+ENTRYPOINT ["python", "dmaap_vth.py"]\r
--- /dev/null
+#!/usr/bin/env groovy\r
+\r
+/* Copyright (c) 2019 AT&T Intellectual Property. #\r
+# #\r
+# Licensed under the Apache License, Version 2.0 (the "License"); #\r
+# you may not use this file except in compliance with the License. #\r
+# You may obtain a copy of the License at #\r
+# #\r
+# http://www.apache.org/licenses/LICENSE-2.0 #\r
+# #\r
+# Unless required by applicable law or agreed to in writing, software #\r
+# distributed under the License is distributed on an "AS IS" BASIS, #\r
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #\r
+# See the License for the specific language governing permissions and #\r
+# limitations under the License. #\r
+##############################################################################*/\r
+\r
+properties([[$class: 'ParametersDefinitionProperty', parameterDefinitions: [\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'PHASE', defaultValue: "BUILD"],\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'ENV', defaultValue: "dev"],\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'MECHID', defaultValue: "id_otf_dev"],\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'KUBE_CONFIG', defaultValue: "kubeConfig-dev"],\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'TILLER_NAMESPACE', defaultValue: "org-oran-otf"]\r
+]]])\r
+\r
+\r
+echo "Build branch: ${env.BRANCH_NAME}"\r
+\r
+node("docker"){\r
+ stage 'Checkout'\r
+ checkout scm\r
+ PHASES=PHASE.tokenize( '_' );\r
+ echo "PHASES : " + PHASES\r
+\r
+\r
+ ARTIFACT_ID="dmaap-vth";\r
+ VERSION="0.0.1-SNAPSHOT";\r
+ NAMESPACE="org-oran-otf"\r
+ DOCKER_REGISTRY="registry.hub.docker.io"\r
+\r
+ if( ENV.equalsIgnoreCase("dev") ){\r
+ IMAGE_NAME=DOCKER_REGISTRY + "/" + NAMESPACE + ".dev" + "/" + ARTIFACT_ID + ":" + VERSION\r
+\r
+ }\r
+ if( ENV.equalsIgnoreCase("prod") || ENV.equalsIgnoreCase("prod-dr")){\r
+ IMAGE_NAME=DOCKER_REGISTRY + "/" + NAMESPACE + ".prod" + "/" + ARTIFACT_ID + ":" + VERSION\r
+\r
+ }\r
+\r
+ if( ENV.equalsIgnoreCase("st") ){\r
+ IMAGE_NAME=DOCKER_REGISTRY + "/" + NAMESPACE + ".st" + "/" + ARTIFACT_ID + ":" + VERSION\r
+\r
+ }\r
+\r
+ echo "Artifact: " + IMAGE_NAME\r
+\r
+ withEnv(["PATH=${env.PATH}:${env.WORKSPACE}/linux-amd64", "HELM_HOME=${env.WORKSPACE}"]) {\r
+\r
+ echo "PATH=${env.PATH}"\r
+ echo "HELM_HOME=${env.HELM_HOME}"\r
+\r
+ if (PHASES.contains("BUILD")){\r
+\r
+ stage 'Publish Artifact'\r
+\r
+ withCredentials([usernamePassword(credentialsId: MECHID, usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {\r
+ dir("dmaap-vth")\r
+ echo "Artifact: " + IMAGE_NAME\r
+\r
+ sh """\r
+ docker login $DOCKER_REGISTRY --username $USERNAME --password $PASSWORD\r
+ docker build -t $IMAGE_NAME .\r
+ docker push $IMAGE_NAME\r
+ """\r
+ }\r
+\r
+ }\r
+\r
+ if (PHASES.contains("DEPLOY") || PHASES.contains("UNDEPLOY")) {\r
+\r
+ stage 'Init Helm'\r
+\r
+ //check if helm exists if not install\r
+ if(fileExists('linux-amd64/helm')){\r
+ sh """\r
+ echo "helm is already installed"\r
+ """\r
+ }\r
+ else{\r
+ //download helm\r
+ sh """\r
+ echo "installing helm"\r
+ wget https://storage.googleapis.com/kubernetes-helm/helm-v2.14.3-linux-amd64.tar.gz\r
+ tar -xf helm-v2.14.3-linux-amd64.tar.gz\r
+ rm helm-v2.14.3-linux-amd64.tar.gz\r
+ """\r
+ }\r
+\r
+ withCredentials([file(credentialsId: KUBE_CONFIG, variable: 'KUBECONFIG')]) {\r
+\r
+ dir('dmaap-vth/helm'){\r
+ //check if charts are valid, and then perform dry run, if successful then upgrade/install charts\r
+\r
+ if (PHASES.contains("UNDEPLOY") ) {\r
+ stage 'Undeploy'\r
+\r
+ sh """\r
+ helm delete --tiller-namespace=$TILLER_NAMESPACE --purge $ARTIFACT_ID\r
+ """\r
+ }\r
+\r
+ //NOTE Double quotes are used below to access groovy variables like artifact_id and tiller_namespace\r
+ if (PHASES.contains("DEPLOY") ){\r
+ stage 'Deploy'\r
+ withCredentials([usernamePassword(credentialsId: MECHID, usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {\r
+\r
+ sh """\r
+ helm version\r
+ echo "Validate Yaml"\r
+ helm lint $ARTIFACT_ID\r
+\r
+ echo "View Helm Templates"\r
+ helm template $ARTIFACT_ID --set appName=$ARTIFACT_ID \\r
+ --set appName=$ARTIFACT_ID \\r
+ --set version=$VERSION \\r
+ --set env=$ENV \\r
+ --set image=$IMAGE_NAME \\r
+ --set namespace=$TILLER_NAMESPACE \\r
+ --set credentials.username= $USERNAME \\r
+ --set credentials.password= $PASSWORD\r
+\r
+ echo "Perform Dry Run Of Install"\r
+ helm upgrade --tiller-namespace=$TILLER_NAMESPACE --install --dry-run $ARTIFACT_ID $ARTIFACT_ID \\r
+ --set appName=$ARTIFACT_ID \\r
+ --set version=$VERSION \\r
+ --set env=$ENV \\r
+ --set image=$IMAGE_NAME \\r
+ --set namespace=$TILLER_NAMESPACE \\r
+ --set credentials.username=$USERNAME \\r
+ --set credentials.password=$PASSWORD\r
+\r
+\r
+ echo "Helm Install/Upgrade"\r
+ helm upgrade --tiller-namespace=$TILLER_NAMESPACE --install $ARTIFACT_ID $ARTIFACT_ID \\r
+ --set appName=$ARTIFACT_ID \\r
+ --set version=$VERSION \\r
+ --set env=$ENV \\r
+ --set image=$IMAGE_NAME \\r
+ --set namespace=$TILLER_NAMESPACE \\r
+ --set credentials.username=$USERNAME \\r
+ --set credentials.password=$PASSWORD\r
+\r
+ """\r
+ }\r
+ }\r
+\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+}\r
--- /dev/null
+[auth]\r
+auth_enabled= true\r
+username = %(USER)s\r
+password = %(PW)s\r
+[resource]\r
+proxy_enabled = true\r
+https_proxy= %(HTTPS)s\r
+http_proxy= %(HTTP)s\r
+base_address = %(BASE_URL)s\r
+publish = /{topic_name}\r
+subscribe = /{topic_name}/{consumer_group}/{consumer_id}
\ No newline at end of file
--- /dev/null
+# Copyright (c) 2019 AT&T Intellectual Property. #\r
+# #\r
+# Licensed under the Apache License, Version 2.0 (the "License"); #\r
+# you may not use this file except in compliance with the License. #\r
+# You may obtain a copy of the License at #\r
+# #\r
+# http://www.apache.org/licenses/LICENSE-2.0 #\r
+# #\r
+# Unless required by applicable law or agreed to in writing, software #\r
+# distributed under the License is distributed on an "AS IS" BASIS, #\r
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #\r
+# See the License for the specific language governing permissions and #\r
+# limitations under the License. #\r
+################################################################################\r
+# File name: dmaap-vth.py #\r
+# Description: vth that utilize dmaap to subscribe and publish to topics #\r
+# Date created: 02/21/2020 #\r
+# Last modified: 04/02/2020 #\r
+# Python Version: 3.7 #\r
+# Author: Jackie Chen (jv246a) #\r
+# Email: jv246a@att.com #\r
+################################################################################\r
+\r
+import datetime\r
+from configparser import ConfigParser\r
+import os\r
+import logging\r
+from logging import FileHandler\r
+import requests\r
+from flask import Flask, request, jsonify\r
+\r
+# redirect http to https\r
+app = Flask(__name__)\r
+\r
+# Prevents print statement every time an endpoint is triggered.\r
+logging.getLogger("werkzeug").setLevel(logging.WARNING)\r
+\r
+\r
+def sendCallback(url, data):\r
+ try:\r
+ if type(data) is not dict:\r
+ data = {"msg": data}\r
+ app.logger.info("sending callback")\r
+ requests.post(url, json= data)\r
+ except Exception as e:\r
+ app.logger.info(e)\r
+ return\r
+\r
+def unix_time_millis(dt):\r
+ epoch = datetime.datetime.utcfromtimestamp(0)\r
+ return (dt - epoch).total_seconds() * 1000.0\r
+\r
+\r
+def _get_request_data():\r
+ if not request.is_json:\r
+ raise ValueError("request must be json")\r
+ requestData = request.get_json()\r
+ return requestData\r
+\r
+\r
+def _get_config(config_file_name):\r
+ config = ConfigParser(os.environ)\r
+ config.read(config_file_name)\r
+ return config\r
+\r
+def _validate_request(request_data, isPublish=True):\r
+ missing_params = []\r
+\r
+ if 'topic_name' not in request_data:\r
+ missing_params.append("topic_name")\r
+ if isPublish:\r
+ if 'data' not in request_data:\r
+ missing_params.append('data')\r
+ else:\r
+ if 'consumer_group' not in request_data:\r
+ missing_params.append('consumer_group')\r
+ if 'consumer_id' not in request_data:\r
+ missing_params.append('consumer_id')\r
+\r
+ if missing_params:\r
+ err_msg = '{} request requires the following: '.format('publish' if isPublish else 'subscribe')\r
+ err_msg += ','.join(missing_params)\r
+ raise KeyError(err_msg)\r
+\r
+\r
+def _build_url(config, request_data, is_publish=True):\r
+ if is_publish:\r
+ base_path = config['resource']['base_address'] + config['resource']['publish']\r
+ topic_name = request_data['topic_name']\r
+ publish_address = base_path.format(topic_name=topic_name)\r
+ return publish_address\r
+\r
+ base_path = config['resource']['base_address'] + config['resource']['subscribe']\r
+ topic_name = request_data['topic_name']\r
+ consumer_group = request_data['consumer_group']\r
+ consumer_id = request_data['consumer_id']\r
+ subscribe_address = base_path.format(topic_name=topic_name, consumer_group=consumer_group, consumer_id=consumer_id)\r
+ if ('timeout' in request_data):\r
+ subscribe_address = (subscribe_address + '?timeout={}').format(request_data['timeout'])\r
+ return subscribe_address\r
+\r
+\r
+def _send_request(url, config, is_subscribe_request=False, payload=None):\r
+ # setup default values\r
+ auth_enabled = config.getboolean('auth', 'auth_enabled')\r
+ proxy_enabled = config.getboolean('resource', 'proxy_enabled')\r
+ username = ''\r
+ password = ''\r
+ req_proxies = {\r
+ 'http': None,\r
+ 'https': None\r
+ }\r
+ # place proxy and authentication information\r
+ if auth_enabled:\r
+ username = config['auth']['username']\r
+ password = config['auth']['password']\r
+ if proxy_enabled:\r
+ req_proxies['http'] = config['resource']['http_proxy']\r
+ req_proxies['https'] = config['resource']['https_proxy']\r
+\r
+ # for subscribe request\r
+ if is_subscribe_request:\r
+ return requests.get(url,\r
+ auth=(username, password) if auth_enabled else None,\r
+ proxies=req_proxies if proxy_enabled else None)\r
+ # for publish request\r
+ req_headers = {'Content-type': 'application/json'}\r
+ return requests.post(url,\r
+ json=payload,\r
+ auth=(username, password) if auth_enabled else None,\r
+ proxies=req_proxies if proxy_enabled else None,\r
+ headers=req_headers)\r
+\r
+@app.route("/otf/vth/oran/dmaap/v1/health", methods=['GET'])\r
+def getHealth():\r
+ return 'UP'\r
+\r
+@app.route("/otf/vth/oran/dmaap/v1/subscribe", methods=["POST"])\r
+def subscribeRequest():\r
+ response_data = {\r
+ 'vthResponse': {\r
+ 'testDuration': '',\r
+ 'dateTimeUTC': str(datetime.datetime.now()),\r
+ 'abstractMessage': '',\r
+ 'resultData': {}\r
+ }\r
+ }\r
+ ret_url = request.args.get('retURL')\r
+ startTime = unix_time_millis(datetime.datetime.now())\r
+ try:\r
+ # validate request\r
+ request_data = _get_request_data()\r
+ _validate_request(request_data, isPublish=False)\r
+ app.logger.info("incoming subscribe request w/ the following payload:" + str(request_data))\r
+\r
+ # setup phase\r
+ config = _get_config('config.ini')\r
+ subscribe_address = _build_url(config, request_data, is_publish=False)\r
+\r
+ # build response\r
+ app.logger.info('Sending GET to subscribe')\r
+ res = _send_request(subscribe_address, config, is_subscribe_request=True)\r
+ app.logger.info('Response received from subscribe: {}'.format(res.json()))\r
+ response_data['vthResponse']['abstractMessage'] = 'Result from subscribe request'\r
+ response_data['vthResponse']['resultData']['status_code'] = res.status_code\r
+ response_data['vthResponse']['resultData']['result_output'] = res.json()\r
+ except Exception as ex:\r
+ endTime = unix_time_millis(datetime.datetime.now())\r
+ totalTime = endTime - startTime\r
+ response_data['vthResponse']['testDuration'] = totalTime\r
+ response_data['vthResponse']['abstractMessage'] = 'error: ' + str(ex)\r
+ app.logger.error('ERROR:{}'.format(str(ex)))\r
+ return jsonify(response_data)\r
+\r
+ endTime = unix_time_millis(datetime.datetime.now())\r
+ totalTime = endTime - startTime\r
+ response_data['vthResponse']['testDuration'] = totalTime\r
+ if ret_url is not None:\r
+ sendCallback(ret_url,response_data)\r
+ return '',200\r
+ return jsonify(response_data), 200\r
+\r
+\r
+@app.route("/otf/vth/oran/dmaap/v1/publish", methods=['POST'])\r
+def publishRequest():\r
+ response_data = {\r
+ 'vthResponse': {\r
+ 'testDuration': '',\r
+ 'dateTimeUTC': str(datetime.datetime.now()),\r
+ 'abstractMessage': '',\r
+ 'resultData': {}\r
+ }\r
+ }\r
+ startTime = unix_time_millis(datetime.datetime.now())\r
+ ret_url = request.args.get('retURL')\r
+\r
+ try:\r
+ # validate request\r
+ request_data = _get_request_data()\r
+ _validate_request(request_data)\r
+ app.logger.info("incoming publish request w/ the following payload:" + str(request_data))\r
+\r
+ # setup phase\r
+ config = _get_config('config.ini')\r
+ payload = request_data['data']\r
+ publish_address = _build_url(config, request_data)\r
+\r
+ # build response\r
+ app.logger.info("Sending POST to publish")\r
+ res = _send_request(url=publish_address, config=config, payload=payload)\r
+ app.logger.info("Response received from publish: {}".format(res.json()))\r
+ response_data['vthResponse']['abstractMessage'] = 'Result from publish request'\r
+ response_data['vthResponse']['resultData']['status_code'] = res.status_code\r
+ response_data['vthResponse']['resultData']['result_output'] = res.json()\r
+ except Exception as ex:\r
+ endTime = unix_time_millis(datetime.datetime.now())\r
+ totalTime = endTime - startTime\r
+ response_data['vthResponse']['testDuration'] = totalTime\r
+ response_data['vthResponse']['abstractMessage'] = 'error: ' + str(ex)\r
+ app.logger.error('ERROR:{}'.format(str(ex)))\r
+ return jsonify(response_data)\r
+\r
+ endTime = unix_time_millis(datetime.datetime.now())\r
+ totalTime = endTime - startTime\r
+ response_data['vthResponse']['testDuration'] = totalTime\r
+ if ret_url is not None:\r
+ sendCallback(ret_url,response_data)\r
+ return '',200\r
+ return jsonify(response_data), 200\r
+\r
+if __name__ == '__main__':\r
+ logHandler = FileHandler('dmaap-vth.log', mode='a')\r
+ logHandler.setLevel(logging.INFO)\r
+ app.logger.setLevel(logging.INFO)\r
+ app.logger.addHandler(logHandler)\r
+ # context = ('opt/cert/otf.pem', 'opt/cert/privateKey.pem')\r
+ # app.run(debug = False, host = '0.0.0.0', port = 5000, ssl_context = context)\r
+ app.run(debug=False, host='0.0.0.0', port=5000)\r
--- /dev/null
+# Patterns to ignore when building packages.\r
+# This supports shell glob matching, relative path matching, and\r
+# negation (prefixed with !). Only one pattern per line.\r
+.DS_Store\r
+# Common VCS dirs\r
+.git/\r
+.gitignore\r
+.bzr/\r
+.bzrignore\r
+.hg/\r
+.hgignore\r
+.svn/\r
+# Common backup files\r
+*.swp\r
+*.bak\r
+*.tmp\r
+*~\r
+# Various IDEs\r
+.project\r
+.idea/\r
+*.tmproj\r
--- /dev/null
+apiVersion: v1\r
+appVersion: "1.0"\r
+description: A Helm chart for the dmaap Virtual Test Head\r
+name: dmaap-vth\r
+version: 0.0.1\r
--- /dev/null
+apiVersion: extensions/v1beta1\r
+kind: Deployment\r
+metadata:\r
+ name: {{ .Values.appName}}\r
+ namespace: {{.Values.namespace}}\r
+ labels:\r
+ app: {{ .Values.appName}}\r
+ version: {{.Values.version}}\r
+spec:\r
+ revisionHistoryLimit: 1\r
+ minReadySeconds: 10\r
+ strategy:\r
+ # indicate which strategy we want for rolling update\r
+ type: RollingUpdate\r
+ rollingUpdate:\r
+ maxSurge: 0\r
+ maxUnavailable: 1\r
+ replicas: {{ .Values.replicas}}\r
+ selector:\r
+ matchLabels:\r
+ app: {{ .Values.appName}}\r
+ version: {{.Values.version}}\r
+ template:\r
+ metadata:\r
+ labels:\r
+ app: {{ .Values.appName}}\r
+ version: {{.Values.version}}\r
+ spec:\r
+ serviceAccount: default\r
+ volumes:\r
+ - name: {{ .Values.appName}}-cert-volume\r
+ secret:\r
+ secretName: {{.Values.sharedCert}}\r
+ optional: true\r
+ items:\r
+ - key: PEM_CERT\r
+ path: otf.pem\r
+ - key: PEM_KEY\r
+ path: privateKey.pem\r
+ containers:\r
+ - name: {{ .Values.appName}}\r
+ image: {{ .Values.image}}\r
+ imagePullPolicy: Always\r
+ ports:\r
+ - name: http\r
+ containerPort: 5000\r
+ nodePort: {{.Values.nodePort}}\r
+ protocol: TCP\r
+ env:\r
+ - name: NAMESPACE\r
+ value: {{.Values.namespace}}\r
+ - name: APP_NAME\r
+ value: {{ .Values.appName}}\r
+ - name: APP_VERSION\r
+ value: {{.Values.version}}\r
+ - name: HTTP\r
+ value: {{ .Values.HTTP}}\r
+ - name: HTTPS\r
+ value: {{ .Values.HTTPS}}\r
+ - name: BASE_URL\r
+ value: {{ .Values.BASE_URL}}\r
+ - name: USER\r
+ valueFrom:\r
+ secretKeyRef:\r
+ name: {{ .Values.appName}}\r
+ key: username\r
+ - name: PW\r
+ valueFrom:\r
+ secretKeyRef:\r
+ name: {{ .Values.appName}}\r
+ key: password\r
+ volumeMounts:\r
+ - name: {{.Values.appName}}-cert-volume\r
+ mountPath: /opt/cert\r
+ livenessProbe:\r
+ httpGet:\r
+ path: {{.Values.health}}\r
+ port: http\r
+ scheme: HTTP\r
+ httpHeaders:\r
+ - name: X-Custom-Header\r
+ value: Alive\r
+ initialDelaySeconds: 30\r
+ timeoutSeconds: 30\r
+ periodSeconds: 30\r
+ readinessProbe:\r
+ httpGet:\r
+ path: {{.Values.health}}\r
+ port: http\r
+ scheme: HTTP\r
+ httpHeaders:\r
+ - name: X-Custom-Header\r
+ value: Ready\r
+ initialDelaySeconds: 30\r
+ timeoutSeconds: 30\r
+ periodSeconds: 30\r
+ restartPolicy: Always\r
--- /dev/null
+apiVersion: v1\r
+kind: Secret\r
+metadata:\r
+ name: {{ .Values.appName}}\r
+type: Opaque\r
+data:\r
+ username: {{ .Values.credentials.username | b64enc}}\r
+ password: {{ .Values.credentials.password | b64enc}}
\ No newline at end of file
--- /dev/null
+apiVersion: v1\r
+kind: Service\r
+metadata:\r
+ name: {{ .Values.appName }}\r
+ namespace: {{ .Values.namespace}}\r
+ labels:\r
+ app: {{ .Values.appName }}\r
+ version: {{ .Values.version}}\r
+spec:\r
+ type: NodePort\r
+ ports:\r
+ - name: http\r
+ port: 5000\r
+ protocol: TCP\r
+ nodePort: {{ .Values.nodePort}}\r
+ selector:\r
+ app: {{ .Values.appName }}\r
+ version: {{ .Values.version}}\r
--- /dev/null
+appName: dmaap-vth\r
+env: dev\r
+version: 0.0.1-SNAPSHOT\r
+image: dmaap-vth:0.0.1-SNAPSHOT\r
+namespace: org-oran-otf\r
+nodePort: 32324\r
+replicas: 1\r
+health : /otf/vth/oran/dmaap/v1/health\r
+sharedCert: otf-cert-secret-builder\r
+pvc:\r
+ dev: org-oran-otf-dev-logs-pv\r
+ prod: org-oran-otf-prod-logs-pv\r
+HTTP: "proxy address here if there is"\r
+HTTPS: "proxy address here if there is"\r
+BASE_URL: "base dmaap address here"\r
+credentials:\r
+ username: "!"\r
+ password: "!"\r
--- /dev/null
+flask\r
+flask-cors\r
+FLASK\r
+FLASK-CORS\r
+requests\r
+configparser
\ No newline at end of file
--- /dev/null
+# Copyright (c) 2019 AT&T Intellectual Property. #\r
+# #\r
+# Licensed under the Apache License, Version 2.0 (the "License"); #\r
+# you may not use this file except in compliance with the License. #\r
+# You may obtain a copy of the License at #\r
+# #\r
+# http://www.apache.org/licenses/LICENSE-2.0 #\r
+# #\r
+# Unless required by applicable law or agreed to in writing, software #\r
+# distributed under the License is distributed on an "AS IS" BASIS, #\r
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #\r
+# See the License for the specific language governing permissions and #\r
+# limitations under the License. #\r
+################################################################################\r
+\r
+FROM python:3.7.4\r
+\r
+RUN python --version\r
+\r
+ADD pip-requirements.txt pip-requirements.txt\r
+ADD o1_vth.py o1_vth.py\r
+ADD config.ini config.ini\r
+\r
+RUN mkdir -p /otf/logs\r
+\r
+RUN python -m pip install -r pip-requirements.txt\r
+ENTRYPOINT ["python", "o1_vth.py"]\r
--- /dev/null
+#!/usr/bin/env groovy\r
+\r
+/* Copyright (c) 2019 AT&T Intellectual Property. #\r
+# #\r
+# Licensed under the Apache License, Version 2.0 (the "License"); #\r
+# you may not use this file except in compliance with the License. #\r
+# You may obtain a copy of the License at #\r
+# #\r
+# http://www.apache.org/licenses/LICENSE-2.0 #\r
+# #\r
+# Unless required by applicable law or agreed to in writing, software #\r
+# distributed under the License is distributed on an "AS IS" BASIS, #\r
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #\r
+# See the License for the specific language governing permissions and #\r
+# limitations under the License. #\r
+##############################################################################*/\r
+\r
+properties([[$class: 'ParametersDefinitionProperty', parameterDefinitions: [\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'PHASE', defaultValue: "BUILD"],\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'ENV', defaultValue: "dev"],\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'MECHID', defaultValue: "id_otf_dev"],\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'KUBE_CONFIG', defaultValue: "kubeConfig-dev"],\r
+ [$class: 'hudson.model.StringParameterDefinition', name: 'TILLER_NAMESPACE', defaultValue: "registry.hub.docker.io"]\r
+]]])\r
+\r
+\r
+echo "Build branch: ${env.BRANCH_NAME}"\r
+\r
+node("docker"){\r
+ stage 'Checkout'\r
+ checkout scm\r
+ PHASES=PHASE.tokenize( '_' );\r
+ echo "PHASES : " + PHASES\r
+\r
+\r
+ ARTIFACT_ID="o1-vth";\r
+ VERSION="0.0.1-SNAPSHOT";\r
+ NAMESPACE="org-oran-otf"\r
+ DOCKER_REGISTRY="registry.hub.docker.io"\r
+\r
+ if( ENV.equalsIgnoreCase("dev") ){\r
+ IMAGE_NAME=DOCKER_REGISTRY + "/" + NAMESPACE + ".dev" + "/" + ARTIFACT_ID + ":" + VERSION\r
+\r
+ }\r
+ if( ENV.equalsIgnoreCase("prod") || ENV.equalsIgnoreCase("prod-dr")){\r
+ IMAGE_NAME=DOCKER_REGISTRY + "/" + NAMESPACE + ".prod" + "/" + ARTIFACT_ID + ":" + VERSION\r
+\r
+ }\r
+\r
+ if( ENV.equalsIgnoreCase("st") ){\r
+ IMAGE_NAME=DOCKER_REGISTRY + "/" + NAMESPACE + ".st" + "/" + ARTIFACT_ID + ":" + VERSION\r
+\r
+ }\r
+\r
+ echo "Artifact: " + IMAGE_NAME\r
+\r
+ withEnv(["PATH=${env.PATH}:${env.WORKSPACE}/linux-amd64", "HELM_HOME=${env.WORKSPACE}"]) {\r
+\r
+ echo "PATH=${env.PATH}"\r
+ echo "HELM_HOME=${env.HELM_HOME}"\r
+\r
+ if (PHASES.contains("BUILD")){\r
+\r
+ stage 'Publish Artifact'\r
+\r
+ withCredentials([usernamePassword(credentialsId: MECHID, usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {\r
+\r
+ dir("o1-vth"){\r
+ echo "Artifact: " + IMAGE_NAME\r
+\r
+ sh """\r
+ docker login $DOCKER_REGISTRY --username $USERNAME --password $PASSWORD\r
+ docker build -t $IMAGE_NAME .\r
+ docker push $IMAGE_NAME\r
+ """\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ if (PHASES.contains("DEPLOY") || PHASES.contains("UNDEPLOY")) {\r
+\r
+ stage 'Init Helm'\r
+\r
+ //check if helm exists if not install\r
+ if(fileExists('linux-amd64/helm')){\r
+ sh """\r
+ echo "helm is already installed"\r
+ """\r
+ }\r
+ else{\r
+ //download helm\r
+ sh """\r
+ echo "installing helm"\r
+ wget https://storage.googleapis.com/kubernetes-helm/helm-v2.14.3-linux-amd64.tar.gz\r
+ tar -xf helm-v2.14.3-linux-amd64.tar.gz\r
+ rm helm-v2.14.3-linux-amd64.tar.gz\r
+ """\r
+ }\r
+\r
+ withCredentials([file(credentialsId: KUBE_CONFIG, variable: 'KUBECONFIG')]) {\r
+\r
+ dir('o1-vth/helm'){\r
+ //check if charts are valid, and then perform dry run, if successful then upgrade/install charts\r
+\r
+ if (PHASES.contains("UNDEPLOY") ) {\r
+ stage 'Undeploy'\r
+\r
+ sh """\r
+ helm delete --tiller-namespace=$TILLER_NAMESPACE --purge $ARTIFACT_ID\r
+ """\r
+ }\r
+\r
+ //NOTE Double quotes are used below to access groovy variables like artifact_id and tiller_namespace\r
+ if (PHASES.contains("DEPLOY") ){\r
+ stage 'Deploy'\r
+ withCredentials([usernamePassword(credentialsId: MECHID, usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {\r
+\r
+ sh """\r
+ helm version\r
+ echo "Validate Yaml"\r
+ helm lint $ARTIFACT_ID\r
+\r
+ echo "View Helm Templates"\r
+ helm template $ARTIFACT_ID --set appName=$ARTIFACT_ID \\r
+ --set appName=$ARTIFACT_ID \\r
+ --set version=$VERSION \\r
+ --set env=$ENV \\r
+ --set image=$IMAGE_NAME \\r
+ --set namespace=$TILLER_NAMESPACE \\r
+ --set credentials.username= $USERNAME \\r
+ --set credentials.password= $PASSWORD\r
+\r
+ echo "Perform Dry Run Of Install"\r
+ helm upgrade --tiller-namespace=$TILLER_NAMESPACE --install --dry-run $ARTIFACT_ID $ARTIFACT_ID \\r
+ --set appName=$ARTIFACT_ID \\r
+ --set version=$VERSION \\r
+ --set env=$ENV \\r
+ --set image=$IMAGE_NAME \\r
+ --set namespace=$TILLER_NAMESPACE \\r
+ --set credentials.username=$USERNAME \\r
+ --set credentials.password=$PASSWORD\r
+\r
+\r
+ echo "Helm Install/Upgrade"\r
+ helm upgrade --tiller-namespace=$TILLER_NAMESPACE --install $ARTIFACT_ID $ARTIFACT_ID \\r
+ --set appName=$ARTIFACT_ID \\r
+ --set version=$VERSION \\r
+ --set env=$ENV \\r
+ --set image=$IMAGE_NAME \\r
+ --set namespace=$TILLER_NAMESPACE \\r
+ --set credentials.username=$USERNAME \\r
+ --set credentials.password=$PASSWORD\r
+\r
+ """\r
+ }\r
+ }\r
+\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+}\r
--- /dev/null
+[auth]\r
+auth_enabled = true\r
+username = %(USER)s\r
+password = %(PW)s\r
+[resource]\r
+proxy_enabled = true\r
+https_proxy = %(HTTPS)s\r
+http_proxy = %(HTTP)s\r
+base_address = %(BASE_URL)s\r
+get_config_alarms_list = /restconf/config/network-topology:network-topology/topology/topology-netconf/node/o-ran-ru-1/yang-ext:mount/ietf-alarms:alarms/alarm-list
\ No newline at end of file
--- /dev/null
+# Patterns to ignore when building packages.\r
+# This supports shell glob matching, relative path matching, and\r
+# negation (prefixed with !). Only one pattern per line.\r
+.DS_Store\r
+# Common VCS dirs\r
+.git/\r
+.gitignore\r
+.bzr/\r
+.bzrignore\r
+.hg/\r
+.hgignore\r
+.svn/\r
+# Common backup files\r
+*.swp\r
+*.bak\r
+*.tmp\r
+*~\r
+# Various IDEs\r
+.project\r
+.idea/\r
+*.tmproj\r
--- /dev/null
+apiVersion: v1\r
+appVersion: "1.0"\r
+description: A Helm chart for the o1 Virtual Test Head\r
+name: o1-vth\r
+version: 0.0.1\r
--- /dev/null
+apiVersion: extensions/v1beta1\r
+kind: Deployment\r
+metadata:\r
+ name: {{ .Values.appName}}\r
+ namespace: {{.Values.namespace}}\r
+ labels:\r
+ app: {{ .Values.appName}}\r
+ version: {{.Values.version}}\r
+spec:\r
+ revisionHistoryLimit: 1\r
+ minReadySeconds: 10\r
+ strategy:\r
+ # indicate which strategy we want for rolling update\r
+ type: RollingUpdate\r
+ rollingUpdate:\r
+ maxSurge: 0\r
+ maxUnavailable: 1\r
+ replicas: {{ .Values.replicas}}\r
+ selector:\r
+ matchLabels:\r
+ app: {{ .Values.appName}}\r
+ version: {{.Values.version}}\r
+ template:\r
+ metadata:\r
+ labels:\r
+ app: {{ .Values.appName}}\r
+ version: {{.Values.version}}\r
+ spec:\r
+ serviceAccount: default\r
+ volumes:\r
+ - name: {{ .Values.appName}}-cert-volume\r
+ secret:\r
+ secretName: {{.Values.sharedCert}}\r
+ optional: true\r
+ items:\r
+ - key: PEM_CERT\r
+ path: otf.pem\r
+ - key: PEM_KEY\r
+ path: privateKey.pem\r
+ containers:\r
+ - name: {{ .Values.appName}}\r
+ image: {{ .Values.image}}\r
+ imagePullPolicy: Always\r
+ ports:\r
+ - name: http\r
+ containerPort: 5000\r
+ nodePort: {{.Values.nodePort}}\r
+ protocol: TCP\r
+ env:\r
+ - name: NAMESPACE\r
+ value: {{.Values.namespace}}\r
+ - name: APP_NAME\r
+ value: {{ .Values.appName}}\r
+ - name: APP_VERSION\r
+ value: {{.Values.version}}\r
+ - name: HTTP\r
+ value: {{ .Values.HTTP}}\r
+ - name: HTTPS\r
+ value: {{ .Values.HTTPS}}\r
+ - name: BASE_URL\r
+ value: {{ .Values.BASE_URL}}\r
+ - name: USER\r
+ valueFrom:\r
+ secretKeyRef:\r
+ name: {{ .Values.appName}}\r
+ key: username\r
+ - name: PW\r
+ valueFrom:\r
+ secretKeyRef:\r
+ name: {{ .Values.appName}}\r
+ key: password\r
+ volumeMounts:\r
+ - name: {{.Values.appName}}-cert-volume\r
+ mountPath: /opt/cert\r
+ livenessProbe:\r
+ httpGet:\r
+ path: {{.Values.health}}\r
+ port: http\r
+ scheme: HTTP\r
+ httpHeaders:\r
+ - name: X-Custom-Header\r
+ value: Alive\r
+ initialDelaySeconds: 30\r
+ timeoutSeconds: 30\r
+ periodSeconds: 30\r
+ readinessProbe:\r
+ httpGet:\r
+ path: {{.Values.health}}\r
+ port: http\r
+ scheme: HTTP\r
+ httpHeaders:\r
+ - name: X-Custom-Header\r
+ value: Ready\r
+ initialDelaySeconds: 30\r
+ timeoutSeconds: 30\r
+ periodSeconds: 30\r
+ restartPolicy: Always\r
--- /dev/null
+apiVersion: v1\r
+kind: Secret\r
+metadata:\r
+ name: {{ .Values.appName}}\r
+type: Opaque\r
+data:\r
+ username: {{ .Values.credentials.username | b64enc}}\r
+ password: {{ .Values.credentials.password | b64enc}}
\ No newline at end of file
--- /dev/null
+apiVersion: v1\r
+kind: Service\r
+metadata:\r
+ name: {{ .Values.appName }}\r
+ namespace: {{ .Values.namespace}}\r
+ labels:\r
+ app: {{ .Values.appName }}\r
+ version: {{ .Values.version}}\r
+spec:\r
+ type: NodePort\r
+ ports:\r
+ - name: http\r
+ port: 5000\r
+ protocol: TCP\r
+ nodePort: {{ .Values.nodePort}}\r
+ selector:\r
+ app: {{ .Values.appName }}\r
+ version: {{ .Values.version}}\r
--- /dev/null
+appName: o1-vth\r
+env: dev\r
+version: 0.0.1-SNAPSHOT\r
+image: o1-vth:0.0.1-SNAPSHOT\r
+namespace: org-oran-otf\r
+nodePort: 32130\r
+replicas: 1\r
+health : /otf/vth/oran/smo/v1/health\r
+sharedCert: otf-cert-secret-builder\r
+pvc:\r
+ dev: org-oran-otf-dev-logs-pv\r
+ prod: org-oran-otf-prod-logs-pv\r
+HTTP: "[Your HTTP PROXY HERE]"\r
+HTTPS: "[Your HTTPS PROXY HERE]"\r
+BASE_URL: http://sdn-r-dev.open5g-test.com\r
+credentials:\r
+ username: "!"\r
+ password: "!"\r
--- /dev/null
+# Copyright (c) 2019 AT&T Intellectual Property. #\r
+# #\r
+# Licensed under the Apache License, Version 2.0 (the "License"); #\r
+# you may not use this file except in compliance with the License. #\r
+# You may obtain a copy of the License at #\r
+# #\r
+# http://www.apache.org/licenses/LICENSE-2.0 #\r
+# #\r
+# Unless required by applicable law or agreed to in writing, software #\r
+# distributed under the License is distributed on an "AS IS" BASIS, #\r
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #\r
+# See the License for the specific language governing permissions and #\r
+# limitations under the License. #\r
+################################################################################\r
+# File name: o1-vth.py #\r
+# Description: Mainly used to get alarm list #\r
+# Date created: 04/14/2020 #\r
+# Python Version: 3.7 #\r
+# Author: Jackie Chen (jv246a) #\r
+# Email: jv246a@att.com #\r
+################################################################################\r
+\r
+import datetime\r
+from configparser import ConfigParser\r
+import os\r
+import logging\r
+from logging import FileHandler\r
+import requests\r
+from flask import Flask, request, jsonify\r
+\r
+# redirect http to https\r
+app = Flask(__name__)\r
+\r
+# Prevents print statement every time an endpoint is triggered.\r
+logging.getLogger("werkzeug").setLevel(logging.WARNING)\r
+\r
+\r
+def sendCallback(url, data):\r
+ try:\r
+ if type(data) is not dict:\r
+ data = {"msg": data}\r
+ app.logger.info("sending callback")\r
+ requests.post(url, json= data)\r
+ except Exception as e:\r
+ app.logger.info(e)\r
+ return\r
+\r
+def unix_time_millis(dt):\r
+ epoch = datetime.datetime.utcfromtimestamp(0)\r
+ return (dt - epoch).total_seconds() * 1000.0\r
+\r
+\r
+def _get_config(config_file_name):\r
+ config = ConfigParser(os.environ)\r
+ config.read(config_file_name)\r
+ return config\r
+\r
+\r
+def _build_url(config):\r
+ return config['resource']['base_address'] + config['resource']['get_config_alarms_list']\r
+\r
+\r
+def _send_request(url, config):\r
+ # setup default values\r
+ proxy_enabled = config.getboolean('resource', 'proxy_enabled')\r
+ auth_enabled = config.getboolean('auth', 'auth_enabled')\r
+ username = ''\r
+ password = ''\r
+\r
+ req_proxies = {\r
+ 'http': None,\r
+ 'https': None\r
+ }\r
+ # place proxy information\r
+ if proxy_enabled:\r
+ req_proxies['http'] = config['resource']['http_proxy']\r
+ req_proxies['https'] = config['resource']['https_proxy']\r
+ if auth_enabled:\r
+ username = config['auth']['username']\r
+ password = config['auth']['password']\r
+ # get call for alarm list\r
+ return requests.get(url,\r
+ auth=(username, password) if auth_enabled else None,\r
+ proxies=req_proxies if proxy_enabled else None)\r
+\r
+def _parse_resposne(response):\r
+ try:\r
+ return response.json()\r
+ except ValueError:\r
+ return response.text\r
+\r
+@app.route('/otf/vth/oran/smo/v1/alarm-list' , methods=['POST'])\r
+def get_alarm_list():\r
+ response_data = {\r
+ 'vthResponse': {\r
+ 'testDuration': '',\r
+ 'dateTimeUTC': str(datetime.datetime.now()),\r
+ 'abstractMessage': '',\r
+ 'resultData': {}\r
+ }\r
+ }\r
+ ret_url = request.args.get('retURL')\r
+ startTime = unix_time_millis(datetime.datetime.now())\r
+ try:\r
+ # setup phase\r
+ config = _get_config('config.ini')\r
+ alarm_list_url = _build_url(config)\r
+\r
+ # build initial response\r
+ app.logger.info('Sending GET for alarm list')\r
+ res = _send_request(alarm_list_url, config)\r
+ app.logger.info('Status code from GET: {}'.format(res.status_code))\r
+ app.logger.info('Response received from GET alarm-list: {}'.format(res.content))\r
+ response_data['vthResponse']['abstractMessage'] = 'Result from GET alarm list request'\r
+ response_data['vthResponse']['resultData']['status_code'] = res.status_code\r
+ response_data['vthResponse']['resultData']['result_output'] = _parse_resposne(res)\r
+ except Exception as ex:\r
+ endTime = unix_time_millis(datetime.datetime.now())\r
+ totalTime = endTime - startTime\r
+ response_data['vthResponse']['testDuration'] = totalTime\r
+ response_data['vthResponse']['abstractMessage'] = 'error: ' + str(ex)\r
+ app.logger.error('ERROR:{}'.format(str(ex)))\r
+ return jsonify(response_data)\r
+\r
+ #finish up building response\r
+ endTime = unix_time_millis(datetime.datetime.now())\r
+ totalTime = endTime - startTime\r
+ response_data['vthResponse']['testDuration'] = totalTime\r
+ if ret_url is not None:\r
+ sendCallback(ret_url, response_data)\r
+ return '', 200\r
+ return jsonify(response_data), 200\r
+\r
+@app.route("/otf/vth/oran/smo/v1/health", methods=['GET'])\r
+def getHealth():\r
+ return 'UP'\r
+\r
+if __name__ == '__main__':\r
+ logHandler = FileHandler('o1-vth.log', mode='a')\r
+ logHandler.setLevel(logging.INFO)\r
+ app.logger.setLevel(logging.INFO)\r
+ app.logger.addHandler(logHandler)\r
+ # context = ('opt/cert/otf.pem', 'opt/cert/privateKey.pem')\r
+ # app.run(debug = False, host = '0.0.0.0', port = 5000, ssl_context = context)\r
+ app.run(debug=False, host='0.0.0.0', port=5000)\r
--- /dev/null
+flask\r
+flask-cors\r
+FLASK\r
+FLASK-CORS\r
+requests\r
+configparser
\ No newline at end of file