Skip to content
Snippets Groups Projects
Commit e7be252c authored by Pablo Armingol's avatar Pablo Armingol
Browse files

pre merge code clean up

parent 04a4f930
No related branches found
No related tags found
2 merge requests!142Release TeraFlowSDN 2.1,!104OpenConfig driver multivendor & ACLs
......@@ -78,7 +78,7 @@ export CRDB_DATABASE="tfs"
export CRDB_DEPLOY_MODE="single"
# Disable flag for dropping database, if it exists.
export CRDB_DROP_DATABASE_IF_EXISTS="YES"
export CRDB_DROP_DATABASE_IF_EXISTS=""
# Disable flag for re-deploying CockroachDB from scratch.
export CRDB_REDEPLOY=""
......@@ -126,7 +126,7 @@ export QDB_TABLE_MONITORING_KPIS="tfs_monitoring_kpis"
export QDB_TABLE_SLICE_GROUPS="tfs_slice_groups"
# Disable flag for dropping tables if they exist.
export QDB_DROP_TABLES_IF_EXIST="YES"
export QDB_DROP_TABLES_IF_EXIST=""
# Disable flag for re-deploying QuestDB from scratch.
export QDB_REDEPLOY=""
......
#!/bin/bash
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# 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.
########################################################################################################################
# Read deployment settings
########################################################################################################################
# ----- TeraFlowSDN ------------------------------------------------------------
# If not already set, set the URL of the Docker registry where the images will be uploaded to.
# By default, assume internal MicroK8s registry is used.
export TFS_REGISTRY_IMAGES=${TFS_REGISTRY_IMAGES:-"http://localhost:32000/tfs/"}
# If not already set, set the list of components, separated by spaces, you want to build images for, and deploy.
# By default, only basic components are deployed
#All components: context device automation monitoring pathcomp service slice compute webui load_generator
export TFS_COMPONENTS=${TFS_COMPONENTS:-"context device automation monitoring pathcomp service slice compute webui load_generator"}
# If not already set, set the tag you want to use for your images.
export TFS_IMAGE_TAG=${TFS_IMAGE_TAG:-"dev"}
# If not already set, set the name of the Kubernetes namespace to deploy TFS to.
export TFS_K8S_NAMESPACE=${TFS_K8S_NAMESPACE:-"tfs"}
# If not already set, set additional manifest files to be applied after the deployment
export TFS_EXTRA_MANIFESTS=${TFS_EXTRA_MANIFESTS:-""}
# If not already set, set the new Grafana admin password
export TFS_GRAFANA_PASSWORD=${TFS_GRAFANA_PASSWORD:-"admin123+"}
# If not already set, disable skip-build flag to rebuild the Docker images.
# If TFS_SKIP_BUILD is "YES", the containers are not rebuilt-retagged-repushed and existing ones are used.
export TFS_SKIP_BUILD=${TFS_SKIP_BUILD:-"YES"}
# If TFS_SKIP_BUILD is "YES", select the containers to be build
# Any other container will use previous docker images
export TFS_QUICK_COMPONENTS="webui"
# ----- CockroachDB ------------------------------------------------------------
# If not already set, set the namespace where CockroackDB will be deployed.
export CRDB_NAMESPACE=${CRDB_NAMESPACE:-"crdb"}
# If not already set, set the database username to be used by Context.
export CRDB_USERNAME=${CRDB_USERNAME:-"tfs"}
# If not already set, set the database user's password to be used by Context.
export CRDB_PASSWORD=${CRDB_PASSWORD:-"tfs123"}
# If not already set, set the database name to be used by Context.
export CRDB_DATABASE=${CRDB_DATABASE:-"tfs"}
# ----- NATS -------------------------------------------------------------------
# If not already set, set the namespace where NATS will be deployed.
export NATS_NAMESPACE=${NATS_NAMESPACE:-"nats"}
# ----- QuestDB ----------------------------------------------------------------
# If not already set, set the namespace where QuestDB will be deployed.
export QDB_NAMESPACE=${QDB_NAMESPACE:-"qdb"}
# If not already set, set the database username to be used for QuestDB.
export QDB_USERNAME=${QDB_USERNAME:-"admin"}
# If not already set, set the database user's password to be used for QuestDB.
export QDB_PASSWORD=${QDB_PASSWORD:-"quest"}
# If not already set, set the table name to be used by Monitoring for KPIs.
export QDB_TABLE_MONITORING_KPIS=${QDB_TABLE_MONITORING_KPIS:-"tfs_monitoring_kpis"}
# If not already set, set the table name to be used by Slice for plotting groups.
export QDB_TABLE_SLICE_GROUPS=${QDB_TABLE_SLICE_GROUPS:-"tfs_slice_groups"}
########################################################################################################################
# Automated steps start here
########################################################################################################################
# Constants
GITLAB_REPO_URL="labs.etsi.org:5050/tfs/controller"
TMP_FOLDER="./tmp"
# Create a tmp folder for files modified during the deployment
TMP_MANIFESTS_FOLDER="$TMP_FOLDER/manifests"
mkdir -p $TMP_MANIFESTS_FOLDER
TMP_LOGS_FOLDER="$TMP_FOLDER/logs"
mkdir -p $TMP_LOGS_FOLDER
echo "Deleting and Creating a new namespace..."
kubectl delete namespace $TFS_K8S_NAMESPACE --ignore-not-found
kubectl create namespace $TFS_K8S_NAMESPACE
printf "\n"
echo "Create secret with CockroachDB data"
CRDB_SQL_PORT=$(kubectl --namespace ${CRDB_NAMESPACE} get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="sql")].port}')
kubectl create secret generic crdb-data --namespace ${TFS_K8S_NAMESPACE} --type='Opaque' \
--from-literal=CRDB_NAMESPACE=${CRDB_NAMESPACE} \
--from-literal=CRDB_SQL_PORT=${CRDB_SQL_PORT} \
--from-literal=CRDB_DATABASE=${CRDB_DATABASE} \
--from-literal=CRDB_USERNAME=${CRDB_USERNAME} \
--from-literal=CRDB_PASSWORD=${CRDB_PASSWORD} \
--from-literal=CRDB_SSLMODE=require
printf "\n"
echo "Create secret with NATS data"
NATS_CLIENT_PORT=$(kubectl --namespace ${NATS_NAMESPACE} get service nats -o 'jsonpath={.spec.ports[?(@.name=="client")].port}')
kubectl create secret generic nats-data --namespace ${TFS_K8S_NAMESPACE} --type='Opaque' \
--from-literal=NATS_NAMESPACE=${NATS_NAMESPACE} \
--from-literal=NATS_CLIENT_PORT=${NATS_CLIENT_PORT}
printf "\n"
echo "Create secret with QuestDB data"
QDB_HTTP_PORT=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="http")].port}')
QDB_ILP_PORT=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="ilp")].port}')
QDB_SQL_PORT=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="sql")].port}')
METRICSDB_HOSTNAME="questdb-public.${QDB_NAMESPACE}.svc.cluster.local"
kubectl create secret generic qdb-data --namespace ${TFS_K8S_NAMESPACE} --type='Opaque' \
--from-literal=QDB_NAMESPACE=${QDB_NAMESPACE} \
--from-literal=METRICSDB_HOSTNAME=${METRICSDB_HOSTNAME} \
--from-literal=METRICSDB_REST_PORT=${QDB_HTTP_PORT} \
--from-literal=METRICSDB_ILP_PORT=${QDB_ILP_PORT} \
--from-literal=METRICSDB_SQL_PORT=${QDB_SQL_PORT} \
--from-literal=METRICSDB_TABLE_MONITORING_KPIS=${QDB_TABLE_MONITORING_KPIS} \
--from-literal=METRICSDB_TABLE_SLICE_GROUPS=${QDB_TABLE_SLICE_GROUPS} \
--from-literal=METRICSDB_USERNAME=${QDB_USERNAME} \
--from-literal=METRICSDB_PASSWORD=${QDB_PASSWORD}
printf "\n"
echo "Deploying components and collecting environment variables..."
ENV_VARS_SCRIPT=tfs_runtime_env_vars.sh
echo "# Environment variables for TeraFlowSDN deployment" > $ENV_VARS_SCRIPT
PYTHONPATH=$(pwd)/src
echo "export PYTHONPATH=${PYTHONPATH}" >> $ENV_VARS_SCRIPT
for COMPONENT in $TFS_COMPONENTS; do
echo "Processing '$COMPONENT' component..."
if [ "$TFS_SKIP_BUILD" != "YES" ]; then
echo " Building Docker image..."
BUILD_LOG="$TMP_LOGS_FOLDER/build_${COMPONENT}.log"
if [ "$COMPONENT" == "automation" ] || [ "$COMPONENT" == "policy" ]; then
docker build -t "$COMPONENT:$TFS_IMAGE_TAG" -f ./src/"$COMPONENT"/Dockerfile ./src/"$COMPONENT"/ > "$BUILD_LOG"
elif [ "$COMPONENT" == "pathcomp" ]; then
BUILD_LOG="$TMP_LOGS_FOLDER/build_${COMPONENT}-frontend.log"
docker build -t "$COMPONENT-frontend:$TFS_IMAGE_TAG" -f ./src/"$COMPONENT"/frontend/Dockerfile . > "$BUILD_LOG"
BUILD_LOG="$TMP_LOGS_FOLDER/build_${COMPONENT}-backend.log"
docker build -t "$COMPONENT-backend:$TFS_IMAGE_TAG" -f ./src/"$COMPONENT"/backend/Dockerfile . > "$BUILD_LOG"
# next command is redundant, but helpful to keep cache updated between rebuilds
IMAGE_NAME="$COMPONENT-backend:$TFS_IMAGE_TAG-builder"
docker build -t "$IMAGE_NAME" --target builder -f ./src/"$COMPONENT"/backend/Dockerfile . >> "$BUILD_LOG"
elif [ "$COMPONENT" == "dlt" ]; then
BUILD_LOG="$TMP_LOGS_FOLDER/build_${COMPONENT}-connector.log"
docker build -t "$COMPONENT-connector:$TFS_IMAGE_TAG" -f ./src/"$COMPONENT"/connector/Dockerfile . > "$BUILD_LOG"
BUILD_LOG="$TMP_LOGS_FOLDER/build_${COMPONENT}-gateway.log"
docker build -t "$COMPONENT-gateway:$TFS_IMAGE_TAG" -f ./src/"$COMPONENT"/gateway/Dockerfile . > "$BUILD_LOG"
else
docker build -t "$COMPONENT:$TFS_IMAGE_TAG" -f ./src/"$COMPONENT"/Dockerfile . > "$BUILD_LOG"
fi
echo " Pushing Docker image to '$TFS_REGISTRY_IMAGES'..."
if [ "$COMPONENT" == "pathcomp" ]; then
IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT-frontend:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g')
TAG_LOG="$TMP_LOGS_FOLDER/tag_${COMPONENT}-frontend.log"
docker tag "$COMPONENT-frontend:$TFS_IMAGE_TAG" "$IMAGE_URL" > "$TAG_LOG"
PUSH_LOG="$TMP_LOGS_FOLDER/push_${COMPONENT}-frontend.log"
docker push "$IMAGE_URL" > "$PUSH_LOG"
IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT-backend:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g')
TAG_LOG="$TMP_LOGS_FOLDER/tag_${COMPONENT}-backend.log"
docker tag "$COMPONENT-backend:$TFS_IMAGE_TAG" "$IMAGE_URL" > "$TAG_LOG"
PUSH_LOG="$TMP_LOGS_FOLDER/push_${COMPONENT}-backend.log"
docker push "$IMAGE_URL" > "$PUSH_LOG"
elif [ "$COMPONENT" == "dlt" ]; then
IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT-connector:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g')
TAG_LOG="$TMP_LOGS_FOLDER/tag_${COMPONENT}-connector.log"
docker tag "$COMPONENT-connector:$TFS_IMAGE_TAG" "$IMAGE_URL" > "$TAG_LOG"
PUSH_LOG="$TMP_LOGS_FOLDER/push_${COMPONENT}-connector.log"
docker push "$IMAGE_URL" > "$PUSH_LOG"
IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT-gateway:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g')
TAG_LOG="$TMP_LOGS_FOLDER/tag_${COMPONENT}-gateway.log"
docker tag "$COMPONENT-gateway:$TFS_IMAGE_TAG" "$IMAGE_URL" > "$TAG_LOG"
PUSH_LOG="$TMP_LOGS_FOLDER/push_${COMPONENT}-gateway.log"
docker push "$IMAGE_URL" > "$PUSH_LOG"
else
IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g')
TAG_LOG="$TMP_LOGS_FOLDER/tag_${COMPONENT}.log"
docker tag "$COMPONENT:$TFS_IMAGE_TAG" "$IMAGE_URL" > "$TAG_LOG"
PUSH_LOG="$TMP_LOGS_FOLDER/push_${COMPONENT}.log"
docker push "$IMAGE_URL" > "$PUSH_LOG"
fi
else
for QUICK_COMPONENT in $TFS_QUICK_COMPONENTS; do
if [ "$COMPONENT" == "$QUICK_COMPONENT" ]; then
echo " Building Docker image..."
BUILD_LOG="$TMP_LOGS_FOLDER/build_${QUICK_COMPONENT}.log"
docker build -t "$QUICK_COMPONENT:$TFS_IMAGE_TAG" -f ./src/"$QUICK_COMPONENT"/Dockerfile . > "$BUILD_LOG"
echo " Pushing Docker image to '$TFS_REGISTRY_IMAGES'..."
IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$QUICK_COMPONENT:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g')
TAG_LOG="$TMP_LOGS_FOLDER/tag_${QUICK_COMPONENT}.log"
docker tag "$QUICK_COMPONENT:$TFS_IMAGE_TAG" "$IMAGE_URL" > "$TAG_LOG"
PUSH_LOG="$TMP_LOGS_FOLDER/push_${QUICK_COMPONENT}.log"
docker push "$IMAGE_URL" > "$PUSH_LOG"
fi
done
fi
echo " Adapting '$COMPONENT' manifest file..."
MANIFEST="$TMP_MANIFESTS_FOLDER/${COMPONENT}service.yaml"
cp ./manifests/"${COMPONENT}"service.yaml "$MANIFEST"
if [ "$COMPONENT" == "pathcomp" ]; then
IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT-frontend:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g')
VERSION=$(grep -i "${GITLAB_REPO_URL}/${COMPONENT}-frontend:" "$MANIFEST" | cut -d ":" -f4)
sed -E -i "s#image: $GITLAB_REPO_URL/$COMPONENT-frontend:${VERSION}#image: $IMAGE_URL#g" "$MANIFEST"
IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT-backend:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g')
VERSION=$(grep -i "${GITLAB_REPO_URL}/${COMPONENT}-backend:" "$MANIFEST" | cut -d ":" -f4)
sed -E -i "s#image: $GITLAB_REPO_URL/$COMPONENT-backend:${VERSION}#image: $IMAGE_URL#g" "$MANIFEST"
elif [ "$COMPONENT" == "dlt" ]; then
IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT-connector:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g')
VERSION=$(grep -i "${GITLAB_REPO_URL}/${COMPONENT}-connector:" "$MANIFEST" | cut -d ":" -f4)
sed -E -i "s#image: $GITLAB_REPO_URL/$COMPONENT-connector:${VERSION}#image: $IMAGE_URL#g" "$MANIFEST"
IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT-gateway:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g')
VERSION=$(grep -i "${GITLAB_REPO_URL}/${COMPONENT}-gateway:" "$MANIFEST" | cut -d ":" -f4)
sed -E -i "s#image: $GITLAB_REPO_URL/$COMPONENT-gateway:${VERSION}#image: $IMAGE_URL#g" "$MANIFEST"
else
IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g')
VERSION=$(grep -i "${GITLAB_REPO_URL}/${COMPONENT}:" "$MANIFEST" | cut -d ":" -f4)
sed -E -i "s#image: $GITLAB_REPO_URL/$COMPONENT:${VERSION}#image: $IMAGE_URL#g" "$MANIFEST"
fi
sed -E -i "s#imagePullPolicy: .*#imagePullPolicy: Always#g" "$MANIFEST"
# TODO: harmonize names of the monitoring component
echo " Deploying '$COMPONENT' component to Kubernetes..."
DEPLOY_LOG="$TMP_LOGS_FOLDER/deploy_${COMPONENT}.log"
kubectl --namespace $TFS_K8S_NAMESPACE apply -f "$MANIFEST" > "$DEPLOY_LOG"
COMPONENT_OBJNAME=$(echo "${COMPONENT}" | sed "s/\_/-/")
#kubectl --namespace $TFS_K8S_NAMESPACE scale deployment --replicas=0 ${COMPONENT_OBJNAME}service >> "$DEPLOY_LOG"
#kubectl --namespace $TFS_K8S_NAMESPACE scale deployment --replicas=1 ${COMPONENT_OBJNAME}service >> "$DEPLOY_LOG"
echo " Collecting env-vars for '$COMPONENT' component..."
SERVICE_DATA=$(kubectl get service ${COMPONENT_OBJNAME}service --namespace $TFS_K8S_NAMESPACE -o json)
if [ -z "${SERVICE_DATA}" ]; then continue; fi
# Env vars for service's host address
SERVICE_HOST=$(echo ${SERVICE_DATA} | jq -r '.spec.clusterIP')
if [ -z "${SERVICE_HOST}" ]; then continue; fi
ENVVAR_HOST=$(echo "${COMPONENT}service_SERVICE_HOST" | tr '[:lower:]' '[:upper:]')
echo "export ${ENVVAR_HOST}=${SERVICE_HOST}" >> $ENV_VARS_SCRIPT
# Env vars for service's 'grpc' port (if any)
SERVICE_PORT_GRPC=$(echo ${SERVICE_DATA} | jq -r '.spec.ports[] | select(.name=="grpc") | .port')
if [ -n "${SERVICE_PORT_GRPC}" ]; then
ENVVAR_PORT_GRPC=$(echo "${COMPONENT}service_SERVICE_PORT_GRPC" | tr '[:lower:]' '[:upper:]')
echo "export ${ENVVAR_PORT_GRPC}=${SERVICE_PORT_GRPC}" >> $ENV_VARS_SCRIPT
fi
# Env vars for service's 'http' port (if any)
SERVICE_PORT_HTTP=$(echo ${SERVICE_DATA} | jq -r '.spec.ports[] | select(.name=="http") | .port')
if [ -n "${SERVICE_PORT_HTTP}" ]; then
ENVVAR_PORT_HTTP=$(echo "${COMPONENT}service_SERVICE_PORT_HTTP" | tr '[:lower:]' '[:upper:]')
echo "export ${ENVVAR_PORT_HTTP}=${SERVICE_PORT_HTTP}" >> $ENV_VARS_SCRIPT
fi
printf "\n"
done
echo "Deploying extra manifests..."
for EXTRA_MANIFEST in $TFS_EXTRA_MANIFESTS; do
echo "Processing manifest '$EXTRA_MANIFEST'..."
if [[ "$EXTRA_MANIFEST" == *"servicemonitor"* ]]; then
kubectl apply -f $EXTRA_MANIFEST
else
kubectl --namespace $TFS_K8S_NAMESPACE apply -f $EXTRA_MANIFEST
fi
printf "\n"
done
printf "\n"
for COMPONENT in $TFS_COMPONENTS; do
echo "Waiting for '$COMPONENT' component..."
COMPONENT_OBJNAME=$(echo "${COMPONENT}" | sed "s/\_/-/")
kubectl wait --namespace $TFS_K8S_NAMESPACE \
--for='condition=available' --timeout=300s deployment/${COMPONENT_OBJNAME}service
printf "\n"
done
if [[ "$TFS_COMPONENTS" == *"webui"* ]] && [[ "$TFS_COMPONENTS" == *"monitoring"* ]]; then
echo "Configuring WebUI DataStores and Dashboards..."
sleep 5
# Exposed through the ingress controller "tfs-ingress"
GRAFANA_URL="127.0.0.1:80/grafana"
# Default Grafana credentials
GRAFANA_USERNAME="admin"
GRAFANA_PASSWORD="admin"
# Configure Grafana Admin Password
# Ref: https://grafana.com/docs/grafana/latest/http_api/user/#change-password
GRAFANA_URL_DEFAULT="http://${GRAFANA_USERNAME}:${GRAFANA_PASSWORD}@${GRAFANA_URL}"
echo ">> Updating Grafana 'admin' password..."
curl -X PUT -H "Content-Type: application/json" -d '{
"oldPassword": "'${GRAFANA_PASSWORD}'",
"newPassword": "'${TFS_GRAFANA_PASSWORD}'",
"confirmNew" : "'${TFS_GRAFANA_PASSWORD}'"
}' ${GRAFANA_URL_DEFAULT}/api/user/password
echo
echo
# Updated Grafana API URL
GRAFANA_URL_UPDATED="http://${GRAFANA_USERNAME}:${TFS_GRAFANA_PASSWORD}@${GRAFANA_URL}"
echo "export GRAFANA_URL_UPDATED=${GRAFANA_URL_UPDATED}" >> $ENV_VARS_SCRIPT
echo ">> Installing Scatter Plot plugin..."
curl -X POST -H "Content-Type: application/json" -H "Content-Length: 0" \
${GRAFANA_URL_UPDATED}/api/plugins/michaeldmoore-scatter-panel/install
echo
# Ref: https://grafana.com/docs/grafana/latest/http_api/data_source/
QDB_HOST_PORT="${METRICSDB_HOSTNAME}:${QDB_SQL_PORT}"
echo ">> Creating datasources..."
curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{
"access" : "proxy",
"type" : "postgres",
"name" : "questdb-mon-kpi",
"url" : "'${QDB_HOST_PORT}'",
"database" : "'${QDB_TABLE_MONITORING_KPIS}'",
"user" : "'${QDB_USERNAME}'",
"basicAuth": false,
"isDefault": true,
"jsonData" : {
"sslmode" : "disable",
"postgresVersion" : 1100,
"maxOpenConns" : 0,
"maxIdleConns" : 2,
"connMaxLifetime" : 14400,
"tlsAuth" : false,
"tlsAuthWithCACert" : false,
"timescaledb" : false,
"tlsConfigurationMethod": "file-path",
"tlsSkipVerify" : true
},
"secureJsonData": {"password": "'${QDB_PASSWORD}'"}
}' ${GRAFANA_URL_UPDATED}/api/datasources
echo
curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{
"access" : "proxy",
"type" : "postgres",
"name" : "questdb-slc-grp",
"url" : "'${QDB_HOST_PORT}'",
"database" : "'${QDB_TABLE_SLICE_GROUPS}'",
"user" : "'${QDB_USERNAME}'",
"basicAuth": false,
"isDefault": false,
"jsonData" : {
"sslmode" : "disable",
"postgresVersion" : 1100,
"maxOpenConns" : 0,
"maxIdleConns" : 2,
"connMaxLifetime" : 14400,
"tlsAuth" : false,
"tlsAuthWithCACert" : false,
"timescaledb" : false,
"tlsConfigurationMethod": "file-path",
"tlsSkipVerify" : true
},
"secureJsonData": {"password": "'${QDB_PASSWORD}'"}
}' ${GRAFANA_URL_UPDATED}/api/datasources
printf "\n\n"
echo ">> Creating dashboards..."
# Ref: https://grafana.com/docs/grafana/latest/http_api/dashboard/
curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_db_mon_kpis_psql.json' \
${GRAFANA_URL_UPDATED}/api/dashboards/db
echo
curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_db_slc_grps_psql.json' \
${GRAFANA_URL_UPDATED}/api/dashboards/db
printf "\n\n"
echo ">> Staring dashboards..."
DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-l3-monit"
DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
echo
DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-slice-grps"
DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
echo
printf "\n\n"
fi
......@@ -25,14 +25,14 @@ LOGGER = logging.getLogger(__name__)
def main():
LOGGER.info('Starting...')
parameters = Parameters(
num_requests = 10,
num_requests = 100,
request_types = [
RequestType.SERVICE_L2NM,
RequestType.SERVICE_L3NM,
#RequestType.SERVICE_MW,
#RequestType.SERVICE_TAPI,
#RequestType.SLICE_L2NM,
#RequestType.SLICE_L3NM,
RequestType.SLICE_L2NM,
RequestType.SLICE_L3NM,
],
device_regex=r'.+',
endpoint_regex=r'.+',
......
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# 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.
import re
from flask import flash, Flask
from flask_wtf import FlaskForm
from wtforms import StringField, SelectField, IntegerField, DecimalField
from wtforms.validators import InputRequired, Optional, NumberRange, ValidationError, StopValidation
#from common.proto.context_pb2 import DeviceOperationalStatusEnum
import ipaddress
def validate_ipv4_address(form, field): #Custom validator for ensuring a valid IPv4 address is submitted
# Check for a valid IPv4 address
# print(field.data)
try:
ipaddress.IPv4Address(field.data)
except ipaddress.AddressValueError:
raise ValidationError('Invalid IPv4 address format')
def validate_ipv6_address(form, field): #Custom validator for ensuring a valid IPv6 address is submitted
# Check for a valid IPv6 address
try:
ipaddress.IPv6Address(field.data)
except ipaddress.AddressValueError:
raise ValidationError('Invalid IPv6 address format')
def validate_mac_address(form, field): #Custom validator for ensuring a valid MAC address is submitted
# Check for a valid MAC [L2] address
if not re.match(r'^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$', field.data):
raise ValidationError('Invalid MAC address format')
def validate_route_distinguisher(form,field): #Custom validator for the input of Route Distinguisher value
pattern = r'^([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]):([0-9]|[1-9][0-9]{1,8}|[1-3][0-9]{9}|4[01][0-9]{8}|42[0-8][0-9]{7}|429[0-3][0-9]{6}|4294[0-8][0-9]{5}|42949[0-5][0-9]{4}|429496[0-6][0-9]{3}|4294967[01][0-9]{2}|42949672[0-8][0-9]|429496729[0-5])$'
if not re.match(pattern, field.data):
raise ValidationError('Invalid Route Distinguisher')
def validate_uint32(form, field): #Custom validator for ensuring uint32 integers
if not 0 <= field.data <= 2**32-1:
raise ValidationError('Value must be a positive integer within the range of uint32')
def validate_NI_as(form, field): #Custom validator that checks if NI_protocol_name is BGP and NI_as is not provided
if form.NI_protocol.data == 'BGP' and field.data == None:
raise StopValidation('AS field is required if the BGP protocol is selected.')
def validator_ADVA(form, field):
if field.name == 'Device_1_NI_VC_ID' and form.Device_1_IF_vendor.data == 'ADVA' and form.Device_1_NI_VC_ID.data != form.Device_1_IF_vlan_id.data:
raise StopValidation('For the ADVA vendor, it is mandatory that the VC_ID is the same as the Vlan_ID.')
if field.name == 'Device_2_NI_VC_ID' and form.Device_2_IF_vendor.data == 'ADVA' and form.Device_2_NI_VC_ID.data != form.Device_2_IF_vlan_id.data:
raise StopValidation('For the ADVA vendor, it is mandatory that the VC_ID is the same as the Vlan_ID.')
class CustomInputRequired(): #Custom validator that ensures that the required data is provided
def __init__(self, message=None): #Define a constructor that takes an optional message parameter
self.message = message or "This field is required." #If message is provided, use it. Otherwise, set a default message.
def __call__(self, form, field): #Define a __call__ method that takes in the form and field to be validated
if field.data is None or field.data == '': #Check if the field data is empty or None
raise StopValidation(self.message) #If the data is empty or None, raise a StopValidation exception with the provided message
class AddServiceForm_1(FlaskForm): #Form-1 - Formulary Fields -> Select the type of new service to add
service_type = SelectField('Type of service', choices=[('', 'Select a type of service to add'), ('ACL_L2', 'ACL_L2'), ('ACL_IPV4', 'ACL_IPV4'), ('ACL_IPV6', 'ACL_IPV6'), ('L2VPN', 'L2VPN'), ('L3VPN', 'L3VPN')], validators=[InputRequired()])
class AddServiceForm_ACL_L2(FlaskForm): #ACL_L2 - Formulary Fields
#GENERIC SERVICE PARAMETERS (COMMON & MANDATORY)
service_name = StringField('Service Name', validators=[CustomInputRequired()]) #MANDATORY PARAMETER
service_type = SelectField('Service Type', choices=[(2, '2 (L2NM)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - FIXED VALUE -> L3NM
service_device_1 = SelectField('Device_1', choices=[('', 'Select a device (Mandatory)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-1
service_device_2 = SelectField('Device_2', choices=[('', 'Select a device (Mandatory)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-2
service_endpoint_1 = StringField('Device_1 Endpoint', validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-1
service_endpoint_2 = StringField('Device_2 Endpoint', validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-2
#GENERIC SERVICE CONSTRAINT PARAMETERS (ALL OPTIONAL)
service_capacity = DecimalField('Service Capacity', places=2, default=10.00, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_latency = DecimalField('Service Latency', places=2, default=15.20, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_availability= DecimalField('Service Availability', places=2, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_isolation = SelectField('Service Isolation', choices=[('', 'Select (Optional)'), ('NO_ISOLATION', 'NO_ISOLATION'), ('PHYSICAL_ISOLATION', 'PHYSICAL_ISOLATION'), ('LOGICAL_ISOLATION', 'LOGICAL_ISOLATION'), ('PROCESS_ISOLATION', 'PROCESS_ISOLATION'), ('PHYSICAL_MEMORY_ISOLATION', 'PHYSICAL_MEMORY_ISOLATION'), ('PHYSICAL_NETWORK_ISOLATION', 'PHYSICAL_NETWORK_ISOLATION'), ('VIRTUAL_RESOURCE_ISOLATION', 'VIRTUAL_RESOURCE_ISOLATION'), ('NETWORK_FUNCTIONS_ISOLATION', 'NETWORK_FUNCTIONS_ISOLATION'), ('SERVICE_ISOLATION', 'SERVICE_ISOLATION')], validators=[Optional()])
#MANDATORY_PARAMETERS
name = StringField('ACL Name', validators=[CustomInputRequired("The name of the ACL is a mandatory parameter")]) #MANDATORY PARAMETER
type = SelectField('ACL Type', choices=[('ACL_L2', 'ACL_L2')], validators=[CustomInputRequired("The type of the ACL is a mandatory parameter")]) #MANDATORY PARAMETER
sequence_id = IntegerField('ACL Sequence ID', validators=[CustomInputRequired("The name of the Sequence ID of the ACL is a mandatory parameter"), validate_uint32]) #MANDATORY PARAMETER
forwarding_action = SelectField('ACL Fowarding Action', choices=[('', 'Select an action (Mandatory)'), ('ACCEPT', 'Accept'), ('DROP','Drop'),('REJECT','Reject')], validators=[CustomInputRequired("The Forwarding Action of the ACL is a mandatory parameter")])
log_action = SelectField('ACL Log Action', choices=[(None, 'Select a log action (Optional)'), ('LOG_SYSLOG', 'Syslog'), ('LOG_NONE','None')], validators=[Optional()])
#PARAMETERS FOR Associating ACL to IF
interface = StringField('Interface Name', validators=[CustomInputRequired("The name of the Interface is a mandatory parameter")]) #MANDATORY PARAMETER
subinterface = StringField('Subinterface Index', validators=[Optional()])
traffic_flow = SelectField('ACL Traffic Flow Direction', choices=[('', 'Select a direction (Mandatory)'), ('Ingress', 'Ingress'), ('Egress','Egress')], validators=[CustomInputRequired("The direction of the traffic flow is a mandatory parameter")]) #MANDATORY PARAMETER
#SPECIFIC PARAMETERS - Creating ACL Entry [ACL_L2]
source_mac = StringField('Source MAC Address', validators=[Optional(), validate_mac_address])
destination_mac = StringField('Destination MAC Address', validators=[Optional(), validate_mac_address])
class AddServiceForm_ACL_IPV4(FlaskForm): #ACL_IPV4 - Formulary Fields
#GENERIC SERVICE PARAMETERS (COMMON & MANDATORY)
service_name = StringField('Service Name', validators=[CustomInputRequired()]) #MANDATORY PARAMETER
service_type = SelectField('Service Type', choices=[(1, '1 (L3NM)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - FIXED VALUE -> L3NM
service_device_1 = SelectField('Device_1', choices=[('', 'Select a device (Mandatory)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-1
service_device_2 = SelectField('Device_2', choices=[('', 'Select a device (Mandatory)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-2
service_endpoint_1 = StringField('Device_1 Endpoint', validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-1
service_endpoint_2 = StringField('Device_2 Endpoint', validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-2
#GENERIC SERVICE CONSTRAINT PARAMETERS (ALL OPTIONAL)
service_capacity = DecimalField('Service Capacity', places=2, default=10.00, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_latency = DecimalField('Service Latency', places=2, default=15.20, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_availability= DecimalField('Service Availability', places=2, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_isolation = SelectField('Service Isolation', choices=[('', 'Select (Optional)'), ('NO_ISOLATION', 'NO_ISOLATION'), ('PHYSICAL_ISOLATION', 'PHYSICAL_ISOLATION'), ('LOGICAL_ISOLATION', 'LOGICAL_ISOLATION'), ('PROCESS_ISOLATION', 'PROCESS_ISOLATION'), ('PHYSICAL_MEMORY_ISOLATION', 'PHYSICAL_MEMORY_ISOLATION'), ('PHYSICAL_NETWORK_ISOLATION', 'PHYSICAL_NETWORK_ISOLATION'), ('VIRTUAL_RESOURCE_ISOLATION', 'VIRTUAL_RESOURCE_ISOLATION'), ('NETWORK_FUNCTIONS_ISOLATION', 'NETWORK_FUNCTIONS_ISOLATION'), ('SERVICE_ISOLATION', 'SERVICE_ISOLATION')], validators=[Optional()])
#MANDATORY_PARAMETERS
name = StringField('ACL Name', validators=[CustomInputRequired("The name of the ACL is a mandatory parameter")]) #MANDATORY PARAMETER
type = SelectField('ACL Type', choices=[('ACL_IPV4', 'ACL_IPV4')], validators=[CustomInputRequired("The type of the ACL is a mandatory parameter")]) #MANDATORY PARAMETER
sequence_id = IntegerField('ACL Sequence ID', validators=[InputRequired(), NumberRange(min=1, message="Sequence ID must be greater than 0")]) #MANDATORY PARAMETER #MANDATORY PARAMETER
forwarding_action = SelectField('ACL Fowarding Action', choices=[(None, 'Select an action (Mandatory)'), ('ACCEPT', 'Accept'), ('DROP','Drop'),('REJECT','Reject')], validators=[InputRequired()])
log_action = SelectField('ACL Log Action', choices=[(None, 'Select a log action (Optional)'), ('LOG_SYSLOG', 'Syslog'), ('LOG_NONE','None')], validators=[Optional()])
#PARAMETERS FOR Associating ACL to IF
interface = StringField('Interface Name', validators=[InputRequired()]) #MANDATORY PARAMETER
subinterface = StringField('Subinterface Index', validators=[Optional()])
traffic_flow = SelectField('ACL Traffic Flow Direction', choices=[('', 'Select a direction (Mandatory)'), ('Ingress', 'Ingress'), ('Egress','Egress')], validators=[InputRequired()]) #MANDATORY PARAMETER
#OPTIONAL_PARAMETERS - Creating ACL Entry [ACL_IPV4]
source_address = StringField('Source Address', validators=[Optional(), validate_ipv4_address])
destination_address = StringField('Destination Address', validators=[Optional(), validate_ipv4_address])
protocol = IntegerField('Protocol', validators=[Optional(),NumberRange(min=1, max=255, message="Protocol number is between 1 and 255 as defined by IANA")]) #Protocols are defined from 1 - 255 as defined in IANA (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml)
hop_limit = IntegerField('Hop Limit', validators=[Optional(),NumberRange(min=1, max=255, message="The Hop limit value has to be between 0 and 255")]) #Max. value of Hop Limit = 255
dscp = IntegerField('DSCP', validators=[Optional(),NumberRange(min=1, max=255, message="The DSCP value has to be between 0 and 63")]) #Max. value of DSCP = 63
source_port = IntegerField('Source Port', validators=[Optional(),NumberRange(min=0, max=65535, message="The Port value has to be between 0 and 655535")]) #Range of existing ports in a PC
destination_port = IntegerField('Destination Port', validators=[Optional(),NumberRange(min=0, max=65535, message="The Port value has to be between 0 and 655535")]) #Range of existing ports in a PC
tcp_flags = SelectField('TCP Flags', choices=[(None, 'Select a TCP Flag (Optional)'),('TCP_SYN', 'TCP_SYN'),('TCP_ACK', 'TCP_ACK'),('TCP_RST', 'TCP_RST'),('TCP_FIN', 'TCP_FIN'),('TCP_PSH', 'TCP_PSH'),('TCP_URG', 'TCP_URG') ,('TCP_ECE', 'TCP_ECE'),('TCP_CWR', 'TCP_CWR')], validators=[Optional()])
class AddServiceForm_ACL_IPV6(FlaskForm): #ACL_IPV6 - Formulary Fields
#GENERIC SERVICE PARAMETERS (COMMON & MANDATORY)
service_name = StringField('Service Name', validators=[CustomInputRequired()]) #MANDATORY PARAMETER
service_type = SelectField('Service Type', choices=[(1, '1 (L3NM)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - FIXED VALUE -> L2NM
service_device_1 = SelectField('Device_1', choices=[('', 'Select a device (Mandatory)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-1
service_device_2 = SelectField('Device_2', choices=[('', 'Select a device (Mandatory)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-2
service_endpoint_1 = StringField('Device_1 Endpoint', validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-1
service_endpoint_2 = StringField('Device_2 Endpoint', validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-2
#GENERIC SERVICE CONSTRAINT PARAMETERS (ALL OPTIONAL)
service_capacity = DecimalField('Service Capacity', places=2, default=10.00, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_latency = DecimalField('Service Latency', places=2, default=15.20, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_availability= DecimalField('Service Availability', places=2, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_isolation = SelectField('Service Isolation', choices=[('', 'Select (Optional)'), ('NO_ISOLATION', 'NO_ISOLATION'), ('PHYSICAL_ISOLATION', 'PHYSICAL_ISOLATION'), ('LOGICAL_ISOLATION', 'LOGICAL_ISOLATION'), ('PROCESS_ISOLATION', 'PROCESS_ISOLATION'), ('PHYSICAL_MEMORY_ISOLATION', 'PHYSICAL_MEMORY_ISOLATION'), ('PHYSICAL_NETWORK_ISOLATION', 'PHYSICAL_NETWORK_ISOLATION'), ('VIRTUAL_RESOURCE_ISOLATION', 'VIRTUAL_RESOURCE_ISOLATION'), ('NETWORK_FUNCTIONS_ISOLATION', 'NETWORK_FUNCTIONS_ISOLATION'), ('SERVICE_ISOLATION', 'SERVICE_ISOLATION')], validators=[Optional()])
#MANDATORY_PARAMETERS
name = StringField('ACL Name', validators=[InputRequired()]) #MANDATORY PARAMETER
type = SelectField('ACL Type', choices=[('ACL_IPV6', 'ACL_IPV6')], validators=[InputRequired()]) #MANDATORY PARAMETER
sequence_id = IntegerField('ACL Sequence ID', validators=[InputRequired(), NumberRange(min=1, message="Sequence ID must be greater than 0")]) #MANDATORY PARAMETER]) #MANDATORY PARAMETER
forwarding_action = SelectField('ACL Fowarding Action', choices=[(None, 'Select an action (Mandatory)'), ('ACCEPT', 'Accept'), ('DROP','Drop'),('REJECT','Reject')], validators=[InputRequired()])
log_action = SelectField('ACL Log Action', choices=[(None, 'Select a log action (Optional)'), ('LOG_SYSLOG', 'Syslog'), ('LOG_NONE','None')], validators=[Optional()])
#PARAMETERS FOR Associating ACL to IF
interface = StringField('Interface Name', validators=[InputRequired()]) #MANDATORY PARAMETER
subinterface = StringField('Subinterface Index', validators=[Optional()])
traffic_flow = SelectField('ACL Traffic Flow Direction', choices=[('', 'Select a direction (Mandatory)'), ('Ingress', 'Ingress'), ('Egress','Egress')], validators=[InputRequired()]) #MANDATORY PARAMETER
#SPECIFIC PARAMETERS - Creating ACL Entry [ACL_IPV6]
source_address = StringField('Source Address', validators=[Optional(), validate_ipv6_address])
destination_address = StringField('Destination Address', validators=[Optional(), validate_ipv6_address])
protocol = IntegerField('Protocol', validators=[Optional(),NumberRange(min=1, max=255, message="Protocol number is between 1 and 255 as defined by IANA")]) #Protocols are defined from 1 - 255 as defined in IANA (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml)
hop_limit = IntegerField('Hop Limit', validators=[Optional(),NumberRange(min=1, max=255, message="The Hop limit value has to be between 0 and 255")]) #Max. value of Hop Limit = 255
dscp = IntegerField('DSCP', validators=[Optional(),NumberRange(min=1, max=255, message="The DSCP value has to be between 0 and 63")]) #Max. value of DSCP = 63
class AddServiceForm_L2VPN(FlaskForm): #L2VPN - Formulary Fields
#GENERIC SERVICE PARAMETERS (COMMON & MANDATORY)
service_name = StringField('Service Name', validators=[CustomInputRequired()]) #MANDATORY PARAMETER
service_type = SelectField('Service Type', choices=[(2, '2 (L2NM)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - FIXED VALUE -> L2NM
service_device_1 = SelectField('Device_1', choices=[('', 'Select a device (Mandatory)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-1
service_device_2 = SelectField('Device_2', choices=[('', 'Select a device (Mandatory)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-2
service_endpoint_1 = StringField('Device_1 Endpoint', validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-1
service_endpoint_2 = StringField('Device_2 Endpoint', validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-2
#Device_1_IF_vendor = SelectField ('Device_1 Vendor', choices=[('', 'Select a vendor (Mandatory)'),('ADVA', 'ADVA'), ('CISCO','CISCO'), ('Huawei', 'Huawei'),('Juniper', 'Juniper'),('Nokia', 'Nokia')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-1
#Device_2_IF_vendor = SelectField ('Device_2 Vendor', choices=[('', 'Select a vendor (Mandatory)'),('ADVA', 'ADVA'), ('CISCO','CISCO'), ('Huawei', 'Huawei'),('Juniper', 'Juniper'),('Nokia', 'Nokia')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-2
#Device_1_Template = SelectField ('Device_1 Template', choices=[('', 'Select a type of template (Mandatory)'),('Jinja', 'Jinja'), ('Pyangbind','Pyangbind')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-1
#Device_2_Template = SelectField ('Device_2 Template', choices=[('', 'Select a type of template (Mandatory)'),('Jinja', 'Jinja'), ('Pyangbind','Pyangbind')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-2
#GENERIC SERVICE CONSTRAINT PARAMETERS (ALL OPTIONAL)
service_capacity = DecimalField('Service Capacity', places=2, default=10.00, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_latency = DecimalField('Service Latency', places=2, default=15.20, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_availability= DecimalField('Service Availability', places=2, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_isolation = SelectField('Service Isolation', choices=[('', 'Select (Optional)'), ('NO_ISOLATION', 'NO_ISOLATION'), ('PHYSICAL_ISOLATION', 'PHYSICAL_ISOLATION'), ('LOGICAL_ISOLATION', 'LOGICAL_ISOLATION'), ('PROCESS_ISOLATION', 'PROCESS_ISOLATION'), ('PHYSICAL_MEMORY_ISOLATION', 'PHYSICAL_MEMORY_ISOLATION'), ('PHYSICAL_NETWORK_ISOLATION', 'PHYSICAL_NETWORK_ISOLATION'), ('VIRTUAL_RESOURCE_ISOLATION', 'VIRTUAL_RESOURCE_ISOLATION'), ('NETWORK_FUNCTIONS_ISOLATION', 'NETWORK_FUNCTIONS_ISOLATION'), ('SERVICE_ISOLATION', 'SERVICE_ISOLATION')], validators=[Optional()])
#NI parameters
#Common for the service
NI_name = StringField('NI Name', validators=[CustomInputRequired()]) #MANDATORY PARAMETER
#NI_type = SelectField('NI Type', choices=[('L2VSI', 'L2VSI')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - FIXED VALUE -> L2VSI
NI_mtu = IntegerField('NI MTU', default=1500, validators=[CustomInputRequired(), NumberRange(min=0, message="MTU value can't be negative")]) #MANDATORY PARAMETER - FIXED VALUE -> 1500
NI_description = StringField('NI Description', validators=[Optional()]) #OPTIONAL PARAMETER
#Device_1 specific
#Device_1_NI_VC_ID = IntegerField('Device_1 NI VC_ID', validators=[CustomInputRequired(), NumberRange(min=0, message="VC can't be negative"), validator_ADVA]) #MANDATORY PARAMETER
Device_1_NI_remote_system = StringField('Device_1 NI Remote System', validators=[CustomInputRequired(),validate_ipv4_address]) #MANDATORY PARAMETER
Device_1_NI_VC_ID = IntegerField('Device_1 NI VC ID', validators=[CustomInputRequired(), NumberRange(min=0, message="VC can't be negative")]) #MANDATORY PARAMETER
Device_1_NI_connection_point = StringField('Device_1 NI Conection Point', validators=[CustomInputRequired()]) #MANDATORY PARAMETER
#Device_2 specific
#Device_2_NI_VC_ID = IntegerField('Device_2 NI VC_ID', validators=[CustomInputRequired(), NumberRange(min=0, message="VC can't be negative"), validator_ADVA]) #MANDATORY PARAMETER
Device_2_NI_remote_system = StringField ('Device_2 NI Remote System', validators=[CustomInputRequired(),validate_ipv4_address]) #MANDATORY PARAMETER
Device_2_NI_VC_ID = IntegerField('Device_2 NI VC ID', validators=[CustomInputRequired(), NumberRange(min=0, message="VC can't be negative")]) #MANDATORY PARAMETER
Device_2_NI_connection_point = StringField ('Device_2 NI Conection Point', validators=[CustomInputRequired()]) #MANDATORY PARAMETER
#Interface parameters (DEVICE SPECIFIC)
#Device-1
#Device_1_IF_name = StringField ('Device_1 Interface Name', validators=[CustomInputRequired()]) #MANDATORY PARAMETER
#Device_1_IF_type = StringField ('Device_1 Interface Type', default="l2vlan", validators=[CustomInputRequired()]) #MANDATORY PARAMETER - FIXED VALUE -> l2vlan?
Device_1_IF_index = IntegerField('Device_1 SubIF Index', validators=[CustomInputRequired(), NumberRange(min=0, message="SubIf index can't be negative")]) #MANDATORY PARAMETER
Device_1_IF_vlan_id = IntegerField('Device_1 VLAN ID', validators=[CustomInputRequired(), NumberRange(min=0, message="VlanID can't be negative")]) #MANDATORY PARAMETER
Device_1_IF_mtu = IntegerField('Device_1 Interface MTU', validators=[Optional(), NumberRange(min=0, message="MTU value can't be negative")]) #OPTIONAL PARAMETER - FIXED VALUE -> 3000?
Device_1_IF_description = StringField ('Device_1 SubIF Description', validators=[Optional()]) #OPTIONAL PARAMETER
#Device-2
#Device_2_IF_name = StringField ('Device_2 Interface Name', validators=[CustomInputRequired()]) #MANDATORY PARAMETER
#Device_2_IF_type = StringField ('Device_2 Interface Type', default="l2vlan", validators=[CustomInputRequired()]) #MANDATORY PARAMETER - FIXED VALUE -> l2vlan?
Device_2_IF_index = IntegerField('Device_2 SubIF Index', validators=[CustomInputRequired(), NumberRange(min=0, message="SubIf index can't be negative")]) #MANDATORY PARAMETER
Device_2_IF_vlan_id = IntegerField('Device_2 VLAN ID', validators=[CustomInputRequired(), NumberRange(min=0, message="VlanID can't be negative")]) #MANDATORY PARAMETER
Device_2_IF_mtu = IntegerField('Device_2 Interface MTU', validators=[Optional(), NumberRange(min=0, message="MTU value can't be negative")]) #OPTIONAL PARAMETER - FIXED VALUE -> 3000?
Device_2_IF_description = StringField ('Device_2 SubIF Description', validators=[Optional()]) #OPTIONAL PARAMETER
class AddServiceForm_L3VPN(FlaskForm): #L3VPN - Formulary Fields
#GENERIC SERVICE PARAMETERS (COMMON & MANDATORY)
service_name = StringField('Service Name', validators=[CustomInputRequired()]) #MANDATORY PARAMETER
service_type = SelectField('Service Type', choices=[(1, '1 (L3NM)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - FIXED VALUE -> L3NM
service_device_1 = SelectField('Device_1', choices=[('', 'Select a device (Mandatory)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-1
service_device_2 = SelectField('Device_2', choices=[('', 'Select a device (Mandatory)')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-2
service_endpoint_1 = StringField('Device_1 Endpoint', validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-1
service_endpoint_2 = StringField('Device_2 Endpoint', validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-2
#Device_1_IF_vendor = SelectField ('Device_1 Vendor', choices=[('', 'Select a vendor (Mandatory)'),('ADVA', 'ADVA'), ('CISCO','CISCO'), ('Huawei', 'Huawei'),('Juniper', 'Juniper'),('Nokia', 'Nokia')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER
#Device_2_IF_vendor = SelectField ('Device_2 Vendor', choices=[('', 'Select a vendor (Mandatory)'),('ADVA', 'ADVA'), ('CISCO','CISCO'), ('Huawei', 'Huawei'),('Juniper', 'Juniper'),('Nokia', 'Nokia')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER
#Device_1_Template = SelectField ('Device_1 Template', choices=[('', 'Select a type of template (Mandatory)'),('Jinja', 'Jinja'), ('Pyangbind','Pyangbind')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-1
#Device_2_Template = SelectField ('Device_2 Template', choices=[('', 'Select a type of template (Mandatory)'),('Jinja', 'Jinja'), ('Pyangbind','Pyangbind')], validators=[CustomInputRequired()]) #MANDATORY PARAMETER - DEVICE-2
#GENERIC SERVICE CONSTRAINT PARAMETERS (ALL OPTIONAL)
service_capacity = DecimalField('Service Capacity', places=2, default=10.00, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_latency = DecimalField('Service Latency', places=2, default=15.20, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_availability= DecimalField('Service Availability', places=2, validators=[Optional(), NumberRange(min=0)]) #OPTIONAL PARAMETER
service_isolation = SelectField('Service Isolation', choices=[('', 'Select (Optional)'), ('NO_ISOLATION', 'NO_ISOLATION'), ('PHYSICAL_ISOLATION', 'PHYSICAL_ISOLATION'), ('LOGICAL_ISOLATION', 'LOGICAL_ISOLATION'), ('PROCESS_ISOLATION', 'PROCESS_ISOLATION'), ('PHYSICAL_MEMORY_ISOLATION', 'PHYSICAL_MEMORY_ISOLATION'), ('PHYSICAL_NETWORK_ISOLATION', 'PHYSICAL_NETWORK_ISOLATION'), ('VIRTUAL_RESOURCE_ISOLATION', 'VIRTUAL_RESOURCE_ISOLATION'), ('NETWORK_FUNCTIONS_ISOLATION', 'NETWORK_FUNCTIONS_ISOLATION'), ('SERVICE_ISOLATION', 'SERVICE_ISOLATION')], validators=[Optional()])
## Network Instance (NI) PARAMS
#Create a NI
NI_name = StringField('Name', validators=[InputRequired()]) #MANDATORY PARAMETER
#NI_type = SelectField('Type', choices=[('L3VRF', 'L3VRF')], validators=[InputRequired()]) #MANDATORY PARAMETER - FIXED VALUE -> L3VRF
NI_route_distinguisher = StringField('Route Distinguisher', validators=[InputRequired(),validate_route_distinguisher]) #MANDATORY PARAMETER
NI_router_id = StringField('Router ID', validators=[Optional(), validate_ipv4_address]) #OPTIONAL PARAMETER
NI_description = StringField('Description', validators=[Optional()]) #OPTIONAL PARAMETER
#Add a protocol to NI
NI_protocol = SelectField('Protocol', choices=[('', 'Select a type (Mandatory)'),('STATIC', 'STATIC'),('DIRECTLY_CONNECTED', 'DIRECTLY_CONNECTED'),('BGP', 'BGP')], validators=[InputRequired()])
NI_as = IntegerField('AS', default=None, validators=[validate_NI_as, Optional(), validate_uint32])
#Create Connections Table
#NI_src_protocol = SelectField('Source Protocol', choices=[('', 'Select a type'),('STATIC', 'STATIC'),('DIRECTLY_CONNECTED', 'DIRECTLY_CONNECTED'),('BGP', 'BGP')], validators=[InputRequired()])
#NI_dst_protocol = SelectField('Destination Protocol', choices=[('', 'Select a type'),('STATIC', 'STATIC'),('DIRECTLY_CONNECTED', 'DIRECTLY_CONNECTED'),('BGP', 'BGP')], validators=[InputRequired()])
NI_address_family = SelectField('Protocol Address Family', choices=[('', 'Select a type (Mandatory)'),('IPV4', 'IPV4'),('IPV6', 'IPV6')], validators=[InputRequired()])
NI_default_import_policy = SelectField('Default Network Instance Import Policy', choices=[('', 'Select a policy (Mandatory)'),('ACCEPT_ROUTE', 'ACCEPT_ROUTE'),('REJECT_ROUTE', 'REJECT_ROUTE')], validators=[Optional()])
#Associate RP to NI
NI_import_policy = StringField('Name of the Network Instance Import Policy', validators=[Optional()]) #OPTIONAL PARAMETER
NI_export_policy = StringField('Name of the Network Instance Export Policy', validators=[Optional()]) #OPTIONAL PARAMETER
## Interface (IF) PARAMS
#Device-1
#Device_1_IF_name = StringField ('Device_1 Interface Name', validators=[CustomInputRequired()]) #MANDATORY PARAMETER
#Device_1_IF_type = StringField ('Device_1 Interface Type', default="l3ipvlan", validators=[CustomInputRequired()]) #MANDATORY PARAMETER - FIXED VALUE -> l3ipvlan?
Device_1_IF_index = IntegerField('Device_1 SubIF Index', validators=[CustomInputRequired(), NumberRange(min=0, message="SubIf index can't be negative")]) #MANDATORY PARAMETER
Device_1_IF_vlan_id = IntegerField('Device_1 VLAN ID', validators=[CustomInputRequired(), NumberRange(min=0, message="VlanID can't be negative")]) #MANDATORY PARAMETER
Device_1_IF_mtu = IntegerField('Device_1 Interface MTU', validators=[Optional(), NumberRange(min=0, message="MTU value can't be negative")]) #OPTIONAL PARAMETER - FIXED VALUE -> 3000?
Device_1_IF_address_ip = StringField('Device_1 IP Address', validators=[CustomInputRequired(), validate_ipv4_address]) #MANDATORY PARAMETER
Device_1_IF_address_prefix = IntegerField('Device_1 IP Prefix length', validators=[CustomInputRequired(), validate_uint32]) #MANDATORY PARAMETER
Device_1_IF_description = StringField ('Device_1 SubIF Description', validators=[Optional()]) #OPTIONAL PARAMETER
#Device-2
#Device_2_IF_name = StringField ('Device_2 Interface Name', validators=[CustomInputRequired()]) #MANDATORY PARAMETER
#Device_2_IF_type = StringField ('Device_1 Interface Type', default="l3ipvlan", validators=[CustomInputRequired()]) #MANDATORY PARAMETER - FIXED VALUE -> l3ipvlan?
Device_2_IF_index = IntegerField('Device_2 SubIF Index', validators=[CustomInputRequired(), NumberRange(min=0, message="SubIf index can't be negative")]) #MANDATORY PARAMETER
Device_2_IF_vlan_id = IntegerField('Device_2 VLAN ID', validators=[CustomInputRequired(), NumberRange(min=0, message="VlanID can't be negative")]) #MANDATORY PARAMETER
Device_2_IF_mtu = IntegerField('Device_2 Interface MTU', validators=[Optional(), NumberRange(min=0, message="MTU value can't be negative")]) #MANDATORY PARAMETER - FIXED VALUE -> 3000?
Device_2_IF_address_ip = StringField('Device_2 IP Address', validators=[CustomInputRequired(), validate_ipv4_address]) #MANDATORY PARAMETER
Device_2_IF_address_prefix = IntegerField('Device_2 IP Prefix length', validators=[CustomInputRequired(), validate_uint32]) #MANDATORY PARAMETER
Device_2_IF_description = StringField ('Device_2 SubIF Description', validators=[Optional()]) #OPTIONAL PARAMETER
## Routing Policy (RP) parameters
#RP_policy_name = StringField('Policy Name', validators=[InputRequired()]) #MANDATORY PARAMETER
#RP_statement_name = StringField('Statement Name', validators=[InputRequired()]) #MANDATORY PARAMETER
#RP_policy_result = SelectField('Policy Result', choices=[(None, 'Not Defined'), ('ACCEPT_ROUTE', 'ACCEPT_ROUTE'),('REJECT_ROUTE', 'REJECT_ROUTE')], validators=[Optional()])
#RP_ext_community_set_name = StringField('Ext Community Set Name', validators=[InputRequired()]) #MANDATORY PARAMETER
#RP_ext_community_member = StringField('Ext Community Member', validators=[InputRequired()]) #MANDATORY PARAMETER
......@@ -12,52 +12,30 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import base64, json, logging #, re
from contextlib import contextmanager
import json
import grpc
from collections import defaultdict
from flask import current_app, redirect, render_template, Blueprint, flash, session, url_for, request
from common.proto.context_pb2 import (
ContextId, IsolationLevelEnum, Service, ServiceId, ServiceTypeEnum, ServiceStatusEnum, Connection, Empty, DeviceDriverEnum,
IsolationLevelEnum, Service, ServiceId, ServiceTypeEnum, ServiceStatusEnum, Connection, Empty, DeviceDriverEnum,
ConfigActionEnum, Device, DeviceList)
from common.tools.context_queries.Context import get_context
from common.tools.context_queries.Topology import get_topology
from common.tools.context_queries.EndPoint import get_endpoint_names
from wtforms.validators import ValidationError
from context.client.ContextClient import ContextClient
from service.client.ServiceClient import ServiceClient
from device.client.DeviceClient import DeviceClient
from common.tools.object_factory.Service import (
json_service_l2nm_planned, json_service_l3nm_planned)
from common.tools.object_factory.Constraint import (
json_constraint_sla_availability, json_constraint_sla_capacity, json_constraint_sla_isolation,
json_constraint_sla_latency)
from common.tools.descriptor.Loader import DescriptorLoader, compose_notifications
from common.tools.object_factory.ConfigRule import json_config_rule_set
from common.tools.object_factory.Device import json_device_id
from common.tools.object_factory.EndPoint import json_endpoint_id
#from src.common.tools.grpc.Tools import grpc_message_to_json_string
from webui.service.service.forms import AddServiceForm_1, AddServiceForm_ACL_L2, AddServiceForm_ACL_IPV4, AddServiceForm_ACL_IPV6, AddServiceForm_L2VPN, AddServiceForm_L3VPN
from common.tools.context_queries.Service import get_service_by_uuid
from common.tools.object_factory.ConfigRule import json_config_rule_set
from common.tools.object_factory.Context import json_context_id
from common.tools.object_factory.Topology import json_topology_id
from context.client.ContextClient import ContextClient
from service.client.ServiceClient import ServiceClient
from typing import Optional, Set
LOGGER = logging.getLogger(__name__)
service = Blueprint('service', __name__, url_prefix='/service') #Define a flask Blueprint called "service" behind the url "/service"
service = Blueprint('service', __name__, url_prefix='/service')
context_client = ContextClient() #Create an instance of ContextClient class as defined in /src/service/client/ContextClient.py
service_client = ServiceClient() #Create an instance of ServiceClient class as defined in /src/service/client/ServiceClient.py
device_client = DeviceClient()
context_client = ContextClient()
service_client = ServiceClient()
type = ["ACL_UNDEFINED", "ACL_IPV4","ACL_IPV6","ACL_L2","ACL_MPLS","ACL_MIXED"]
f_action = ["UNDEFINED", "DROP","ACCEPT","REJECT"]
l_action = ["UNDEFINED", "LOG_NONE","LOG_SYSLOG"]
'''
@service.get('/') #Route for the homepage of the created "service" blueprint
@contextmanager
def connected_client(c):
try:
......@@ -65,7 +43,7 @@ def connected_client(c):
yield c
finally:
c.close()
'''
# Context client must be in connected state when calling this function
def get_device_drivers_in_use(topology_uuid: str, context_uuid: str) -> Set[str]:
active_drivers = set()
......@@ -80,15 +58,15 @@ def get_device_drivers_in_use(topology_uuid: str, context_uuid: str) -> Set[str]
@service.get('/')
def home():
if 'context_uuid' not in session or 'topology_uuid' not in session: #Check if context_uuid and topology_uuid are defined in the current seession
flash("Please select a context!", "warning") #If they are not defined in the current session [Return to main page - STOP]
if 'context_uuid' not in session or 'topology_uuid' not in session:
flash("Please select a context!", "warning")
return redirect(url_for("main.home"))
context_uuid = session['context_uuid']
topology_uuid = session['topology_uuid']
context_client.connect() #Creates a connection, as specified in the connect method of the ContextClient() class
context_client.connect()
context_obj = get_context(context_client, context_uuid, rw_copy=False) #Using the get_context function, defined in /src/common/
context_obj = get_context(context_client, context_uuid, rw_copy=False)
if context_obj is None:
flash('Context({:s}) not found'.format(str(context_uuid)), 'danger')
services, device_names, endpoints_data = list(), list(), list()
......@@ -275,6 +253,7 @@ def detail(service_uuid: str):
try:
context_client.connect()
endpoint_ids = list()
service_obj = get_service_by_uuid(context_client, service_uuid, rw_copy=False)
if service_obj is None:
......@@ -292,15 +271,17 @@ def detail(service_uuid: str):
device_names, endpoints_data = dict(), dict()
context_client.close()
return render_template(
'service/detail.html', service=service_obj, connections=connections, device_names=device_names,
endpoints_data=endpoints_data, ste=ServiceTypeEnum, sse=ServiceStatusEnum, ile=IsolationLevelEnum, type = type, f_action = f_action, l_action = l_action)
endpoints_data=endpoints_data, ste=ServiceTypeEnum, sse=ServiceStatusEnum, ile=IsolationLevelEnum)
except Exception as e:
flash('The system encountered an error and cannot show the details of this service.', 'warning')
current_app.logger.exception(e)
return redirect(url_for('service.home'))
@service.get('<path:service_uuid>/delete') #Route for deleting a specific service
@service.get('<path:service_uuid>/delete')
def delete(service_uuid: str):
if 'context_uuid' not in session or 'topology_uuid' not in session:
flash("Please select a context!", "warning")
......@@ -320,372 +301,3 @@ def delete(service_uuid: str):
flash('Problem deleting service "{:s}": {:s}'.format(service_uuid, str(e.details())), 'danger')
current_app.logger.exception(e)
return redirect(url_for('service.home'))
#Added routes for creating a new Service
@service.route('add/configure', methods=['GET', 'POST']) #Route for adding a new service [Selecting the type of operation to be performed - First Form]
def add_configure():
form_1 = AddServiceForm_1()
if form_1.validate_on_submit():
#store the selected service type in session
#session['service_type'] = form_1.service_type.data
#redirect to the same page to display the second form
if form_1.service_type.data == 'ACL_L2':
return redirect(url_for('service.add_configure_ACL_L2'))
elif form_1.service_type.data == 'ACL_IPV4':
return redirect(url_for('service.add_configure_ACL_IPV4'))
elif form_1.service_type.data == 'ACL_IPV6':
return redirect(url_for('service.add_configure_ACL_IPV6'))
elif form_1.service_type.data == 'L2VPN':
return redirect(url_for('service.add_configure_L2VPN'))
elif form_1.service_type.data == 'L3VPN':
return redirect(url_for('service.add_configure_L3VPN'))
# display the first form
return render_template('service/add.html', form_1=form_1, submit_text='Continue to configuraton')
@service.route('add/configure/ACL_L2', methods=['GET', 'POST']) #Route for adding a new ACL_L2 service [Setting the parameters for defining the service]
def add_configure_ACL_L2():
form_acl = AddServiceForm_ACL_L2()
service_obj = Service()
context_uuid, topology_uuid = get_context_and_topology_uuids() #Get the topology and context UUIDS
if context_uuid and topology_uuid: #If the UUIDs exist
context_client.connect() #Connects to the context service using the context_client object
grpc_topology = get_topology(context_client, topology_uuid, context_uuid=context_uuid, rw_copy=False) #Call the get_topology() function to retrieve the topology information for the given context and topology UUIDs
if grpc_topology: #If the topology is defined
topo_device_uuids = {device_id.device_uuid.uuid for device_id in grpc_topology.device_ids}
#devices = get_filtered_devices(context_client, topo_device_uuids) #Calls the fucntion that returns a list of devices that have UUIDs in the set of topology device UUIDs.
context_obj = get_context(context_client, context_uuid, rw_copy=False)
if context_obj is None:
flash('Context({:s}) not found'.format(str(context_uuid)), 'danger')
return redirect(request.url)
services = context_client.ListServices(context_obj.context_id)
devices = []
for service in services.services:
devices_services = []
if service.service_type == ServiceTypeEnum.SERVICETYPE_L2NM:
LOGGER.warning('L2NM service')
for ep in service.service_endpoint_ids:
device_uuid = ep.device_id.device_uuid.uuid
devices_services.append(device_uuid, service.service_name)
LOGGER.warning('device_uuid')
LOGGER.warning(device_uuid)
grpc_devices = context_client.ListDevices(Empty())
for device in grpc_devices.devices:
if device.device_id.device_uuid.uuid in devices_services:
devices.append(device)
choices = get_device_choices(devices) #Returns a list of tuples, where each tuple contains the index of the device in the list and the name of the device
add_device_choices_to_form(choices, form_acl.service_device_1) #Adds the device choices to the select options for the form (Device1)
add_device_choices_to_form(choices, form_acl.service_device_2) #Adds the device choices to the select options for the form (Device2)
else:
flash('Context({:s})/Topology({:s}) not found'.format(str(context_uuid), str(topology_uuid)), 'danger') #If the topology is not found, display an error message and set the devices list to an empty list
else:
flash('Missing context or topology UUID', 'danger') #If the topology or context UUID is not found, display an error message
if form_acl.validate_on_submit():
flash(f'New configuration was created', 'success')
return redirect(url_for('service.home'))
return render_template('service/configure_ACL_L2.html', form_acl=form_acl, submit_text='Add New Service')
@service.route('add/configure/ACL_IPV4', methods=['GET', 'POST']) #Route for adding a new ACL_IPV4 service [Setting the parameters for defining the service]
def add_configure_ACL_IPV4():
form_acl = AddServiceForm_ACL_IPV4()
if form_acl.validate_on_submit():
flash(f'New configuration was created', 'success')
return redirect(url_for('service.home'))
print(form_acl.errors)
return render_template('service/configure_ACL_IPV4.html', form_acl=form_acl, submit_text='Add New Service')
@service.route('add/configure/ACL_IPV6', methods=['GET', 'POST']) #Route for adding a new ACL_IPV6 service [Setting the parameters for defining the service]
def add_configure_ACL_IPV6():
form_acl = AddServiceForm_ACL_IPV6()
if form_acl.validate_on_submit():
flash(f'New configuration was created', 'success')
return redirect(url_for('service.home'))
print(form_acl.errors)
return render_template('service/configure_ACL_IPV6.html', form_acl=form_acl, submit_text='Add New Service')
@service.route('add/configure/L2VPN', methods=['GET', 'POST']) #Route for adding a new L2VPN service [Setting the parameters for defining the service]
def add_configure_L2VPN():
form_l2vpn = AddServiceForm_L2VPN() #Load the AddServiceForm_L3VPN form defined in forms.py
service_obj = Service() #Create a new instance of the Service class
context_uuid, topology_uuid = get_context_and_topology_uuids() #Get the topology and context UUIDS
if context_uuid and topology_uuid: #If the UUIDs exist
context_client.connect() #Connects to the context service using the context_client object
grpc_topology = get_topology(context_client, topology_uuid, context_uuid=context_uuid, rw_copy=False) #Call the get_topology() function to retrieve the topology information for the given context and topology UUIDs
if grpc_topology: #If the topology is defined
topo_device_uuids = {device_id.device_uuid.uuid for device_id in grpc_topology.device_ids}
devices = get_filtered_devices(context_client, topo_device_uuids) #Calls the fucntion that returns a list of devices that have UUIDs in the set of topology device UUIDs.
choices = get_device_choices(devices) #Returns a list of tuples, where each tuple contains the index of the device in the list and the name of the device
add_device_choices_to_form(choices, form_l2vpn.service_device_1) #Adds the device choices to the select options for the form (Device1)
add_device_choices_to_form(choices, form_l2vpn.service_device_2) #Adds the device choices to the select options for the form (Device2)
else:
flash('Context({:s})/Topology({:s}) not found'.format(str(context_uuid), str(topology_uuid)), 'danger') #If the topology is not found, display an error message and set the devices list to an empty list
else:
flash('Missing context or topology UUID', 'danger') #If the topology or context UUID is not found, display an error message
if form_l2vpn.validate_on_submit(): #Check if the form has been submitted and is valid
try: #Calls a function that validates the selected devices and endpoints exists and are correct
[selected_device_1, selected_device_2, selected_endpoint_1, selected_endpoint_2] = validate_selected_devices_and_endpoints(form_l2vpn, devices)
except Exception as e: #Catch any exception raised during the validation process
flash('{:s}'.format(str(e.args[0])), 'danger')
current_app.logger.exception(e)
return render_template('service/configure_L2VPN.html', form_l2vpn=form_l2vpn, submit_text='Add New Service') #Render the L2VPN configuration form with the previously entered data and an error message
#Check the specific values of the parameters dependent by the vendor of the device
[vendor_1, vendor_2] = get_device_vendor(form_l2vpn, devices)
try:
validate_params_vendor(form_l2vpn, vendor_1, 1)
validate_params_vendor(form_l2vpn, vendor_2, 2)
except Exception as e:
flash('{:s}'.format(str(e.args[0])), 'danger')
current_app.logger.exception(e)
return render_template('service/configure_L2VPN.html', form_l2vpn=form_l2vpn, submit_text='Add New Service') #Render the L2VPN configuration form with the previously entered data and an error message
#Create definition of the Service:
service_uuid, service_type, endpoint_ids = set_service_parameters(service_obj, form_l2vpn, selected_device_1, selected_device_2, selected_endpoint_1, selected_endpoint_2) #Calls the function to set the Service - Endpoint UUIDS
constraints = add_constraints(form_l2vpn) #Calls the function to add the constraint parameters for defining a service
params_device_1_with_data = get_device_params(form_l2vpn, 1, service_type) #Calls the function that getst the parameters that will configure the service in the device-1
params_device_2_with_data = get_device_params(form_l2vpn, 2, service_type) #Calls the function that getst the parameters that will configure the service in the device-2
print(params_device_1_with_data)
print(params_device_2_with_data)
params_settings = {} #Param settings (Defined despite it has no value) -> Avoid error
config_rules = [ #Create the configuration rules from the params_with_data
json_config_rule_set(
'/settings', params_settings
),
json_config_rule_set(
'/device[{:s}]/endpoint[{:s}]/settings'.format(str(selected_device_1.name), str(selected_endpoint_1)), params_device_1_with_data
),
json_config_rule_set(
'/device[{:s}]/endpoint[{:s}]/settings'.format(str(selected_device_2.name), str(selected_endpoint_2)), params_device_2_with_data
)
]
service_client.connect()
context_client.connect()
device_client.connect()
descriptor_json = json_service_l2nm_planned(service_uuid = service_uuid, endpoint_ids = endpoint_ids, constraints = constraints, config_rules = config_rules, context_uuid= context_uuid)
descriptor_json = {"services": [descriptor_json]} #Wrap the descriptor between the tag: "services": []"
try:
process_descriptors(descriptor_json)
flash('Service "{:s}" added successfully!'.format(service_obj.service_id.service_uuid.uuid), 'success') #If the service was added succesfully -> Flash success message with newly added service UUID.
return redirect(url_for('service.home', service_uuid=service_obj.service_id.service_uuid.uuid)) #If the service was added succesfully -> Redirect to the service.home URL #Call the process_descriptors function to add the new service defined in the descriptor_json variable
except Exception as e:
flash('Problem adding service: {:s}'.format((str(e.args[0]))), 'danger') #If the service was NOT added succesfully -> Include the exception message in a flashed message
current_app.logger.exception(e) #If the service was NOT added succesfully -> Log the exception using Flask's logger
finally:
context_client.close()
device_client.close()
service_client.close()
return render_template('service/configure_L2VPN.html', form_l2vpn=form_l2vpn, submit_text='Add New Service')
@service.route('add/configure/L3VPN', methods=['GET', 'POST']) #Route for adding a new L3VPN service [Setting the parameters for defining the service]
def add_configure_L3VPN():
form_l3vpn = AddServiceForm_L3VPN() #Load the AddServiceForm_L3VPN form defined in forms.py
service_obj = Service() #Create a new instance of the Service class
context_uuid, topology_uuid = get_context_and_topology_uuids() #Get the topology and context UUIDS
if context_uuid and topology_uuid: #If the UUIDs exist
context_client.connect() #Connects to the context service using the context_client object
grpc_topology = get_topology(context_client, topology_uuid, context_uuid=context_uuid, rw_copy=False) #Call the get_topology() function to retrieve the topology information for the given context and topology UUIDs
if grpc_topology: #If the topology is defined
topo_device_uuids = {device_id.device_uuid.uuid for device_id in grpc_topology.device_ids}
devices = get_filtered_devices(context_client, topo_device_uuids) #Calls the fucntion that returns a list of devices that have UUIDs in the set of topology device UUIDs.
choices = get_device_choices(devices) #Returns a list of tuples, where each tuple contains the index of the device in the list and the name of the device
add_device_choices_to_form(choices, form_l3vpn.service_device_1) #Adds the device choices to the select options for the form (Device1)
add_device_choices_to_form(choices, form_l3vpn.service_device_2) #Adds the device choices to the select options for the form (Device2)
else:
flash('Context({:s})/Topology({:s}) not found'.format(str(context_uuid), str(topology_uuid)), 'danger') #If the topology is not found, display an error message and set the devices list to an empty list
else:
flash('Missing context or topology UUID', 'danger') #If the topology or context UUID is not found, display an error message
if form_l3vpn.validate_on_submit():
try:
[selected_device_1, selected_device_2, selected_endpoint_1, selected_endpoint_2] = validate_selected_devices_and_endpoints(form_l3vpn, devices) #Calls a function that validates the selected devices and endpoints exists and are correct
except Exception as e: # Catch any exception raised during the validation process
flash('{:s}'.format(str(e.args[0])), 'danger')
current_app.logger.exception(e)
return render_template('service/configure_L3VPN.html', form_l3vpn=form_l3vpn, submit_text='Add New Service') #Render the L3VPN configuration form with the previously entered data and an error message
#Create definition of the Service:
service_uuid, service_type, endpoint_ids = set_service_parameters(service_obj, form_l3vpn, selected_device_1, selected_device_2, selected_endpoint_1, selected_endpoint_2) #Calls the function to set the Service - Endpoint UUIDS
constraints = add_constraints(form_l3vpn) #Calls the function to add the constraint parameters for defining a service
params_device_1_with_data = get_device_params(form_l3vpn, 1, service_type)
params_device_2_with_data = get_device_params(form_l3vpn, 2, service_type)
params_settings = {}
config_rules = [ #Create the configuration rules from the params_with_data
json_config_rule_set(
'/settings', params_settings
),
json_config_rule_set(
'/device[{:s}]/endpoint[{:s}]/settings'.format(str(selected_device_1.name), str(selected_endpoint_1)), params_device_1_with_data
),
json_config_rule_set(
'/device[{:s}]/endpoint[{:s}]/settings'.format(str(selected_device_2.name), str(selected_endpoint_2)), params_device_2_with_data
)
]
service_client.connect()
context_client.connect()
device_client.connect()
descriptor_json = json_service_l3nm_planned(service_uuid = service_uuid, endpoint_ids = endpoint_ids, constraints = constraints, config_rules = config_rules, context_uuid= context_uuid)
descriptor_json = {"services": [descriptor_json]} #Wrap the descriptor between the tag: "services": []"
try:
process_descriptors(descriptor_json)
flash('Service "{:s}" added successfully!'.format(service_obj.service_id.service_uuid.uuid), 'success') #If the service was added succesfully -> Flash success message with newly added service UUID.
return redirect(url_for('service.home', service_uuid=service_obj.service_id.service_uuid.uuid)) #If the service was added succesfully -> Redirect to the service.home URL #Call the process_descriptors function to add the new service defined in the descriptor_json variable
except Exception as e:
flash('Problem adding service: {:s}'.format((str(e.args[0]))), 'danger') #If the service was NOT added succesfully -> Include the exception message in a flashed message
current_app.logger.exception(e) #If the service was NOT added succesfully -> Log the exception using Flask's logger
finally:
context_client.close()
device_client.close()
service_client.close()
return render_template('service/configure_L3VPN.html', form_l3vpn=form_l3vpn, submit_text='Add New Service')
##Function for creating the service
DESCRIPTOR_LOADER_NUM_WORKERS = 10
def process_descriptors(descriptors): #The function receives a "descriptors" parameter which has to be a JSON descriptor object
descriptor_loader = DescriptorLoader(descriptors, num_workers=DESCRIPTOR_LOADER_NUM_WORKERS) #Creates a descriptor_loader object
results = descriptor_loader.process() #Calls the descriptor_loader.process method and saves the result in the results variable
for message,level in compose_notifications(results): #Retrieve the notifications that are obtained in the proccess
if level == 'error':
LOGGER.warning('ERROR message={:s}'.format(str(message))) #Display any error message in the LOG
flash(message, level) #Show any notification message to the user in the webUI by using flash()
##Functions for having a higher leaver of abstraction and understanding in the code:
def get_context_and_topology_uuids(): #Retrieve the context and topology UUIDs from the session, if they exist
context_uuid = session.get('context_uuid')
topology_uuid = session.get('topology_uuid')
return context_uuid, topology_uuid #Return the UUIDs as a tuple, or None if either is missing
def get_filtered_devices(context_client, topo_device_uuids): #Call the ListDevices() method on the context client to retrieve a list of all devices
grpc_devices = context_client.ListDevices(Empty())
return [device for device in grpc_devices.devices if device.device_id.device_uuid.uuid in topo_device_uuids] #Filter the list of devices to only include those with UUIDs that appear in the topology
def get_device_choices(devices): #Create the tuple (Number, Device) that will be added to the form
return [(i, str(device.name)) for i, device in enumerate(devices)]
def add_device_choices_to_form(choices, form): #Add the device choices (tuple) to the select options of the correspondent form
form.choices += choices
def validate_selected_devices_and_endpoints(form, devices): #Validates that the 2 selected devices and 2 endpoints exist and are valid. Then it returns them
selected_device_1 = devices[int(form.service_device_1.data)] #Selected_Device1 will be the one selected by the user in the previously defined form field
selected_device_2 = devices[int(form.service_device_2.data)] #Selected_Device2 will be the one selected by the user in the previously defined form field
if selected_device_1 == selected_device_2:
raise ValidationError('The devices must be different!. Please select two valid and different devices') # If it is not a valid endpoint -> Raise a Validation Error
elif form.service_endpoint_1.data not in [endpoint.name for endpoint in selected_device_1.device_endpoints]: # Check if the endpoint submitted by the user is a valid endpoint of the selected device
raise ValidationError('The selected endpoint: ' + form.service_endpoint_1.data + ' is not a valid endpoint for: '+ selected_device_1.name + '. Please select an endpoint that is available for this device')
elif form.service_endpoint_2.data not in [endpoint.name for endpoint in selected_device_2.device_endpoints]: # Check if the endpoint submitted by the user is a valid endpoint of the selected device
raise ValidationError('The selected endpoint: ' + form.service_endpoint_2.data + ' is not a valid endpoint for: '+ selected_device_2.name + '. Please select an endpoint that is available for this device')
else:
selected_endpoint_1 = form.service_endpoint_1.data #If the selected endpoint is valid, save it in a variable
selected_endpoint_2 = form.service_endpoint_2.data #If the selected endpoint is valid, save it in a variable
return selected_device_1, selected_device_2, selected_endpoint_1, selected_endpoint_2 #Return the devices and endpoints
def get_device_vendor(form, devices):
selected_device_1 = devices[int(form.service_device_1.data)] #Selected_Device1 will be the one selected by the user in the previously defined form field
selected_device_2 = devices[int(form.service_device_2.data)] #Selected_Device2 will be the one selected by the user in the previously defined form field
vendor_value_1 = None
vendor_value_2 = None
for config_rule in selected_device_1.device_config.config_rules:
if "vendor" in config_rule.custom.resource_value:
vendor_config_rule_1 = config_rule.custom.resource_value
config_rule_dict_1 = json.loads(vendor_config_rule_1)
if "vendor" in config_rule_dict_1:
vendor_value_1 = config_rule_dict_1["vendor"]
break
for config_rule in selected_device_2.device_config.config_rules:
if "vendor" in config_rule.custom.resource_value:
vendor_config_rule_2 = config_rule.custom.resource_value
config_rule_dict_2 = json.loads(vendor_config_rule_2)
if "vendor" in config_rule_dict_2:
vendor_value_2 = config_rule_dict_2["vendor"]
break
return vendor_value_1, vendor_value_2
def validate_params_vendor(form, vendor, device_num): #num is an auxiliar variable that can be 1 or 2 for knowing if it corresponds to the first or second device
if vendor == "ADVA":
if form.NI_name.data != f"ELAN-AC:{getattr(form, f'Device_{device_num}_IF_vlan_id').data}":
raise ValidationError('For an ADVA device, the name of the Network Instance should have this name: "ELAN-AC:vlanID"')
elif getattr(form, f'Device_{device_num}_NI_VC_ID').data != getattr(form, f'Device_{device_num}_IF_vlan_id').data:
raise ValidationError('For an ADVA device, the value of the VlanID and the value of the VC_ID must be the same')
else:
None
return None
def set_service_parameters(service_obj, form, selected_device_1, selected_device_2, selected_endpoint_1, selected_endpoint_2): #Function to retrieve and set the service parameters for defining the service
#Service UUID:
service_obj.service_id.service_uuid.uuid = str(form.service_name.data) #Create the Service UUID (Unique Identifier of the service) from the service name
service_uuid = service_obj.service_id.service_uuid.uuid
#Service type [OPTIONS Defined in Context.proto]: 0(Unknown), 1(L3NM), 2(L2NM), 3(TAPI_CONNECTIVITY_SERVICE), 4(ACL)
service_obj.service_type = int(form.service_type.data) #Set the Service type as selected by the user in the form
service_type = service_obj.service_type
# Set the endpoint IDs
endpoint_ids = [ #Create a list containing a element that represents the Selected Device ID and the Selected Endpoint
json_endpoint_id(json_device_id(selected_device_1.name), str(selected_endpoint_1)),
json_endpoint_id(json_device_id(selected_device_2.name), str(selected_endpoint_2))
]
return service_uuid, service_type, endpoint_ids
def add_constraints(form): #Function to add the constraints for a definition of a service
constraints = [] #Constraints -> Creates a list in which the constraints for the service will be added
if form.service_capacity.data:
constraints.append(json_constraint_sla_capacity(float(form.service_capacity.data))) #Capacity [Gbps]
if form.service_latency.data:
constraints.append(json_constraint_sla_latency(float(form.service_latency.data))) #Latency [ms]
if form.service_availability.data:
constraints.append(json_constraint_sla_availability(1, True, float(form.service_availability.data))) #Availability [%]
if form.service_isolation.data is not None and form.service_isolation.data != '':
constraints.append(json_constraint_sla_isolation([getattr(IsolationLevelEnum, str(form.service_isolation.data))])) #Isolation (Predefined values)
return constraints #Returns a list with the constraints and values
def get_device_params(form, device_num, form_type): #Function to retrieve and set the device parameters for defining the service
if form_type == 2: #Type2 = L2NM
device_params = {
'ni_name': str(getattr(form, 'NI_name').data),
'sub_interface_index': str(getattr(form, f'Device_{device_num}_IF_index').data),
'vlan_id': str(getattr(form, f'Device_{device_num}_IF_vlan_id').data),
'remote_router': str(getattr(form, f'Device_{device_num}_NI_remote_system').data),
'vc_id': str(getattr(form, f'Device_{device_num}_NI_VC_ID').data),
'conn_point': str(getattr(form, f'Device_{device_num}_NI_connection_point').data),
'mtu': str(getattr(form, f'Device_{device_num}_IF_mtu').data),
'ni_description': str(getattr(form, 'NI_description').data),
'subif_description': str(getattr(form, f'Device_{device_num}_IF_description').data),
}
elif form_type == 1: #Type1 = L3NM
if device_num == 1:
policy_az_field = 'NI_import_policy'
policy_za_field = 'NI_export_policy'
elif device_num == 2:
policy_az_field = 'NI_export_policy'
policy_za_field = 'NI_import_policy'
device_params = {
'ni_name': str(getattr(form, 'NI_name').data),
'bgp_as':str(getattr(form, 'NI_as').data),
'route_distinguisher': str(getattr(form, 'NI_route_distinguisher').data),
'sub_interface_index': str(getattr(form, f'Device_{device_num}_IF_index').data),
'router_id': str(getattr(form, 'NI_router_id').data),
'vlan_id': str(getattr(form, f'Device_{device_num}_IF_vlan_id').data),
'address_ip': str(getattr(form, f'Device_{device_num}_IF_address_ip').data),
'address_prefix': str(getattr(form, f'Device_{device_num}_IF_address_prefix').data),
'policy_AZ': str(getattr(form, policy_az_field).data),
'policy_ZA': str(getattr(form, policy_za_field).data),
'mtu': str(getattr(form, f'Device_{device_num}_IF_mtu').data),
'ni_description': str(getattr(form, 'NI_description').data),
'subif_description': str(getattr(form, f'Device_{device_num}_IF_description').data),
}
else:
raise ValueError(f'Unsupported form type: {form_type}')
params_with_data = {k: v for k, v in device_params.items() if v is not None and str(v) != 'None' and v != ''} #Retrieve the params that do not have value (None or ' ')
return params_with_data
......@@ -51,6 +51,7 @@
</div> -->
</div>
<table class="table table-striped table-hover">
<thead>
<tr>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment