diff --git a/my_deploy.sh b/my_deploy.sh index 1ffbffa15e3d285af37ed6328e45b768de7abc1b..e3ad5e71ae58f8546ecf7f24bf2a3a4c0d03cb37 100755 --- a/my_deploy.sh +++ b/my_deploy.sh @@ -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="" diff --git a/quick_deploy.sh b/quick_deploy.sh deleted file mode 100755 index 72c034a8fb31bd6ab2a1b5700621f12d2ef81bd2..0000000000000000000000000000000000000000 --- a/quick_deploy.sh +++ /dev/null @@ -1,440 +0,0 @@ -#!/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 - diff --git a/src/load_generator/command/__main__.py b/src/load_generator/command/__main__.py index 555f1dd4fb12e86eaeda1658a97164b52a7bf1ba..4fa2094e0fdc94b9665b2cfc86811e67809bcb5f 100644 --- a/src/load_generator/command/__main__.py +++ b/src/load_generator/command/__main__.py @@ -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'.+', diff --git a/src/webui/service/service/forms.py b/src/webui/service/service/forms.py deleted file mode 100644 index 52fc68302a3826a834e8c010403eb64a830a188b..0000000000000000000000000000000000000000 --- a/src/webui/service/service/forms.py +++ /dev/null @@ -1,295 +0,0 @@ -# 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 - diff --git a/src/webui/service/service/routes.py b/src/webui/service/service/routes.py index 9aff0ad79ab2f92d244502ad2b32ad927860766f..08312e5257d13c4b55b83733ded689c7565c4790 100644 --- a/src/webui/service/service/routes.py +++ b/src/webui/service/service/routes.py @@ -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 diff --git a/src/webui/service/templates/service/home.html b/src/webui/service/templates/service/home.html index 4e492c7a32717d5cd72029ef7ebd52c17fc77340..00feaff59128dd026ab2bdb369229a9d0aaae805 100644 --- a/src/webui/service/templates/service/home.html +++ b/src/webui/service/templates/service/home.html @@ -51,6 +51,7 @@ </div> --> </div> + <table class="table table-striped table-hover"> <thead> <tr>